Beispiel für einen End-to-End-TF-Test

Dieses Tutorial führt Sie durch die Erstellung einer „Hallo Welt“-Testkonfiguration der Trade Federation (Tradefed oder TF) und gibt Ihnen eine praktische Einführung in das TF-Framework. Ausgehend von einer Entwicklungsumgebung erstellen Sie eine einfache Konfiguration und fügen Funktionen hinzu.

Das Tutorial stellt den Testentwicklungsprozess als eine Reihe von Übungen vor, die jeweils aus mehreren Schritten bestehen und zeigen, wie Sie Ihre Konfiguration erstellen und schrittweise verfeinern. Der gesamte Beispielcode, den Sie zum Abschließen der Testkonfiguration benötigen, wird bereitgestellt, und der Titel jeder Übung ist mit einem Buchstaben versehen, der die an diesem Schritt beteiligten Rollen beschreibt:

  • D für Entwickler
  • Ich für Integrator
  • R für Test Runner

Nach Abschluss des Tutorials verfügen Sie über eine funktionierende TF-Konfiguration und verstehen viele wichtige Konzepte im TF-Framework.

Gründe eine Handelsföderation

Einzelheiten zum Einrichten der TF-Entwicklungsumgebung finden Sie unter Maschinen-Setup . Im Rest dieses Tutorials wird davon ausgegangen, dass Sie eine Shell geöffnet haben, die für die TF-Umgebung initialisiert wurde.

Der Einfachheit halber veranschaulicht dieses Tutorial das Hinzufügen einer Konfiguration und ihrer Klassen zur TF-Framework-Kernbibliothek. Dies kann auf die Entwicklung von Modulen außerhalb des Quellbaums ausgeweitet werden, indem Sie das handelsübliche JAR kompilieren und dann Ihre Module anhand dieses JAR kompilieren.

Erstellen Sie eine Testklasse (D)

Erstellen wir einen Hallo-Welt-Test, der einfach eine Nachricht an stdout sendet. Ein Tradefed-Test implementiert im Allgemeinen die IRemoteTest- Schnittstelle. Hier ist eine Implementierung für den HelloWorldTest:

package com.android.tradefed.example;

import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.invoker.TestInformation;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.result.ITestInvocationListener;
import com.android.tradefed.testtype.IRemoteTest;

public class HelloWorldTest implements IRemoteTest {
    @Override
    public void run(TestInformation testInfo, ITestInvocationListener listener) throws DeviceNotAvailableException {
        CLog.i("Hello, TF World!");
    }
}

Speichern Sie diesen Beispielcode unter <tree>/tools/tradefederation/core/src/com/android/tradefed/example/HelloWorldTest.java und erstellen Sie Tradefed von Ihrer Shell aus neu:

m -jN

Beachten Sie, dass CLog.i im obigen Beispiel verwendet wird, um die Ausgabe an die Konsole zu leiten. Weitere Informationen zur Protokollierung in Trade Federation finden Sie unter Protokollierung (D, I, R) .

Wenn der Build nicht erfolgreich ist, konsultieren Sie das Maschinen-Setup, um sicherzustellen, dass Sie keinen Schritt verpasst haben.

Erstellen Sie eine Konfiguration (I)

Trade Federation-Tests werden ausführbar gemacht, indem eine Konfiguration erstellt wird, eine XML-Datei, die Tradefed anweist, welcher Test (oder welche Tests) ausgeführt werden soll, sowie welche anderen Module in welcher Reihenfolge ausgeführt werden sollen.

Erstellen wir eine neue Konfiguration für unseren HelloWorldTest (beachten Sie den vollständigen Klassennamen des HelloWorldTest):

<configuration description="Runs the hello world test">
    <test class="com.android.tradefed.example.HelloWorldTest" />
</configuration>

Speichern Sie diese Daten in einer helloworld.xml Datei an einer beliebigen Stelle in Ihrem lokalen Dateisystem (z. B. /tmp/helloworld.xml ). TF analysiert die Konfigurations-XML-Datei (auch bekannt als config ), lädt die angegebene Klasse mithilfe von Reflektion, instanziiert sie, wandelt sie in einen IRemoteTest um und ruft ihre run auf.

Führen Sie die Konfiguration aus (R)

Starten Sie von Ihrer Shell aus die Tradefed-Konsole:

