Напишите средство выполнения тестов Sharded IRemoteTest

При написании средства запуска тестов важно думать о масштабируемости. Спросите себя: «Если бы моему тестировщику пришлось запустить 200 000 тестов», сколько времени это заняло бы?

Sharding — один из ответов, доступных в Trade Federation. Это требует разделения всех тестов, необходимых бегуну, на несколько частей, которые можно распараллелить.

На этой странице описывается, как сделать ваш раннер доступным для Tradefed.

Интерфейс для реализации

Единственным наиболее важным интерфейсом, который необходимо реализовать для того, чтобы TF считал его разделяемым, является IShardableTest , который содержит два метода: split(int numShard) и split() .

Если ваш сегмент будет зависеть от количества запрошенных сегментов, вам следует реализовать split(int numShard) . В противном случае реализуйте split() .

Когда тестовая команда TF выполняется с параметрами --shard-count и --shard-index , TF перебирает все IRemoteTest в поисках тех, которые реализуют IShardableTest . Если он будет найден, он вызовет split , чтобы получить новый объект IRemoteTest для запуска подмножества тестовых случаев для определенного сегмента.

Что я должен знать о разделенной реализации?

  • Ваш бегун может шардить только при некоторых условиях; в этом случае верните null , если вы не шардировали.
  • Постарайтесь разделить столько, сколько это имеет смысл: разделите бегун на единицы выполнения, которые имеют для него смысл. Это действительно зависит от вашего бегуна. Например: HostTest сегментируется на уровне класса, каждый тестовый класс помещается в отдельный сегмент.
  • Если это имеет смысл, добавьте некоторые параметры, чтобы немного контролировать сегментирование. Например: AndroidJUnitTest имеет ajur-max-shard для указания максимального количества сегментов, на которые он может быть разделен, независимо от запрошенного числа.

Подробный пример реализации

Вот пример фрагмента кода, реализующего IShardableTest , на который вы можете ссылаться. Полный код доступен по адресу (https://android.googlesource.com/platform/tools/tradefederation/+/refs/heads/master/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 не выполняет агрегацию результатов тестов для сегментированных вызовов, вам необходимо убедиться, что ваша служба отчетов поддерживает это.