Напишите сегментированный тест-раннер IRemoteTest

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

Шардинг — один из ответов, доступных в Торговой федерации. Для этого необходимо разбить все тесты, необходимые бегуну, на несколько частей, которые можно распараллелить.

На этой странице описывается, как сделать ваш бегун сегментируемым для 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/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 не выполняет агрегирование результатов тестов для сегментированных вызовов, вам необходимо убедиться, что ваша служба отчетов поддерживает это.