Questo tutorial illustra la creazione di una configurazione di test "hello world" per Trade Federation (TradeFed o TF) e fornisce un'introduzione pratica al framework TF. A partire da un ambiente di sviluppo, creerai una configurazione semplice e aggiungerai funzionalità.
Il tutorial presenta il processo di sviluppo del test come un insieme di esercizi, ciascuno costituito da diversi passaggi, che mostrano come creare e perfezionare gradualmente la configurazione. Viene fornito tutto il codice di esempio necessario per completare la configurazione del test e il titolo di ogni esercizio è annotato con una lettera che descrive i ruoli coinvolti in quel passaggio:
- D per sviluppatore
- I per l'integratore
- R per Test Runner
Al termine del tutorial, avrai una configurazione TF funzionante e avrai compreso molti concetti importanti del framework TF.
Configurare Trade Federation
Per informazioni dettagliate sulla configurazione dell'ambiente di sviluppo di TF, consulta Configurazione della macchina. Il resto di questo tutorial presuppone che tu abbia aperto una shell inizializzata per l'ambiente TF.
Per semplicità, questo tutorial illustra l'aggiunta di una configurazione e delle relative classi alla libreria di base del framework TF. Questa operazione può essere estesa allo sviluppo di moduli al di fuori dell'albero di origine compilando il JAR di TradeFed e poi compilando i moduli in base a quel JAR.
Crea una classe di test (D)
Creiamo un test di tipo "Hello World" che esamini un messaggio in stdout. Un test tradefed in genere implementa l'interfaccia IRemoteTest. Ecco un'implementazione per 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!"); } }
Salva questo codice di esempio in <tree>/tools/tradefederation/core/src/com/android/tradefed/example/HelloWorldTest.java
e ricostruisci il pacchetto tradefed dalla shell:
m -jN
Tieni presente che CLog.i
nell'esempio riportato sopra viene utilizzato per indirizzare l'output alla console. Ulteriori informazioni su come accedere a Trade Federation sono descritte in Logging (D, I, R).
Se la compilazione non va a buon fine, consulta la sezione Configurazione della macchina per assicurarti di non aver perso un passaggio.
Creare una configurazione (I)
I test di Trade Federation vengono resi eseguibili creando una configurazione, un file XML che indica a TradeFed quale test o quali test eseguire, nonché quali altri moduli eseguire e in quale ordine.
Creiamo una nuova configurazione per il nostro HelloWorldTest (tieni presente il nome completo della classe di HelloWorldTest):
<configuration description="Runs the hello world test"> <test class="com.android.tradefed.example.HelloWorldTest" /> </configuration>
Salva questi dati in un file helloworld.xml
in un punto qualsiasi del file system locale (ad es. /tmp/helloworld.xml
). TF analizzerà il file XML di configurazione (noto anche come config), caricherà la classe specificata utilizzando la riflessione, la eseguirà in un'istanza, la convertirà in un IRemoteTest
e chiamerà il relativo metodo run
.
Esegui la configurazione (R)
Dalla shell, avvia la console tradefed:
tradefed.sh
Assicurati che un dispositivo sia connesso alla macchina host e sia visibile a tradefed:
tf> list devices Serial State Product Variant Build Battery 004ad9880810a548 Available mako mako JDQ39 100
Le configurazioni possono essere eseguite utilizzando il comando run <config>
console. Prova:
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!
Dovresti vedere l'output "Hello, TF World!" sul terminale.
Puoi verificare che l'esecuzione di un comando sia terminata utilizzando list invocations
o
l i
nel prompt della console. Non dovrebbe essere stampato nulla. Se i comandi sono attualmente in esecuzione, vengono visualizzati come segue:
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}'
Aggiungi la configurazione al percorso di classe (D, I, R)
Per comodità di implementazione, puoi anche raggruppare le configurazioni nei JAR di tradefed. Tradefed riconosce automaticamente tutte le configurazioni inserite nelle cartelle config nel percorso di classe.
A titolo esemplificativo, sposta il file helloworld.xml
nella libreria di base di tradefed (<tree>/tools/tradefederation/core/res/config/example/helloworld.xml
). Ricostruisci tradefed, riavvia la console di tradefed e chiedi a tradefed di visualizzare l'elenco delle configurazioni dal percorso di classe:
tf> list configs […] example/helloworld: Runs the hello world test
Ora puoi eseguire la configurazione di helloworld utilizzando:
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!
Interagire con un dispositivo (D, R)
Finora, il nostro HelloWorldTest non fa nulla di interessante. La specialità di TradeFed è eseguire test utilizzando dispositivi Android, quindi aggiungi un dispositivo Android al test.
I test possono ottenere un riferimento a un dispositivo Android utilizzando TestInformation
, fornito
dal framework quando viene chiamato il metodo IRemoteTest#run
.
Modifichiamo il messaggio di stampa di HelloWorldTest in modo da visualizzare il numero di serie del dispositivo:
@Override public void run(TestInformation testInfo, ITestInvocationListener listener) throws DeviceNotAvailableException { CLog.i("Hello, TF World! I have device " + testInfo.getDevice().getSerialNumber()); }
Ora ricostruisci il tradefed e controlla l'elenco dei dispositivi:
tradefed.sh
tf> list devices Serial State Product Variant Build Battery 004ad9880810a548 Available mako mako JDQ39 100
Prendi nota del numero di serie indicato come Disponibile, ovvero il dispositivo che deve essere allocato a 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
Dovresti vedere il nuovo messaggio di stampa che mostra il numero di serie del dispositivo.
Inviare i risultati del test (D)
IRemoteTest
genera i risultati chiamando i metodi sull'istanza
ITestInvocationListener
fornita al metodo #run
. Il framework TF stesso è responsabile di segnalare l'inizio (tramite ITestInvocationListener#invocationStarted) e la fine (tramite ITestInvocationListener#invocationEnded) di ogni chiamata.
Un esecuzione di test è una raccolta logica di test. Per segnalare i risultati dei test,
IRemoteTest
è responsabile della segnalazione dell'inizio di un'esecuzione del test,
dell'inizio e della fine di ogni test e della fine dell'esecuzione del test.
Ecco come potrebbe essere l'implementazione di HelloWorldTest con un singolo risultato di test non riuscito.
@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 include diverse implementazioni di IRemoteTest
che puoi riutilizzare
invece di scriverne una da zero. Ad esempio,
InstrumentationTest
può eseguire i test di un'applicazione Android da remoto su un dispositivo Android, analizzare
i risultati e inoltrarli al ITestInvocationListener
).
Per maggiori dettagli, consulta
Tipi di test.
Risultati del test dello Store (I)
L'implementazione predefinita dell'ascoltatore di test per una configurazione TF è TextResultReporter, che esegue il dump dei risultati di un'invocazione in stdout. A titolo esemplificativo, esegui la configurazione HelloWorldTest della sezione precedente:
./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
Per archiviare i risultati di un'invocazione altrove, ad esempio in un file, specifica un'implementazione ITestInvocationListener
personalizzata utilizzando il tag result_reporter
nella configurazione.
TF include anche l'ascoltatore
XmlResultReporter
che scrive i risultati dei test in un file XML in un formato simile a quello
utilizzato dallo scrittore XML JUnit di ant. Per specificare result_reporter nella configurazione, modifica il file …/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>
Ora ricostruisci tradefed ed esegui di nuovo l'esempio 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
Nota il messaggio di log che indica che è stato generato un file XML. Il file generato dovrebbe avere il seguente aspetto:
<?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>
Puoi anche scrivere i tuoi ascoltatori di invocazioni personalizzati, che devono semplicemente implementare l'interfaccia ITestInvocationListener.
Tradefed supporta più ascoltatori di chiamate, quindi puoi inviare i risultati dei test
a più destinazioni indipendenti. Per farlo, specifica più tag <result_reporter>
nella configurazione.
Strutture di logging (D, I, R)
Le strutture di registrazione di TF includono la possibilità di:
- Acquisisci i log dal dispositivo (ovvero logcat del dispositivo)
- Registra i log del framework Trade Federation in esecuzione sulla macchina host (noto anche come log host)
Il framework TF acquisisce automaticamente il logcat dal dispositivo allocato
e lo invia all'ascoltatore di chiamate per l'elaborazione.
XmlResultReporter
salva quindi il logcat del dispositivo acquisito come file.
I log dell'host di TF vengono registrati utilizzando il wrapper CLog per la classe Log di ddmlib. Convertiamo la chiamata System.out.println
precedente in HelloWorldTest in una chiamata CLog
:
@Override public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { CLog.i("Hello, TF World! I have device %s", getDevice().getSerialNumber());
CLog
gestisce direttamente l'interpolazione di stringhe, in modo simile a
String.format
. Quando ricostruisci ed esegui di nuovo TF, dovresti vedere il messaggio di log su stdout:
tf> run example/helloworld … 05-16 21:30:46 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548 …
Per impostazione predefinita, tradefed
esporta i messaggi del log dell'host su stdout. TF include anche un'implementazione di log che scrive
i messaggi in un file:
FileLogger.
Per aggiungere il logging dei file, aggiungi un tag logger
alla configurazione, specificando il nome completo della classe 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>
Ora, ricostruisci ed esegui di nuovo l'esempio 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 …
Il messaggio del log indica il percorso del log dell'host, che, se visualizzato, dovrebbe contenere il messaggio del log HelloWorldTest:
more /tmp/0/inv_6390011618174565918/host_log_4255420317120216614.txt
Esempio di output:
… 05-16 21:38:21 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548
Opzioni di gestione (D, I, R)
Gli oggetti caricati da una configurazione TF (ovvero oggetti di configurazione) possono anche ricevere dati dagli argomenti della riga di comando tramite l'uso dell'annotazione @Option
.
Per partecipare, una classe di oggetti Configuration applica l'annotazione @Option
a un campo membro e gli fornisce un nome univoco. In questo modo, il valore del campo del membro può essere compilato tramite un'opzione a riga di comando (e l'opzione viene aggiunta automaticamente al sistema di guida alla configurazione).
Nota:non tutti i tipi di campo sono supportati. Per una descrizione dei tipi supportati, consulta OptionSetter.
Aggiungiamo un @Option
a 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";
Aggiungiamo un messaggio di log per visualizzare il valore dell'opzione in HelloWorldTest in modo da dimostrare che è stato ricevuto correttamente:
@Override public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { … CLog.logAndDisplay(LogLevel.INFO, "I received option '%s'", mMyOption);
Infine, ricostruisci TF ed esegui helloworld. Dovresti visualizzare un messaggio di log con il valore predefinito my_option
:
tf> run example/helloworld … 05-24 18:30:05 I/HelloWorldTest: I received option 'thisisthedefault'
Passare valori dalla riga di comando
Passa un valore per my_option
; dovresti vedere
my_option
compilato con quel valore:
tf> run example/helloworld --my_option foo … 05-24 18:33:44 I/HelloWorldTest: I received option 'foo'
Le configurazioni di TF includono anche un sistema di guida che mostra automaticamente il testo di aiuto per i campi @Option
. Prova subito e dovresti vedere il testo di aiuto per 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.
Tieni presente il messaggio relativo alla "stampa solo delle opzioni importanti". Per ridurre il numero di opzioni di guida, TF utilizza l'attributo Option#importance
per determinare se mostrare un determinato testo di guida per il campo @Option
quando viene specificato --help
. --help-all
mostra sempre la guida per tutti i campi @Option
, indipendentemente dall'importanza. Per maggiori dettagli, consulta
Option.Importance.
Passare valori da una configurazione
Puoi anche specificare un valore Option all'interno della configurazione aggiungendo un elemento
<option name="" value="">
. Provalo utilizzando
helloworld.xml
:
<test class="com.android.tradefed.example.HelloWorldTest" > <option name="my_option" value="fromxml" /> </test>
La nuova compilazione ed esecuzione di helloworld dovrebbe produrre questo output:
05-24 20:38:25 I/HelloWorldTest: I received option 'fromxml'
Anche la guida alla configurazione dovrebbe aggiornarsi per indicare il valore predefinito di
my_option
:
tf> run example/helloworld --help test options: -m, --my_option this is the option's help text Default: fromxml.
Anche altri oggetti di configurazione inclusi nella configurazione di helloworld, come
FileLogger
, accettano opzioni. L'opzione
--log-level-display
è interessante perché filtra i log visualizzati su stdout. All'inizio del tutorial, potresti aver notato il messaggio "Hello, TF
World! Il messaggio di log "Ho il dispositivo…" non viene più visualizzato su stdout dopo che abbiamo iniziato a utilizzare FileLogger
. Puoi aumentare la modalità dettagliata del logging in stdout passando l'argomento --log-level-display
.
Prova subito e dovresti vedere il messaggio di log "Ho il dispositivo" riapparire su stdout, oltre a essere registrato in un file:
tf> run example/helloworld --log-level-display info … 05-24 18:53:50 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548
Abbiamo terminato.
Ti ricordiamo che, se hai difficoltà, il codice sorgente di Trade Federation contiene molte informazioni utili non riportate nella documentazione. Se non riesci a risolvere il problema, prova a chiedere nel gruppo Google android-platform, inserendo "Trade Federation" nell'oggetto del messaggio.