Test Mapping

本文简要介绍了 Test Mapping,并说明了如何在 Android 开源项目 (AOSP) 中轻松开始配置测试。

什么是 Test Mapping?

Test Mapping 是一种基于 Gerrit 的方法,让开发者能够直接在 Android 源代码树中创建提交前规则和提交后规则,并将要测试的分支和设备的决策留给测试基础架构本身。Test Mapping 定义是名为 TEST_MAPPING 的 JSON 文件,该文件可放置在任何源目录中。

Atest 可以使用 TEST_MAPPING 文件在相关目录中运行提交前测试。借助 Test Mapping,您只需在 Android 源代码树中进行简单的更改,即可将同一组测试添加到提交前检查。

请参阅以下示例:

针对 services.core 将提交前测试添加到 TEST_MAPPING

通过导入针对 tools/dexter 将提交前的测试添加到 TEST_MAPPING

Test Mapping 依赖于 Trade Federation (TF) 自动化测试框架来进行测试执行和结果报告。

定义测试组

Test Mapping 组通过测试组进行测试。测试组的名称可以是任何字符串。例如,presubmit 可用于在验证更改时运行的测试组。postsubmit 测试可在更改合并后用于验证 build。

打包构建脚本规则

为了让 Trade Federation 自动化测试框架针对指定 build 运行 Test Mapping 的测试模块,必须针对 Soong 将这些模块的 test_suite(或者针对 Make 将这些模块的 LOCAL_COMPATIBILITY_SUITE)设为以下两个套件之一:

  • general-tests - 不依赖于设备专用功能(例如大多数设备不包含的、供应商专用的硬件)的测试。大多数测试都应该在 general-tests 套件中,即使它们针对一个 ABI、位数或 HWASan 等硬件功能(每个 ABI 都有一个单独的 test_suites 目标),并且即使它们必须在设备上运行。
  • device-tests - 取决于设备专用功能的测试。您通常可以在 vendor/ 下找到这些测试。由于“设备专用”不是指其他设备可能具有或者不具有的 ABI 或 SoC 功能,而是一个设备的独特功能,所以它适用于 JUnit 测试的情形与 GTest 原生测试完全一样(通常应该是 general-tests,即使这些测试是针对 ABI 的)。

示例:

Android.bp: test_suites: ["general-tests"],
Android.mk: LOCAL_COMPATIBILITY_SUITE := general-tests

配置在测试套件中运行的测试

如需在测试套件中运行测试,该测试:

  • 不得包含任何 build 提供程序。
  • 必须在完成之后进行清理,例如,删除测试期间生成的任何临时文件。
  • 将系统设置更改为默认设置或原始值。
  • 不应假定设备处于特定状态下,例如 root 准备就绪状态。大多数测试无需 root 权限即可运行。如果测试需要 root 权限,应通过 AndroidTest.xml 中的 RootTargetPreparer 指定该权限,如以下示例所示:
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>

创建 Test Mapping 文件

对于需要测试覆盖率的目录,只需添加与以下示例类似的 TEST_MAPPING JSON 文件即可。这些规则可以确保当此目录或其子目录中的任何文件被访问时,相应测试会在提交前检查中运行。

示例

以下是 TEST_MAPPING 文件示例(该文件采用 JSON 格式,但支持注释):

{
  "presubmit": [
    // JUnit test with options and file patterns.
    {
      "name": "CtsWindowManagerDeviceTestCases",
      "options": [
        {
          "include-annotation": "android.platform.test.annotations.RequiresDevice"
        }
      ],
      "file_patterns": ["(/|^)Window[^/]*\\.java", "(/|^)Activity[^/]*\\.java"]
    },
    // Device-side GTest with options.
    {
      "name" : "hello_world_test",
      "options": [
        {
          "native-test-flag": "\"servicename1 servicename2\""
        },
        {
          "native-test-timeout": "6000"
        }
      ]
    }
    // Host-side GTest.
    {
      "name" : "net_test_avrcp",
      "host" : true
    }
  ],
  "postsubmit": [
    {
      "name": "CtsWindowManagerDeviceTestCases"
    }
  ],
  "imports": [
    {
      "path": "frameworks/base/services/core/java/com/android/server/am"
    }
  ]
}

设置属性

在上面的示例中,presubmitpostsubmit 分别是每个测试组的名称。如需详细了解测试组,请参阅定义测试组

可在 name 属性值中设置测试模块名称Trade Federation 集成测试名称(指向测试 XML 文件的资源路径,例如 uiautomator/uiautomator-demo)。请注意,不可在 name 字段中使用类 name 或测试方法 name。如需缩减要运行的测试,可在此处使用 include-filter 等选项。请参阅(include-filter 用法示例)。

