Sharded IRemoteTest-Test-Runner schreiben

Beim Schreiben eines Test-Runners ist die Skalierbarkeit wichtig. Fragen Sie sich: „Wie lange würde es dauern, wenn mein Test-Runner 200.000 Testfälle ausführen müsste?“

Die Fragmentierung ist eine der Antworten, die in der Handelsföderation verfügbar sind. Es erfordert alle erforderlichen Tests in mehrere Blöcke aufzuteilen, die parallelisiert werden.

Auf dieser Seite wird beschrieben, wie du deinen Runner für Tradefed fragmentierbar machst.

Zu implementierende Schnittstelle

Die wichtigste Schnittstelle, die Sie implementieren müssen, damit Ihr Test von TF als shardbar eingestuft wird, ist IShardableTest. Sie enthält zwei Methoden: split(int numShard) und split().

Wenn das Sharding von der Anzahl der angeforderten Shards abhängen soll, sollten Sie split(int numShard) implementieren. Implementieren Sie andernfalls split().

Wenn ein TF-Testbefehl mit den Sharding-Parametern --shard-count und --shard-index ausgeführt wird, durchsucht TF alle IRemoteTest nach solchen, die IShardableTest implementieren. Wenn ein Objekt gefunden wird, wird split aufgerufen, um ein neues IRemoteTest-Objekt abzurufen, mit dem eine Teilmenge von Testfällen für einen bestimmten Shard ausgeführt wird.

Was sollte ich über die Implementierung der Aufteilung wissen?

  • Der Läufer kann nur unter bestimmten Bedingungen fragmentieren: Geben Sie in diesem Fall null zurück. als du sie nicht fragmentiert hast.
  • Versuchen Sie, so viel wie möglich zu teilen: Teilen Sie den Runner in eine sinnvolle Ausführungseinheit auf. Das kommt ganz auf Ihren Läufer an. Für Beispiel: HostTest auf Klassenebene fragmentiert ist, wird jede Testklasse in einem separaten Shard abgelegt.
  • Fügen Sie ggf. Optionen hinzu, um die Fragmentierung ein wenig zu steuern. Hier einige Beispiele: AndroidJUnitTest hat einen ajur-max-shard, um die maximale Anzahl an Shards anzugeben, die unabhängig von der angeforderten Anzahl aufgeteilt.

Detaillierte Beispielimplementierung

Hier ist ein Beispiel-Code-Snippet, mit dem IShardableTest implementiert wird, das Sie Referenz. Den vollständigen Code finden Sie unter (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;
    }
    ...
}

In diesem Beispiel wird einfach eine neue Instanz von sich selbst erstellt und Shard festgelegt. hinzufügen. Die Aufteilungslogik kann jedoch völlig anders sein als Tests durchführen: Und solange sie deterministisch ist und kollektiv Teilmengen enthalten ist, ist das in Ordnung.

Unabhängigkeit

Die Shards müssen unabhängig sein. Zwei Shards, die durch die Implementierung von split in Ihrem Runner sollten keine Abhängigkeiten haben oder gemeinsam nutzen. Ressourcen.

Die Shard-Aufteilung muss deterministisch sein. Dies ist ebenfalls obligatorisch. Unter denselben Bedingungen sollte Ihre split-Methode immer genau dieselbe Liste von Shards in derselben Reihenfolge zurückgeben.

HINWEIS: Da jeder Shard auf verschiedenen TF-Instanzen ausgeführt werden kann, ist es wichtig, Die split-Logik muss Teilmengen liefern, die sich gegenseitig ausschließen und und kollektiv auf deterministische Weise erschöpfend sind.

Test lokal fragmentieren

Zum Fragmentieren eines Tests auf einer lokalen TF können Sie einfach die Option --shard-count zu in die Befehlszeile ein.

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

Anschließend werden von TF automatisch Befehle für jeden Shard generiert und ausgeführt.

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)

Zusammenfassung der Testergebnisse

Da in TF keine Testergebnisaggregation für ge shardete Aufrufe erfolgt, muss Ihr Berichtsdienst diese Funktion unterstützen.