Test mapping

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

Test Mapping 简介

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

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

请参阅以下示例:

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

定义测试组

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

打包构建脚本规则

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

  • general-tests 适用于不依赖于设备专用功能(例如大多数设备不包含的、供应商专用的硬件)的测试。大多数测试都应该在 general-tests 套件中,即使它们针对一个 ABI、位数或 HWASan 等硬件功能(每个 ABI 都有一个单独的 test_suites 目标),并且即使它们必须在设备上运行。
  • device-tests 适用于取决于设备专用功能的测试。通常,这些测试位于 vendor/ 下。设备专用仅指设备专有的功能,因此这适用于 JUnit 测试以及 GTest 测试(即使它们是 ABI 专用,通常也应标记为 general-tests)。

示例:

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": "CtsDeqpTestCases",
      "options": [
        {
          // Use regex in include-filter which is supported in AndroidJUnitTest
          "include-filter": "dEQP-EGL.functional.color_clears.*"
        }
      ]
    }
  ],
  "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 文件的目录)。在示例中,只有当 Java 文件以 WindowActivity 开头,并且该文件存在于与 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 命令中指定测试组。以下命令可运行与 src/project_1 目录中的文件相关的所有 postsubmit 测试,其中只包含一个测试 (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"
    }
  ]
}