测试的 host 设置指示测试是否是在主机上运行的无设备测试。默认值为 false,表示需要有设备才能运行测试。受支持的测试类型包括 HostGTest(适用于 GTest 二进制文件)和 HostTest(适用于 JUnit 测试)。

使用 file_patterns 属性,您可以设置正则表达式字符串列表,以匹配任何源代码文件的相对路径(相对于包含 TEST_MAPPING 文件的目录)。在上面的示例中,只有当任意以 Window 或 Activity 开头的 java 文件(该文件所在目录与 TEST_MAPPING 文件的目录或其子目录相同)发生更改时,测试 CtsWindowManagerDeviceTestCases 才会在提交前测试中运行。当反斜线 \ 包含在 JSON 文件中时,需要对其进行转义。

借助 imports 属性,您可以包含其他 TEST_MAPPING 文件中的测试,而无需复制相应内容。请注意,导入路径的父目录中的 TEST_MAPPING 文件也将包含在内。Test Mapping 支持嵌套导入;这意味着两个 TEST_MAPPING 文件可以互相导入,并且 Test Mapping 能够正确合并随附测试。

options 属性包含其他 TradeFed 命令行选项。

要获取指定测试可用选项的完整列表,请运行以下命令:

tradefed.sh run commandAndExit [test_module] --help

如需详细了解各选项的工作原理,请参阅 TradeFed 选项处理

使用 Atest 运行测试

要在本地执行提交前测试规则,请执行以下操作:

  1. 转到包含 TEST_MAPPING 文件的目录。
  2. 运行以下命令:
atest

在当前目录及其父目录的 TEST_MAPPING 文件中配置的所有提交前测试都会运行。Atest 将针对提交前测试找到两个测试(A 和 B)并加以运行。

要运行当前工作目录 (CWD) 和父目录中的 TEST_MAPPING 文件中的提交前测试,这是最简单的方法。Atest 会找到 CWD 及其所有父目录中的 TEST_MAPPING 文件并使用这些文件。

构建源代码

以下示例显示了如何在源代码树中配置 TEST_MAPPING 文件。

src
├── project_1
│   └── TEST_MAPPING
├── project_2
│   └── TEST_MAPPING
└── TEST_MAPPING

src/TEST_MAPPING 的内容:

{
  "presubmit": [
    {
      "name": "A"
    }
  ]
}

src/project_1/TEST_MAPPING 的内容:

{
  "presubmit": [
    {
      "name": "B"
    }
  ],
  "postsubmit": [
    {
      "name": "C"
    }
  ],
  "other_group": [
    {
      "name": "X"
    }
  ]}

src/project_2/TEST_MAPPING 的内容:

{
  "presubmit": [
    {
      "name": "D"
    }
  ],
  "import": [
    {
      "path": "src/project_1"
    }
  ]}

指定目标目录

您可以指定一个目标目录,以便在该目录中运行 TEST_MAPPING 文件中的测试。以下命令可运行两个测试(A、B)。

atest --test-mapping src/project_1

运行提交后测试规则

您还可以使用该命令运行在 src_path(默认为 CWD)及其父目录中的 TEST_MAPPING 中定义的提交后测试规则:

atest [--test-mapping] [src_path]:postsubmit

仅运行不需要设备的测试

要想仅运行针对主机配置的不需要设备的测试,可以为 Atest 使用选项 --host。如果没有此选项,Atest 将运行两种测试,即需要设备的测试和在主机上运行的不需要设备的测试。这两种测试将分别在两个独立的套件中运行。

atest [--test-mapping] --host

识别测试组

您可以在 Atest 命令中指定测试组。以下命令可运行与 directory src/project_1 中的文件相关的所有提交后测试,其中只包含一个测试 (C)。

或者,您也可以使用 :all 来运行所有测试(无论测试属于哪个组)。以下命令可运行四个测试(A、B、C、X):

atest --test-mapping src/project_1:all

包含子目录

默认情况下,如果使用 Atest 运行 TEST_MAPPING 中的测试,那么系统仅会运行在 CWD(或指定目录)及其父目录中的 TEST_MAPPING 文件中配置的提交前测试。如果要运行子目录中的所有 TEST_MAPPING 文件中的测试,请使用 --include-subdir 选项强制要求 Atest 将这些测试包含在内。

atest --include-subdir

如果未使用 --include-subdir 选项,Atest 将仅运行测试 A。如果使用了 --include-subdir 选项,Atest 将运行两个测试(A、B)。

支持行级注解

您可以添加行级 // 格式的注释,以使用后面的设置描述来充实 TEST_MAPPING 文件。ATest 和 Trade Federation 会将 TEST_MAPPING 预处理为不带注释的有效 JSON 格式。为了使 JSON 文件简洁易读,仅支持行级 // 格式的注释。

示例:

{
  // For presubmit test group.
  "presubmit": [
    {
      // Run test on module A.
      "name": "A"
    }
  ]
}