При написании тестового раннера важно думать о масштабируемости. Спросите себя: «Если бы моему тестовому раннеру пришлось запустить 200 тыс. тестовых случаев», сколько времени это заняло бы?
Шардинг — один из ответов, доступных в 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/android16-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
всегда должен возвращать точно такой же список осколков в том же порядке.
ПРИМЕЧАНИЕ: Поскольку каждый сегмент может работать на разных экземплярах 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 не выполняет агрегацию результатов тестирования для сегментированных вызовов, вам необходимо убедиться, что ваша служба отчетов поддерживает эту функцию.