Przykład kompleksowego testu TF

Ten samouczek przeprowadzi Cię przez proces tworzenia konfiguracji testowej „hello world” Federacji Handlowej (TF) i zapewni praktyczne wprowadzenie do frameworka TF. Zaczynając od środowiska programistycznego stworzysz prostą konfigurację i dodasz funkcje.

Samouczek przedstawia proces tworzenia testów jako zestaw ćwiczeń, z których każde składa się z kilku kroków, które demonstrują, jak budować i stopniowo udoskonalać konfigurację. Podany jest cały przykładowy kod potrzebny do ukończenia konfiguracji testu, a tytuł każdego ćwiczenia jest oznaczony literą opisującą role związane z tym krokiem:

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

Po ukończeniu samouczka będziesz miał działającą konfigurację TF i zrozumiesz wiele ważnych pojęć we frameworku TF.

Zakładanie Federacji Handlowej

Aby uzyskać szczegółowe informacje na temat konfigurowania środowiska programistycznego TF, zobacz Konfiguracja maszyny . Reszta tego samouczka zakłada, ż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 TF Framework. Można to rozszerzyć na programowanie modułów poza drzewem źródłowym, kompilując plik JAR ze źródłem handlu, a następnie kompilując moduły na podstawie tego pliku JAR.

Tworzenie klasy testowej (D)

Stwórzmy test hello world, który po prostu zrzuca wiadomość na standardowe wyjście. Testy oparte na handlu generalnie implementują 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 do <tree>/tools/tradefederation/core/src/com/android/tradefed/example/HelloWorldTest.java i odbuduj tradefed z twojej powłoki:

m -jN

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

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

Tworzenie konfiguracji (I)

Testy Federacji Handlowej stają się wykonalne, tworząc plik Configuration , plik XML, który instruuje handel, który test (lub testy) należy uruchomić, a także jakie inne moduły należy wykonać i w jakiej kolejności.

Stwórzmy nową konfigurację dla naszego HelloWorldTest (zwróć uwagę na 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 w lokalnym systemie plików (np. /tmp/helloworld.xml ). TF przeanalizuje plik konfiguracyjny XML (aka config ), załaduje określoną klasę za pomocą odbicia, utworzy jej instancję, rzuci ją na IRemoteTest i wywoła jego metodę run .

Uruchamianie konfiguracji (R)

W swojej powłoce uruchom konsolę z wymianą:

tradefed.sh

Upewnij się, że urządzenie jest podłączone do maszyny hosta i jest widoczne dla handlu:

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

Konfiguracje można wykonać za pomocą polecenia 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ć "Hello, TF World!" wyjście na terminalu.

Możesz potwierdzić, że polecenie zostało uruchomione, używając list invocations lub li w wierszu poleceń i nie powinno niczego drukować. Jeśli polecenia są aktualnie uruchomione, wyświetlają się 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}'

Dodanie konfiguracji do ścieżki klas (D, I, R)

Dla wygody wdrażania można również pakować konfiguracje do samych plików JAR podlegających wymianie. Tradefed automatycznie rozpoznaje wszystkie konfiguracje umieszczone w folderach konfiguracyjnych na ścieżce klas.

Aby to zilustrować, przenieś plik helloworld.xml do biblioteki podstawowej helloworld.xml ( <tree>/tools/tradefederation/core/res/config/example/helloworld.xml ). Odbuduj tradefed, uruchom ponownie 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 dotąd nasz HelloWorldTest nie robi nic ciekawego. 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 przy użyciu TestInformation , dostarczanego przez platformę, gdy wywoływana jest metoda 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 przebuduj 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 podany jako Dostępny ; czyli urządzenie, które powinno być 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 z numerem seryjnym urządzenia.

Wysyłanie wyników testu (D)

IRemoteTest zgłasza 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 końca (za pośrednictwem ITestInvocationListener#invocationEnded ) każdego wywołania.

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

Oto jak implementacja HelloWorldTest może wyglądać z pojedynczym nieudanym wynikiem testu.

@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 systemu Android na urządzeniu z systemem Android, analizować wyniki i przesyłać je dalej do ITestInvocationListener ). Aby uzyskać szczegółowe informacje, zobacz Typy testów .

Przechowywanie wyników badań (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 przechowywać wyniki wywołania w innym miejscu, na przykład w pliku, określ niestandardową implementację ITestInvocationListener przy użyciu tagu result_reporter w konfiguracji.

TF zawiera również odbiornik XmlResultReporter , który zapisuje wyniki testów do pliku XML w formacie podobnym do używanego przez program zapisywania XML JUnit . Aby określić wynik_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 handel 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ć tak:

<?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 detektory wywołań — wystarczy zaimplementować interfejs ITestInvocationListener .

Tradefed obsługuje wiele detektoró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.

Logowanie (D, I, R)

Urządzenia rejestrujące TF obejmują możliwość:

  1. Przechwytuj logi z urządzenia (aka logcat urządzenia)
  2. Rejestruj dzienniki ze struktury 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 nasłuchiwania wywołania w celu przetworzenia. XmlResultReporter następnie zapisuje przechwycony dziennik urządzenia jako plik.

Dzienniki hosta TF są zgłaszane przy użyciu opakowania CLog dla klasy Log ddmlib. Skonwertujmy 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 . Po przebudowaniu i ponownym uruchomieniu TF powinieneś zobaczyć komunikat dziennika na standardowe wyjście:

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 do stdout . TF zawiera również implementację dziennika, która zapisuje komunikaty do pliku: FileLogger . Aby dodać rejestrowanie plików, dodaj tag logger do konfiguracji, określają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 uruchom ponownie 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 wyświetleniu powinien zawierać komunikat dziennika HelloWorldTest:

more /tmp/0/inv_6390011618174565918/host_log_4255420317120216614.txt

Przykładowe dane wyjściowe:

…
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ń za pomocą adnotacji @Option .

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

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 zademonstrować, że została odebrana poprawnie:

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

Na koniec przebuduj 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'

Przekazywanie wartości z wiersza poleceń

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

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

Konfiguracje TF zawierają również 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 o „drukowaniu tylko ważnych opcji”. Aby zmniejszyć bałagan w pomocy opcji, TF używa atrybutu Option#importance , aby określić, czy wyświetlać konkretny tekst pomocy pola @Option , gdy podano --help . --help-all zawsze pokazuje pomoc dla wszystkich pól @Option , niezależnie od ważności. Aby uzyskać szczegółowe informacje, zobacz Option.Importance .

Przekazywanie wartości z konfiguracji

Możesz również określić wartość Option w konfiguracji, dodając element <option name="" value=""> . Przetestuj go za pomocą helloworld.xml :

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

Ponowne budowanie i uruchamianie helloworld powinno teraz generować następujące dane wyjściowe:

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

Pomoc konfiguracji 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 standardowe wyjście. Wcześniej w samouczku mogłeś zauważyć, że komunikat dziennika „Hello, TF World! Mam urządzenie…” przestał być wyświetlany na stdout po przełączeniu na używanie FileLogger . Możesz zwiększyć szczegółowość logowania do stdout, przekazując --log-level-display argument --log-level-display .

Wypróbuj to teraz, a oprócz tego, że jesteś zalogowany do pliku, na standardowe wyjście powinien pojawić się komunikat dziennika „Mam urządzenie”:

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 coś 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 zadać pytanie w Grupie dyskusyjnej Google dla platformy Android , wpisując w tytule wiadomości „Federacja handlowa”.