W tym samouczku omawiamy proces tworzenia konfiguracji testowej „hello world” dla Trade Federation (TF). Przedstawiamy też praktyczne informacje na temat tej platformy. Zaczynając od środowiska programistycznego, utworzysz prostą konfigurację i dodasz funkcje.
Samouczek przedstawia proces tworzenia testu jako zestaw ćwiczeń, z których każde składa się z kilku kroków. Każdy z nich pokazuje, jak tworzyć i stopniowo ulepszać konfigurację. Udostępniamy cały przykładowy kod potrzebny do skonfigurowania testu, a tytuł każdego ćwiczenia jest opatrzony literą, która określa role zaangażowane w konkretnym kroku:
- D – deweloper
- I dla integratora
- R – Test Runner
Po zakończeniu samouczka będziesz mieć działającą konfigurację TF i zrozumiesz wiele ważnych pojęć związanych z frameworkiem TF.
Konfigurowanie federacji handlowej
Szczegółowe informacje o konfigurowaniu środowiska rozwoju TF znajdziesz w artykule Konfigurowanie maszyny. W dalszej części tego samouczka zakładamy, że masz otwartą powłokę skonfigurowaną pod kątem środowiska TF.
W tym samouczku pokazujemy, jak dodać konfigurację i jej klasy do głównej biblioteki frameworka TF. Można to rozszerzyć na potrzeby tworzenia modułów poza drzewem źródłowym, kompilując plik JAR z zawartością, a potem kompilując swoje moduły z użyciem tego pliku.
Tworzenie testowej klasy (D)
Utwórz test „Hello World”, który po prostu wypisuje wiadomość na stdout. Test skompilowany z użyciem narzędzia MSBuild zwykle 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 pliku <tree>/tools/tradefederation/core/src/com/android/tradefed/example/HelloWorldTest.java
i ponownie skompiluj tradefed w powłoce:
m -jN
Pamiętaj, że w tym przykładzie parametr CLog.i
służy do kierowania danych na konsolę. Więcej informacji o rejestrowaniu w ramach wymiany danych znajdziesz w artykule Rejestrowanie (D, I, R).
Jeśli kompilacja się nie powiedzie, zapoznaj się z sekcją Konfiguracja maszyny, aby upewnić się, że nie pominięto żadnego kroku.
Tworzenie konfiguracji (I)
Testy Federation Trade są wykonywane przez utworzenie konfiguracji, czyli pliku XML, który instruuje narzędzie tradefed, które testy mają być wykonywane, a także które inne moduły mają być wykonywane i w jakiej kolejności.
Utwórz nową konfigurację dla 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 XML konfiguracji (zwany też config), załaduje określoną klasę za pomocą odbicia lustrzanego, tworzy jej instancję, przekształca ją w element typu IRemoteTest
i wywołuje metodę run
.
Uruchom konfigurację (R)
W powłoce uruchom konsolę tradefed:
tradefed.sh
Upewnij się, że urządzenie jest połączone z hostem i widoczne dla tradefed:
tf> list devices Serial State Product Variant Build Battery 004ad9880810a548 Available mako mako JDQ39 100
Konfiguracje można uruchamiać za pomocą polecenia run <config>
w konsoli. Spróbuj:
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!
W terminalu powinien pojawić się komunikat „Hello, TF World!”.
Aby sprawdzić, czy polecenie zostało wykonane, użyj w prośbie konsoli polecenia list invocations
lub l i
. Nie powinno się nic wydrukować. Jeśli polecenia są obecnie wykonywane, wyświetlają się w ten 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 classpath (D, I, R)
Dla wygody wdrażania możesz też umieścić konfiguracje w samych plikach JAR. Narzędzie Tradefed automatycznie rozpoznaje wszystkie konfiguracje umieszczone w folderach config w ścieżce klas.
Aby to zrobić, przenieś plik helloworld.xml
do biblioteki podstawowej tradefed (<tree>/tools/tradefederation/core/res/config/example/helloworld.xml
). Zbuduj ponownie tradefed, uruchom ponownie konsolę tradefed, a następnie poproś tradefed o wyświetlenie listy konfiguracji z ścieżki klas:
tf> list configs […] example/helloworld: Runs the hello world test
Teraz możesz 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)
Do tej pory nasz HelloWorldTest nie robi nic interesującego. Specjalnością Tradefed jest przeprowadzanie testów na urządzeniach z Androidem, więc dodaj do testu urządzenie z Androidem.
Testy mogą uzyskać odwołanie do urządzenia z Androidem za pomocą funkcji TestInformation
, która jest udostępniana przez platformę podczas wywołania metody IRemoteTest#run
.
Zmieńmy komunikat drukowania 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 ponownie skompiluj plik tradefed i sprawdź listę urządzeń:
tradefed.sh
tf> list devices Serial State Product Variant Build Battery 004ad9880810a548 Available mako mako JDQ39 100
Zwróć uwagę na numer seryjny podany jako Dostępne. To urządzenie powinno zostać przypisane 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
Powinien pojawić się nowy komunikat drukowania z numerem seryjnym urządzenia.
Wysyłanie wyników testu (D)
IRemoteTest
przekazuje wyniki przez wywoływanie metod instancji ITestInvocationListener przekazanej do metody #run
. Sam framework TF odpowiada za zgłaszanie początku (za pomocą metody ITestInvocationListener#invocationStarted) i końca (za pomocą metody ITestInvocationListener#invocationEnded) każdego wywołania.
Testowanie to logiczny zbiór testów. Aby zgłaszać wyniki testów,
IRemoteTest
odpowiada za zgłaszanie rozpoczęcia testu, rozpoczęcia i zakończenia każdego testu oraz zakończenia testu.
Oto jak może wyglądać implementacja HelloWorldTest z jednym wynikiem testu nieudanego.
@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żywać wielokrotnie zamiast pisać własne od podstaw. Na przykład InstrumentationTest może zdalnie uruchamiać testy aplikacji na urządzeniu z Androidem, analizować wyniki i przekazywać je do ITestInvocationListener
. Więcej informacji znajdziesz w sekcji Typy testów.
Wyniki testów w sklepie (I)
Domyślną implementacją odbiornika testów w przypadku konfiguracji TF jest TextResultReporter, który zapisuje wyniki wywołania w wyjściu standardowym. 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, np. w pliku, określ niestandardową implementację ITestInvocationListener
za pomocą tagu result_reporter
w konfiguracji.
TF zawiera też listenera XmlResultReporter, który zapisuje wyniki testów w pliku XML w formacie podobnym do używanego przez ant JUnit XML writer. Aby określić parametr result_reporter w konfiguracji, edytuj plik …/res/config/example/helloworld.xml
config:
<configuration description="Runs the hello world test"> <test class="com.android.tradefed.example.HelloWorldTest" /> <result_reporter class="com.android.tradefed.result.XmlResultReporter" /> </configuration>
Teraz ponownie utwórz plik tradefed i ponownie uruchom przykład „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 w logu, który informuje o wygenerowaniu pliku 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 też napisać własne niestandardowe interfejsy wywołania. Wystarczy, że zaimplementujesz interfejs ITestInvocationListener.
Tradefed obsługuje wielu odbiorców wywołania, dzięki czemu możesz wysyłać wyniki testów do wielu niezależnych miejsc docelowych. Aby to zrobić, w konfiguracji wskaż kilka tagów <result_reporter>
.
Usługi rejestrowania (D, I, R)
Możliwości logowania w TF obejmują:
- Przechwytywanie dzienników z urządzenia (czyli logcat urządzenia)
- Dzienniki rejestrowane przez platformę Trade Federation działającą na komputerze hosta (tzw. dziennik hosta)
Ramka TF automatycznie rejestruje logcat z przypisanego urządzenia i przesyła go do komponentu wywołania w celu przetworzenia.
XmlResultReporter
następnie zapisuje zrzut ekranu logcat urządzenia jako plik.
Dzienniki hosta TF są zgłaszane za pomocą opakowania CLog dla klasy ddmlib Log. Przekształcimy poprzednie wywołanie System.out.println
w HelloWorldTest w 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 interpolację ciągu znaków bezpośrednio, podobnie jak String.format
. Po ponownym utworzeniu i uruchomieniu TF powinien wyświetlić się komunikat log na wyjściu standardowym:
tf> run example/helloworld … 05-16 21:30:46 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548 …
Domyślnie narzędzie tradefed wyprowadza komunikaty hosta do standardowego wyjścia. TF zawiera też implementację logowania, która zapisuje wiadomości do pliku: FileLogger.
Aby dodać rejestrowanie plików, dodaj do konfiguracji tag 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 ponownie skompiluj i 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 logowania wskazuje ścieżkę do pliku logowania hosta, który po otwarciu powinien zawierać komunikat logowania 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 wczytane z konfiguracji TF (czyli obiekty konfiguracji) mogą też otrzymywać dane z argumentów wiersza poleceń za pomocą adnotacji @Option
.
Aby uczestniczyć w tym procesie, klasa obiektu Configuration stosuje adnotację @Option
do pola członka i nadaje mu niepowtarzalną nazwę. Dzięki temu wartość pola członka może być wypełniana za pomocą opcji wiersza poleceń (a także automatycznie dodawana do systemu pomocy konfiguracji).
Uwaga: nie wszystkie typy pól są obsługiwane. Opis obsługiwanych typów znajdziesz w interfejsie 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 dodamy komunikat dziennika, aby wyświetlić wartość opcji w HelloWorldTest, abyśmy mogli sprawdzić, czy została ona prawidłowo odebrana:
@Override public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { … CLog.logAndDisplay(LogLevel.INFO, "I received option '%s'", mMyOption);
Na koniec ponownie skompiluj TF i uruchom helloworld. Powinieneś zobaczyć wiadomość z wartością domyślną 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ść dla zmiennej my_option
. Zmienna my_option
powinna zostać wypełniona tą wartością:
tf> run example/helloworld --my_option foo … 05-24 18:33:44 I/HelloWorldTest: I received option 'foo'
Konfiguracje TF zawierają też system pomocy, który automatycznie wyświetla tekst pomocy dla pól @Option
. Wypróbuj to teraz. Powinien pojawić się 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 ograniczyć natłok w informacjach o opcjach, TF używa atrybutu Option#importance
, aby określić, czy należy wyświetlić tekst pomocy dotyczącego pola @Option
, gdy podany jest atrybut --help
. --help-all
zawsze wyświetla pomoc dotyczącą wszystkich pól @Option
, niezależnie od ich znaczenia. Więcej informacji znajdziesz w artykule Option.Importance.
Przekazywanie wartości z konfiguracji
Wartość opcji możesz też określić w konfiguracji, dodając element <option name="" value="">
. Aby przetestować tę funkcję, wykonaj te czynności:helloworld.xml
<test class="com.android.tradefed.example.HelloWorldTest" > <option name="my_option" value="fromxml" /> </test>
Ponowne skompilowanie i uruchomienie helloworld powinno teraz wygenerować ten wynik:
05-24 20:38:25 I/HelloWorldTest: I received option 'fromxml'
Pomoc dotycząca konfiguracji powinna się zaktualizować, aby wskazywać 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 konfiguracji zawarte w konfiguracji helloworld, takie jak FileLogger
, również obsługują opcje. Opcja --log-level-display
jest interesująca, ponieważ filtruje dzienniki, które wyświetlają się na stdout. Wcześniej w tym samouczku mogłeś/mogłaś zauważyć stronę „Hello, TF
World!”. Po przejściu na użycie FileLogger
przestał się wyświetlać komunikat z logów „I have device…” na wyjściu standardowym. Możesz zwiększyć szczegółowość logowania na wyjście standardowe, podając argument --log-level-display
.
Spróbuj teraz wykonać tę czynność. Komunikat logowania „I have device” powinien ponownie pojawić się w stdout, a oprócz tego zostanie zapisany w pliku:
tf> run example/helloworld --log-level-display info … 05-24 18:53:50 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548
To wszystko.
Pamiętaj, że jeśli utkniesz w jakimś miejscu, kod źródłowy Trade Federation zawiera wiele przydatnych informacji, które nie są dostępne w dokumentacji. Jeśli nic nie pomoże, spróbuj zapytać na forum android-platform w Google Groups, wpisując „Trade Federation” w temacie wiadomości.