编写分片 IRemoteTest 测试运行程序

在编写测试运行程序时,请务必考虑可伸缩性。问问您自己,“如果我的测试运行程序必须运行 20 万个测试用例,需要多长时间?”

分片是 Trade Federation 提供的答案之一。它要求将运行程序需要的所有测试拆分成几个可以并行处理的区块。

本页介绍如何针对 Tradefed 将运行程序设为可分片。

要实现的接口

如需被 TF 视为可分片,有一个最重要的接口要实现,那就是 IShardableTest,它包含两种方法:split(int numShard)split()

如果分片将取决于请求的分片数,您应实现 split(int numShard)。否则,请实现 split()

当使用分片参数 --shard-count--shard-index 来执行 TF 测试命令时,TF 会遍历所有 IRemoteTest 以查找实现 IShardableTest 的对象。找到后,它将调用 split 获取一个新的 IRemoteTest 对象,以运行特定分片的测试用例子集。

关于拆分实现,我应该知道些什么?

  • 运行程序可能只在某些条件下分片;在这种情况下,如果未分片,将返回 null
  • 尝试尽可能合理地拆分:将运行程序拆分成对其有意义的执行单元。这其实取决于运行程序。例如:HostTest 在类级别进行分片,每个测试类放在一个单独的分片中。
  • 在有意义的情况下,您可以添加一些选项来稍微控制分片。例如:AndroidJUnitTest 有一个 ajur-max-shard 选项,用于指定它可以拆分成的最大分片数,而不管请求的数量是多少。

详细的实现示例

下面列举了一个实现 IShardableTest 的代码段示例供您参考。完整的代码位于以下网址:(https://android.googlesource.com/platform/tools/tradefederation/+/refs/heads/main/test_framework/com/android/tradefed/testtype/InstalledInstrumentationsTest.java)

/**
 * Runs all instrumentation found on current device.
 */
@OptionClass(alias = "installed-instrumentation")
public class InstalledInstrumentationsTest
        implements IDeviceTest, IResumableTest, IShardableTest {
    ...

    /** {@inheritDoc} */
    @Override
    public Collection<IRemoteTest> split(int shardCountHint) {
        if (shardCountHint > 1) {
            Collection<IRemoteTest> shards = new ArrayList<>(shardCountHint);
            for (int index = 0; index < shardCountHint; index++) {
                shards.add(getTestShard(shardCountHint, index));
            }
            return shards;
        }
        // Nothing to shard
        return null;
    }

    private IRemoteTest getTestShard(int shardCount, int shardIndex) {
        InstalledInstrumentationsTest shard = new InstalledInstrumentationsTest();
        try {
            OptionCopier.copyOptions(this, shard);
        } catch (ConfigurationException e) {
            CLog.e("failed to copy instrumentation options: %s", e.getMessage());
        }
        shard.mShardIndex = shardIndex;
        shard.mTotalShards = shardCount;
        return shard;
    }
    ...
}

本例只是创建了它自己的一个新实例,并为其设置了分片参数。然而,不同测试的拆分逻辑可能完全不同,只要具有确定性且能够产生总体详尽的子集即可。

独立性

分片需要独立!由运行程序中的 split 实现创建的两个分片不应相互依赖或共享资源。

分片拆分需要具有确定性!这也是强制性的,在相同的条件下,split 方法应始终以相同的顺序返回完全相同的分片列表。

注意:由于每个分片可以在不同的 TF 实例上运行,因此务必确保 split 逻辑以确定的方式产生互斥且总体详尽的子集。

在本地将测试分片

如需在本地 TF 上将测试分片,只需将 --shard-count 选项添加到命令行即可。

tf >run host --class com.android.tradefed.UnitTests --shard-count 3

然后,TF 将自动为每个分片衍生命令并运行这些命令。

tf >l i
Command Id  Exec Time  Device          State
3           0m:03      [null-device-2]  running stub on build 0 (shard 1 of 3)
3           0m:03      [null-device-1]  running stub on build 0 (shard 0 of 3)
3           0m:03      [null-device-3]  running stub on build 0 (shard 2 of 3)

测试结果聚合

由于 TF 不对分片调用执行任何测试结果聚合,因此您需要确保报告服务支持该功能。