Tworzenie dzielonego modułu testowego IRemoteTest

Podczas pisania testów należy wziąć pod uwagę skalowalność. Zastanów się, ile czasu zajmie uruchomienie 200 tys. przypadków testowych.

Dzielenie na fragmenty to jedna z opcji dostępnych w Federacji Handlowej. Wymaga to podziału wszystkich testów potrzebnych do uruchomienia na kilka części, które można przeprowadzić równolegle.

Na tej stronie znajdziesz informacje o tym, jak udostępnić runnera w ramach Tradefed.

Interfejs do wdrożenia

Najważniejszym interfejsem do wdrożenia, który TF uzna za możliwy do podzielenia na fragmenty, jest IShardableTest, który zawiera 2 metody: split(int numShard)split().

Jeśli podział na fragmenty będzie zależał od liczby żądanych fragmentów, należy zaimplementować split(int numShard). W przeciwnym razie zastosuj split().

Gdy polecenie testowe TF jest wykonywane z parametrami podziału --shard-count--shard-index, TF przechodzi przez wszystkie IRemoteTest, aby znaleźć te, które implementują IShardableTest. Jeśli zostanie znaleziony, wywoła funkcję split, aby uzyskać nowy obiekt IRemoteTest, który przeprowadzi podzbiór przypadków testowych dla konkretnego fragmentu.

Co muszę wiedzieć o wdrożeniu podziału?

  • Runner może dzielić się tylko w niektórych warunkach; w takim przypadku zwraca null, gdy nie dzieli się.
  • Spróbuj podzielić proces na jak najwięcej części: podziel Runnera na jednostki wykonania, które mają sens. To zależy od biegacza. Na przykład:HostTestjest dzielony na fragmenty na poziomie klasy, a każda klasa testu jest umieszczana w osobnym fragmencie.
  • Jeśli to ma sens, dodaj opcje, które pozwolą Ci nieco kontrolować dzielenie. Na przykład: AndroidJUnitTest ma element ajur-max-shard, który służy do określania maksymalnej liczby fragmentów, na które można podzielić ten element, niezależnie od liczby zażądanych fragmentów.

Szczegółowy przykład implementacji

Oto przykładowy fragment kodu implementujący funkcję IShardableTest, którego możesz użyć. Pełny kod jest dostępny pod adresem (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;
    }
    ...
}

W tym przykładzie po prostu tworzymy nową instancję i ustawiamy dla niej parametry fragmentacji. Zasady podziału mogą się jednak całkowicie różnić w zależności od testu. Dopóki są deterministyczne i dają w sumie wyczerpujące zbiory, wszystko jest w porządku.

Independence

Fragmenty muszą być niezależne. Dwa fragmenty utworzone przez implementację funkcji split w Twoim programie nie powinny być od siebie zależne ani współdzielić zasobów.

Podział fragmentów musi być deterministyczny. Jest to też obowiązkowe, ponieważ w tych samych warunkach metoda split powinna zawsze zwracać dokładnie tę samą listę fragmentów w tej samej kolejności.

UWAGA: ponieważ każdy fragment może działać na różnych instancjach TF, ważne jest, aby logika split generowała podzbiory, które są wzajemnie wykluczające się i wyczerpujące w sposób deterministyczny.

Dzielenie testu na części lokalnie

Aby podzielić test na lokalnym serwerze TF, wystarczy dodać opcję --shard-count do wiersza poleceń.

tf >run host --class com.android.tradefed.UnitTests --shard-count 3

Następnie TF automatycznie utworzy polecenia dla każdego fragmentu i je uruchomi.

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)

Agregacja wyników testu

Ponieważ TF nie agreguje wyników testów w przypadku wywołań w ramach fragmentacji, musisz się upewnić, że Twoja usługa raportowania obsługuje tę funkcję.