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

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

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

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

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

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

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

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

Что мне следует знать о реализации функции разделения экрана?

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

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

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

ПРИМЕЧАНИЕ: Поскольку каждый сегмент может работать на разных экземплярах TensorFlow, крайне важно обеспечить, чтобы логика split генерировала взаимоисключающие и исчерпывающие подмножества детерминированным образом.

Протестируйте Shard локально.

Чтобы разделить тест на части на локальном TensorFlow, достаточно добавить параметр --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)

Агрегация результатов тестирования

Поскольку TensorFlow не выполняет агрегацию результатов тестирования для сегментированных вызовов, вам необходимо убедиться, что ваша служба отчетности поддерживает эту функцию.