При написании средства запуска тестов важно учитывать масштабируемость. Задайте себе вопрос: "Если бы моему исполнителю тестов пришлось запустить 200 000 тестовых случаев, сколько времени это заняло бы?"
Сегментирование – один из вариантов, доступных в 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/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 обеспечивала получение взаимоисключающих и в совокупности исчерпывающих подмножеств детерминированным образом.
Как разделить тест на части локально
Чтобы разбить тест на сегменты в локальном 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 не выполняет агрегирование результатов тестирования для сегментированных вызовов, вы необходимо убедиться, что ваша служба отчетности поддерживает это.