Przykład kompleksowego testu TF

Ten samouczek przeprowadzi Cię przez proces tworzenia konfiguracji testowej Federacji Handlowej (Tradefed lub TF) „witaj, świecie” i zapewni praktyczne wprowadzenie do frameworka TF. Zaczynając od środowiska programistycznego, utworzysz prostą konfigurację i dodasz funkcje.

W samouczku przedstawiono proces tworzenia testów w postaci zestawu ćwiczeń, z których każde składa się z kilku kroków, które pokazują, jak zbudować i stopniowo udoskonalać konfigurację. Dostarczono cały przykładowy kod potrzebny do ukończenia konfiguracji testu, a tytuł każdego ćwiczenia jest opatrzony literą opisującą role zaangażowane w tym kroku:

  • D dla programisty
  • Ja za Integratora
  • R jak biegacz testowy

Po ukończeniu samouczka będziesz mieć działającą konfigurację TF i zrozumiesz wiele ważnych koncepcji w środowisku TF.

Załóż Federację Handlową

Aby uzyskać szczegółowe informacje na temat konfigurowania środowiska programistycznego TF, zobacz Konfiguracja maszyny . W pozostałej części tego samouczka założono, że masz otwartą powłokę, która została zainicjowana w środowisku TF.

Dla uproszczenia ten samouczek ilustruje dodawanie konfiguracji i jej klas do podstawowej biblioteki platformy TF. Można to rozszerzyć na tworzenie modułów poza drzewem źródłowym, kompilując dostarczony plik JAR, a następnie kompilując moduły na podstawie tego pliku JAR.

Utwórz klasę testową (D)

Stwórzmy test hello world, który po prostu zrzuci wiadomość na standardowe wyjście. Test handlowy zazwyczaj implementuje interfejs IRemoteTest . Oto implementacja 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!");
    }
}

Zapisz ten przykładowy kod w <tree>/tools/tradefederation/core/src/com/android/tradefed/example/HelloWorldTest.java i odbuduj Tradefed ze swojej powłoki:

m -jN

Zauważ, że CLog.i w powyższym przykładzie jest używany do kierowania danych wyjściowych do konsoli. Więcej informacji na temat logowania do Federacji Handlowej opisano w rozdziale Logowanie (D, I, R) .

Jeśli kompilacja się nie powiedzie, skonsultuj się z konfiguracją maszyny , aby upewnić się, że nie pominąłeś żadnego kroku.

Utwórz konfigurację (I)

Testy Federacji Handlowej stają się wykonywalne poprzez utworzenie pliku konfiguracyjnego XML, który instruuje firmę handlującą, który test (lub testy) ma uruchomić, a także które inne moduły mają zostać wykonane i w jakiej kolejności.

Stwórzmy nową konfigurację dla naszego HelloWorldTest (zanotuj pełną nazwę klasy HelloWorldTest):

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

Zapisz te dane w pliku helloworld.xml w dowolnym miejscu lokalnego systemu plików (np. /tmp/helloworld.xml ). TF przeanalizuje plik konfiguracyjny XML (inaczej config ), załaduje określoną klasę za pomocą odbicia, utworzy jej instancję, przerzuci ją na IRemoteTest i wywoła jej metodę run .

Uruchom konfigurację (R)

Z poziomu powłoki uruchom wymienioną konsolę:

tradefed.sh

Upewnij się, że urządzenie jest podłączone do komputera hosta i jest widoczne dla traderów:

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

Konfiguracje można wykonać za pomocą polecenia konsoli run <config> . Próbować:

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!

Powinieneś zobaczyć „Witaj, świecie TF!” wyjście na terminal.

Możesz potwierdzić, że polecenie zostało wykonane, używając list invocations lub li w wierszu poleceń konsoli; polecenie nie powinno nic wyświetlić. Jeśli polecenia są aktualnie uruchomione, są one wyświetlane w następujący sposób:

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}'

Dodaj konfigurację do ścieżki klas (D, I, R)

Dla wygody wdrożenia możesz także spakować konfiguracje do samych plików JAR. Tradefed automatycznie rozpoznaje wszystkie konfiguracje umieszczone w folderach konfiguracyjnych w ścieżce klas.

Aby to zilustrować, przenieś plik helloworld.xml do podstawowej biblioteki Tradefed ( <tree>/tools/tradefederation/core/res/config/example/helloworld.xml ). Odbuduj Tradefed, zrestartuj konsolę Tradefed, a następnie poproś Tradefed o wyświetlenie listy konfiguracji ze ścieżki klas:

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