tradefed.sh

Stellen Sie sicher, dass ein Gerät mit dem Host-Computer verbunden und für Tradefed sichtbar ist:

tf> list devices
Serial            State      Product   Variant   Build   Battery
004ad9880810a548  Available  mako      mako      JDQ39   100

Konfigurationen können mit dem Konsolenbefehl run <config> ausgeführt werden. Versuchen:

tf> run /tmp/helloworld.xml
05-12 13:19:36 I/TestInvocation: Starting invocation for target stub on build 0 on device 004ad9880810a548
Hello, TF World!

Sie sollten „Hallo, TF World!“ sehen. Ausgabe auf dem Terminal.

Sie können bestätigen, dass die Ausführung eines Befehls abgeschlossen ist, indem Sie list invocations oder li in der Konsoleneingabeaufforderung verwenden. Es sollte nichts ausgegeben werden. Wenn gerade Befehle ausgeführt werden, werden diese wie folgt angezeigt:

tf >l i
Command Id  Exec Time  Device       State
10          0m:00      [876X00GNG]  running stub on build(s) 'BuildInfo{bid=0, target=stub, serial=876X00GNG}'

Fügen Sie die Konfiguration zum Klassenpfad hinzu (D, I, R)

Zur Vereinfachung der Bereitstellung können Sie Konfigurationen auch in den gehandelten JARs selbst bündeln. Tradefed erkennt automatisch alle Konfigurationen, die in Konfigurationsordnern im Klassenpfad abgelegt sind.

Verschieben Sie zur Veranschaulichung die Datei helloworld.xml in die Tradefed-Kernbibliothek ( <tree>/tools/tradefederation/core/res/config/example/helloworld.xml ). Erstellen Sie Tradefed neu, starten Sie die Tradefed-Konsole neu und bitten Sie dann Tradefed, die Liste der Konfigurationen aus dem Klassenpfad anzuzeigen:

tf> list configs
[…]
example/helloworld: Runs the hello world test

Sie können jetzt die Helloworld-Konfiguration ausführen mit:

tf> run example/helloworld
05-12 13:21:21 I/TestInvocation: Starting invocation for target stub on build 0 on device 004ad9880810a548
Hello, TF World!

Mit einem Gerät interagieren (D, R)

Bisher macht unser HelloWorldTest nichts Interessantes. Die Spezialität von Tradefed ist die Durchführung von Tests mit Android-Geräten. Fügen wir also ein Android-Gerät zum Test hinzu.

Tests können mithilfe von TestInformation einen Verweis auf ein Android-Gerät abrufen, der vom Framework bereitgestellt wird, wenn die IRemoteTest#run -Methode aufgerufen wird.

Ändern wir die Druckmeldung von HelloWorldTest, um die Seriennummer des Geräts anzuzeigen:

@Override
public void run(TestInformation testInfo, ITestInvocationListener listener) throws DeviceNotAvailableException {
    CLog.i("Hello, TF World! I have device " + testInfo.getDevice().getSerialNumber());
}

Erstellen Sie nun Tradefed neu und überprüfen Sie die Liste der Geräte:

tradefed.sh
tf> list devices
Serial            State      Product   Variant   Build   Battery
004ad9880810a548  Available  mako      mako      JDQ39   100

Notieren Sie sich die Seriennummer, die als „Verfügbar“ aufgeführt ist. Das ist das Gerät, das HelloWorld zugewiesen werden soll:

tf> run example/helloworld
05-12 13:26:18 I/TestInvocation: Starting invocation for target stub on build 0 on device 004ad9880810a548
Hello, TF World! I have device 004ad9880810a548

Sie sollten die neue Druckmeldung mit der Seriennummer des Geräts sehen.

Testergebnisse senden (D)

IRemoteTest meldet Ergebnisse durch Aufrufen von Methoden für die ITestInvocationListener- Instanz, die für die #run -Methode bereitgestellt wird. Das TF-Framework selbst ist dafür verantwortlich, den Start (über ITestInvocationListener#invocationStarted ) und das Ende (über ITestInvocationListener#invocationEnded ) jedes Aufrufs zu melden.

Ein Testlauf ist eine logische Sammlung von Tests. Um Testergebnisse zu melden, ist IRemoteTest dafür verantwortlich, den Start eines Testlaufs, den Start und das Ende jedes Tests sowie das Ende des Testlaufs zu melden.