Możesz teraz uruchomić konfigurację helloworld za pomocą:

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!

Interakcja z urządzeniem (D, R)

Jak na razie nasz HelloWorldTest nie robi nic interesującego. Specjalnością Tradefed jest przeprowadzanie testów na urządzeniach z Androidem, więc dodajmy do testu urządzenie z Androidem.

Testy mogą uzyskać odwołanie do urządzenia z systemem Android za pomocą TestInformation dostarczonego przez platformę po wywołaniu metody IRemoteTest#run .

Zmodyfikujmy komunikat wydruku HelloWorldTest, aby wyświetlał numer seryjny urządzenia:

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

Teraz odbuduj Tradefed i sprawdź listę urządzeń:

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

Zanotuj numer seryjny wymieniony jako Dostępny ; to jest urządzenie, które powinno zostać przydzielone do HelloWorld:

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

Powinieneś zobaczyć nowy komunikat drukowania zawierający numer seryjny urządzenia.

Wyślij wyniki testu (D)

IRemoteTest raportuje wyniki, wywołując metody w instancji ITestInvocationListener dostarczonej do metody #run . Sama struktura TF jest odpowiedzialna za raportowanie początku (za pośrednictwem ITestInvocationListener#invocationStarted ) i zakończenia (za pośrednictwem ITestInvocationListener#invocationEnded ) każdego wywołania.

Przebieg testu to logiczny zbiór testów. Aby zgłosić wyniki testu, IRemoteTest jest odpowiedzialny za raportowanie rozpoczęcia przebiegu testu, początku i końca każdego testu oraz zakończenia przebiegu testu.

Oto jak może wyglądać implementacja HelloWorldTest w przypadku pojedynczego wyniku testu zakończonego niepowodzeniem.

@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 zawiera kilka implementacji IRemoteTest , których możesz użyć ponownie, zamiast pisać własne od zera. Na przykład InstrumentationTest może zdalnie uruchamiać testy aplikacji dla systemu Android na urządzeniu z systemem Android, analizować wyniki i przekazywać je do ITestInvocationListener ). Aby uzyskać szczegółowe informacje, zobacz Typy testów .

Przechowuj wyniki testów (I)

Domyślną implementacją odbiornika testowego dla konfiguracji TF jest TextResultReporter , która zrzuca wyniki wywołania na standardowe wyjście. Aby to zilustrować, uruchom konfigurację HelloWorldTest z poprzedniej sekcji:

./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

Aby zapisać wyniki wywołania w innym miejscu, na przykład w pliku, określ niestandardową implementację ITestInvocationListener , używając result_reporter w konfiguracji.

TF zawiera także odbiornik XmlResultReporter , który zapisuje wyniki testów do pliku XML w formacie podobnym do używanego przez moduł zapisujący XML Ant JUnit. Aby określić report_reporter w konfiguracji, edytuj konfigurację …/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>

Teraz odbuduj Tradefed i ponownie uruchom próbkę hello world:

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

Zwróć uwagę na komunikat dziennika informujący, że wygenerowano plik XML; wygenerowany plik powinien wyglądać następująco:

<?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>

Możesz także napisać własne niestandardowe odbiorniki wywołań — wystarczy, że zaimplementują interfejs ITestInvocationListener .

Tradefed obsługuje wiele odbiorników wywołań, dzięki czemu możesz wysyłać wyniki testów do wielu niezależnych miejsc docelowych. Aby to zrobić, po prostu określ wiele tagów <result_reporter> w swojej konfiguracji.

Urządzenia do pozyskiwania drewna (D, I, R)

Możliwości logowania TF obejmują możliwość:

  1. Przechwytuj dzienniki z urządzenia (inaczej logcat urządzenia)
  2. Rejestruj dzienniki z platformy Federacji Handlowej działającej na komputerze hosta (inaczej dziennik hosta)

Struktura TF automatycznie przechwytuje logcat z przydzielonego urządzenia i wysyła go do słuchacza wywołań w celu przetworzenia. Następnie XmlResultReporter zapisuje przechwycony logcat urządzenia jako plik.

Dzienniki hosta TF są raportowane przy użyciu opakowania CLog dla klasy ddmlib Log. Przekonwertujmy poprzednie wywołanie System.out.println w HelloWorldTest na wywołanie CLog :

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