So könnte die HelloWorldTest-Implementierung mit einem einzelnen fehlgeschlagenen Testergebnis aussehen.

@Override
public void run(TestInformation testInfo, ITestInvocationListener listener) throws DeviceNotAvailableException {
    CLog.i("Hello, TF World! I have device " + testInfo.getDevice().getSerialNumber());

    TestDescription testId = new TestDescription("com.example.TestClassName", "sampleTest");
    listener.testRunStarted("helloworldrun", 1);
    listener.testStarted(testId);
    listener.testFailed(testId, "oh noes, test failed");
    listener.testEnded(testId, Collections.emptyMap());
    listener.testRunEnded(0, Collections.emptyMap());
}

TF enthält mehrere IRemoteTest Implementierungen, die Sie wiederverwenden können, anstatt Ihre eigenen von Grund auf neu zu schreiben. Beispielsweise kann InstrumentationTest die Tests einer Android-Anwendung remote auf einem Android-Gerät ausführen, die Ergebnisse analysieren und diese Ergebnisse an den ITestInvocationListener weiterleiten. Einzelheiten finden Sie unter Testtypen .

Testergebnisse speichern (I)

Die standardmäßige Test-Listener-Implementierung für eine TF-Konfiguration ist TextResultReporter , die die Ergebnisse eines Aufrufs an stdout ausgibt. Führen Sie zur Veranschaulichung die HelloWorldTest-Konfiguration aus dem vorherigen Abschnitt aus:

./tradefed.sh
tf> run example/helloworld
04-29 18:25:55 I/TestInvocation: Invocation was started with cmd: /tmp/helloworld.xml
04-29 18:25:55 I/TestInvocation: Starting invocation for 'stub' with '[ BuildInfo{bid=0, target=stub, serial=876X00GNG} on device '876X00GNG']
04-29 18:25:55 I/HelloWorldTest: Hello, TF World! I have device 876X00GNG
04-29 18:25:55 I/InvocationToJUnitResultForwarder: Running helloworldrun: 1 tests
04-29 18:25:55 W/InvocationToJUnitResultForwarder:
Test com.example.TestClassName#sampleTest failed with stack:
 oh noes, test failed
04-29 18:25:55 I/InvocationToJUnitResultForwarder: Run ended in 0 ms

Um die Ergebnisse eines Aufrufs an anderer Stelle zu speichern, beispielsweise in einer Datei, geben Sie in Ihrer Konfiguration eine benutzerdefinierte ITestInvocationListener Implementierung mithilfe des Tags result_reporter an.

TF enthält auch den XmlResultReporter- Listener, der Testergebnisse in eine XML-Datei in einem Format schreibt, das dem vom ant JUnit XML-Writer verwendeten Format ähnelt. Um den result_reporter in der Konfiguration anzugeben, bearbeiten Sie die Konfiguration …/res/config/example/helloworld.xml :

<configuration description="Runs the hello world test">
    <test class="com.android.tradefed.example.HelloWorldTest" />
    <result_reporter class="com.android.tradefed.result.XmlResultReporter" />
</configuration>

Erstellen Sie nun Tradefed neu und führen Sie das Hallo-Welt-Beispiel erneut aus:

tf> run example/helloworld
05-16 21:07:07 I/TestInvocation: Starting invocation for target stub on build 0 on device 004ad9880810a548
Hello, TF World! I have device 004ad9880810a548
05-16 21:07:07 I/XmlResultReporter: Saved device_logcat log to /tmp/0/inv_2991649128735283633/device_logcat_6999997036887173857.txt
05-16 21:07:07 I/XmlResultReporter: Saved host_log log to /tmp/0/inv_2991649128735283633/host_log_6307746032218561704.txt
05-16 21:07:07 I/XmlResultReporter: XML test result file generated at /tmp/0/inv_2991649128735283633/test_result_536358148261684076.xml. Total tests 1, Failed 1, Error 0

Beachten Sie die Protokollmeldung, die besagt, dass eine XML-Datei generiert wurde. Die generierte Datei sollte so aussehen:

<?xml version='1.0' encoding='UTF-8' ?>
<testsuite name="stub" tests="1" failures="1" errors="0" time="9" timestamp="2011-05-17T04:07:07" hostname="localhost">
  <properties />
  <testcase name="sampleTest" classname="com.example.TestClassName" time="0">
    <failure>oh noes, test failed
    </failure>
  </testcase>
</testsuite>

Sie können auch Ihre eigenen benutzerdefinierten Aufruf-Listener schreiben – sie müssen lediglich die ITestInvocationListener- Schnittstelle implementieren.

Tradefed unterstützt mehrere Aufruf-Listener, sodass Sie Testergebnisse an mehrere unabhängige Ziele senden können. Geben Sie dazu einfach mehrere <result_reporter> -Tags in Ihrer Konfiguration an.

Holzeinschlagsanlagen (D, I, R)

Zu den Protokollierungsfunktionen von TF gehört die Möglichkeit:

  1. Protokolle vom Gerät erfassen (auch Geräte-Logcat genannt)
  2. Zeichnen Sie Protokolle vom Trade Federation-Framework auf, das auf dem Host-Computer ausgeführt wird (auch bekannt als Host-Protokoll).

Das TF-Framework erfasst automatisch den Logcat vom zugewiesenen Gerät und sendet ihn zur Verarbeitung an den Aufruf-Listener. XmlResultReporter speichert dann den erfassten Geräte-Logcat als Datei.

TF-Hostprotokolle werden mithilfe des CLog-Wrappers für die ddmlib-Protokollklasse gemeldet. Konvertieren wir den vorherigen System.out.println Aufruf in HelloWorldTest in einen CLog Aufruf:

@Override
public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
    CLog.i("Hello, TF World! I have device %s", getDevice().getSerialNumber());

CLog übernimmt die String-Interpolation direkt, ähnlich wie String.format . Wenn Sie TF neu erstellen und erneut ausführen, sollte die Protokollmeldung auf stdout angezeigt werden:

tf> run example/helloworld
…
05-16 21:30:46 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548
…

Standardmäßig gibt Tradefed Host-Protokollmeldungen an stdout aus . TF enthält auch eine Protokollimplementierung, die Nachrichten in eine Datei schreibt: FileLogger . Um die Dateiprotokollierung hinzuzufügen, fügen Sie der Konfiguration ein logger Tag hinzu und geben Sie dabei den vollständigen Klassennamen von FileLogger an:

<configuration description="Runs the hello world test">
    <test class="com.android.tradefed.example.HelloWorldTest" />
    <result_reporter class="com.android.tradefed.result.XmlResultReporter" />
    <logger class="com.android.tradefed.log.FileLogger" />
</configuration>

Erstellen Sie nun das Helloworld-Beispiel neu und führen Sie es erneut aus:

tf >run example/helloworld
…
05-16 21:38:21 I/XmlResultReporter: Saved device_logcat log to /tmp/0/inv_6390011618174565918/device_logcat_1302097394309452308.txt
05-16 21:38:21 I/XmlResultReporter: Saved host_log log to /tmp/0/inv_6390011618174565918/host_log_4255420317120216614.txt
…

Die Protokollnachricht gibt den Pfad des Hostprotokolls an, das bei Anzeige Ihre HelloWorldTest-Protokollnachricht enthalten sollte:

more /tmp/0/inv_6390011618174565918/host_log_4255420317120216614.txt

Beispielausgabe:

…
05-16 21:38:21 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548

Handhabungsmöglichkeiten (D, I, R)

Aus einer TF-Konfiguration geladene Objekte (auch bekannt als Konfigurationsobjekte ) können mithilfe der @Option Annotation auch Daten von Befehlszeilenargumenten empfangen.

Um teilzunehmen, wendet eine Konfigurationsobjektklasse die Annotation @Option auf ein Mitgliedsfeld an und gibt ihm einen eindeutigen Namen. Dadurch kann dieser Mitgliedsfeldwert über eine Befehlszeilenoption ausgefüllt werden (und diese Option wird außerdem automatisch zum Konfigurationshilfesystem hinzugefügt).

Hinweis: Nicht alle Feldtypen werden unterstützt. Eine Beschreibung der unterstützten Typen finden Sie unter OptionSetter .

Fügen wir HelloWorldTest eine @Option hinzu:

@Option(name="my_option",
        shortName='m',
        description="this is the option's help text",
        // always display this option in the default help text
        importance=Importance.ALWAYS)