CLog obsługuje bezpośrednio interpolację ciągów, podobnie jak String.format . Kiedy przebudujesz i ponownie uruchomisz TF, powinieneś zobaczyć komunikat dziennika na stdout:

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

Domyślnie Tradefed wysyła komunikaty dziennika hosta na standardowe wyjście . TF zawiera również implementację dziennika, która zapisuje komunikaty do pliku: FileLogger . Aby dodać rejestrowanie plików, dodaj do konfiguracji znacznik logger , podając pełną nazwę klasy FileLogger :

<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>

Teraz przebuduj i ponownie uruchom przykład helloworld:

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
…

Komunikat dziennika wskazuje ścieżkę dziennika hosta, który po przejrzeniu powinien zawierać komunikat dziennika HelloWorldTest:

more /tmp/0/inv_6390011618174565918/host_log_4255420317120216614.txt

Przykładowe wyjście:

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

Opcje obsługi (D, I, R)

Obiekty ładowane z konfiguracji TF (inaczej obiekty konfiguracyjne ) mogą również odbierać dane z argumentów wiersza poleceń poprzez użycie adnotacji @Option .

Aby wziąć udział, klasa obiektu Configuration stosuje adnotację @Option do pola członkowskiego i nadaje mu unikalną nazwę. Umożliwia to wypełnienie wartości pola elementu za pomocą opcji wiersza poleceń (a także automatycznie dodaje tę opcję do systemu pomocy konfiguracyjnej).

Uwaga: nie wszystkie typy pól są obsługiwane. Aby uzyskać opis obsługiwanych typów, zobacz OptionSetter .

Dodajmy @Option do HelloWorldTest:

@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";

Następnie dodajmy komunikat dziennika, aby wyświetlić wartość opcji w HelloWorldTest, abyśmy mogli wykazać, że została poprawnie odebrana:

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

Na koniec odbuduj TF i uruchom helloworld; powinieneś zobaczyć komunikat dziennika z domyślną wartością my_option :

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

Przekaż wartości z wiersza poleceń

Przekaż wartość my_option ; powinieneś zobaczyć my_option wypełnioną tą wartością:

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

Konfiguracje TF zawierają także system pomocy, który automatycznie wyświetla tekst pomocy dla pól @Option . Wypróbuj teraz, a powinieneś zobaczyć tekst pomocy dla my_option :

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.

Zwróć uwagę na komunikat „drukowanie tylko ważnych opcji”. Aby zmniejszyć bałagan w pomocy dotyczącej opcji, TF używa atrybutu Option#importance w celu określenia, czy wyświetlać tekst pomocy konkretnego pola @Option , gdy podano --help . --help-all zawsze pokazuje pomoc dla wszystkich pól @Option , niezależnie od ich ważności. Aby uzyskać szczegółowe informacje, zobacz Opcja.Ważność .

Przekaż wartości z konfiguracji

Możesz także określić wartość opcji w konfiguracji, dodając element <option name="" value=""> . Przetestuj to za pomocą helloworld.xml :

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

Ponowne zbudowanie i uruchomienie helloworld powinno teraz dać następujący wynik:

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

Pomoc konfiguracyjna powinna również zostać zaktualizowana, aby wskazać domyślną wartość my_option :

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

Inne obiekty konfiguracyjne zawarte w konfiguracji helloworld, takie jak FileLogger , również akceptują opcje. Opcja --log-level-display jest interesująca, ponieważ filtruje logi wyświetlane na stdout. Wcześniej w tym samouczku być może zauważyłeś, że komunikat dziennika „Witaj, TF World! Mam urządzenie…” przestał być wyświetlany na stdout po przejściu na FileLogger . Możesz zwiększyć szczegółowość logowania na standardowe wyjście, przekazując --log-level-display arg --log-level-display .

Spróbuj tego teraz, a poza zalogowaniem do pliku powinieneś ponownie zobaczyć komunikat dziennika „Mam urządzenie” na standardowym wyjściu:

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

To wszystko, ludzie!

Przypominamy, że jeśli na czymś utkniesz, kod źródłowy Federacji Handlowej zawiera wiele przydatnych informacji, które nie są ujawnione w dokumentacji. Jeśli wszystko inne zawiedzie, spróbuj zapytać w Grupie dyskusyjnej Google na platformie Android , w temacie wiadomości wpisując „Federacja handlowa”.