private String mMyOption = "thisisthedefault";

Als Nächstes fügen wir eine Protokollmeldung hinzu, um den Wert der Option in HelloWorldTest anzuzeigen, damit wir nachweisen können, dass sie korrekt empfangen wurde:

@Override
public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
    …
    CLog.logAndDisplay(LogLevel.INFO, "I received option '%s'", mMyOption);

Erstellen Sie abschließend TF neu und führen Sie helloworld aus. Sie sollten eine Protokollmeldung mit dem Standardwert my_option sehen:

tf> run example/helloworld
…
05-24 18:30:05 I/HelloWorldTest: I received option 'thisisthedefault'

Übergeben Sie Werte von der Befehlszeile

Übergeben Sie einen Wert für my_option ; Sie sollten my_option mit diesem Wert gefüllt sehen:

tf> run example/helloworld --my_option foo
…
05-24 18:33:44 I/HelloWorldTest: I received option 'foo'

TF-Konfigurationen umfassen auch ein Hilfesystem, das automatisch Hilfetext für @Option Felder anzeigt. Probieren Sie es jetzt aus und Sie sollten den Hilfetext für my_option sehen:

tf> run example/helloworld --help
Printing help for only the important options. To see help for all options, use the --help-all flag

  cmd_options options:
    --[no-]help          display the help text for the most important/critical options. Default: false.
    --[no-]help-all      display the full help text for all options. Default: false.
    --[no-]loop          keep running continuously. Default: false.

  test options:
    -m, --my_option      this is the option's help text Default: thisisthedefault.

  'file' logger options:
    --log-level-display  the minimum log level to display on stdout. Must be one of verbose, debug, info, warn, error, assert. Default: error.

Beachten Sie die Meldung „Nur die wichtigen Optionen drucken“. Um das Durcheinander in der Optionshilfe zu reduzieren, verwendet TF das Option#importance -Attribut, um zu bestimmen, ob ein bestimmter @Option Feldhilfetext angezeigt werden soll, wenn --help angegeben ist. --help-all zeigt immer Hilfe für alle @Option Felder an, unabhängig von der Wichtigkeit. Einzelheiten finden Sie unter Option.Importance .

Übergeben Sie Werte aus einer Konfiguration

Sie können auch einen Optionswert innerhalb der Konfiguration angeben, indem Sie ein <option name="" value=""> Element hinzufügen. Testen Sie es mit helloworld.xml :

<test class="com.android.tradefed.example.HelloWorldTest" >
    <option name="my_option" value="fromxml" />
</test>

Das erneute Erstellen und Ausführen von helloworld sollte nun folgende Ausgabe erzeugen:

05-24 20:38:25 I/HelloWorldTest: I received option 'fromxml'

Die Konfigurationshilfe sollte ebenfalls aktualisiert werden, um den Standardwert von my_option anzuzeigen:

tf> run example/helloworld --help
  test options:
    -m, --my_option      this is the option's help text Default: fromxml.

Andere in der helloworld-Konfiguration enthaltene Konfigurationsobjekte, wie z. B. FileLogger , akzeptieren ebenfalls Optionen. Die Option --log-level-display ist interessant, weil sie die Protokolle filtert, die auf stdout angezeigt werden. Zu Beginn des Tutorials ist Ihnen vielleicht aufgefallen, dass die Protokollmeldung „Hallo, TF World! Ich habe Gerät …“ nicht mehr auf stdout angezeigt wird, nachdem wir auf die Verwendung FileLogger umgestiegen sind. Sie können die Ausführlichkeit der Protokollierung auf stdout erhöhen, indem Sie --log-level-display übergeben. --log-level-display arg.

Probieren Sie es jetzt aus und Sie sollten sehen, dass die Protokollmeldung „Ich habe ein Gerät“ erneut auf stdout angezeigt wird und zusätzlich in einer Datei protokolliert wird:

tf> run example/helloworld --log-level-display info
…
05-24 18:53:50 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548

Das war's Leute!

Zur Erinnerung: Falls Sie nicht weiterkommen, enthält der Quellcode der Trade Federation viele nützliche Informationen, die in der Dokumentation nicht offengelegt werden. Wenn alles andere fehlschlägt, fragen Sie bei der Android-Plattform Google Group nach, mit „Trade Federation“ im Betreff der Nachricht.