Este tutorial orienta você na criação de uma configuração de teste da Federação de Comércio "olá mundo" (Tradefed ou TF) e fornece uma introdução prática à estrutura do TF. A partir de um ambiente de desenvolvimento, você criará uma configuração simples e adicionará recursos.
O tutorial apresenta o processo de desenvolvimento de teste como um conjunto de exercícios, cada um composto por diversas etapas, que demonstram como construir e refinar gradualmente sua configuração. Todo o código de exemplo necessário para concluir a configuração do teste é fornecido, e o título de cada exercício é anotado com uma letra descrevendo as funções envolvidas nessa etapa:
- D para desenvolvedor
- Eu para Integrador
- R para executor de teste
Depois de concluir o tutorial, você terá uma configuração do TF funcional e compreenderá muitos conceitos importantes da estrutura do TF.
Configurar Federação Comercial
Para obter detalhes sobre como configurar o ambiente de desenvolvimento TF, consulte Configuração da máquina . O restante deste tutorial pressupõe que você tenha um shell aberto que foi inicializado no ambiente TF.
Para simplificar, este tutorial ilustra a adição de uma configuração e suas classes à biblioteca principal da estrutura TF. Isso pode ser estendido ao desenvolvimento de módulos fora da árvore de origem, compilando o JAR tradefed e, em seguida, compilando seus módulos nesse JAR.
Crie uma classe de teste (D)
Vamos criar um teste hello world que apenas envia uma mensagem para stdout. Um teste tradefed geralmente implementa a interface IRemoteTest . Aqui está uma implementação para o 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!"); } }
Salve este código de exemplo em <tree>/tools/tradefederation/core/src/com/android/tradefed/example/HelloWorldTest.java
e reconstrua o tradefed a partir do seu shell:
m -jN
Observe que CLog.i
no exemplo acima é usado para direcionar a saída para o console. Mais informações sobre registro na Federação Comercial estão descritas em Registro (D, I, R) .
Se a compilação não for bem-sucedida, consulte a Configuração da Máquina para garantir que você não perdeu nenhuma etapa.
Crie uma configuração (I)
Os testes da Trade Federation tornam-se executáveis através da criação de um Configuration , um arquivo XML que instrui o tradefed sobre qual teste (ou testes) executar, bem como quais outros módulos executar e em que ordem.
Vamos criar uma nova configuração para nosso HelloWorldTest (observe o nome completo da classe HelloWorldTest):
<configuration description="Runs the hello world test"> <test class="com.android.tradefed.example.HelloWorldTest" /> </configuration>
Salve esses dados em um arquivo helloworld.xml
em qualquer lugar do seu sistema de arquivos local (por exemplo /tmp/helloworld.xml
). TF irá analisar o arquivo XML de configuração (também conhecido como config ), carregar a classe especificada usando reflexão, instanciá-la, convertê-la em IRemoteTest
e chamar seu método run
.
Execute a configuração (R)
No seu shell, inicie o console tradefed:
tradefed.sh
Certifique-se de que um dispositivo esteja conectado à máquina host e visível para tradefed:
tf> list devices Serial State Product Variant Build Battery 004ad9880810a548 Available mako mako JDQ39 100
As configurações podem ser executadas usando o comando do console run <config>
. Tentar:
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!
Você deverá ver "Olá, TF World!" saída no terminal.
Você pode confirmar se a execução de um comando foi concluída usando list invocations
ou li
no prompt do console e ele não deve imprimir nada. Se os comandos estiverem em execução no momento, eles serão exibidos da seguinte forma:
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}'
Adicione a configuração ao classpath (D, I, R)
Para conveniência de implantação, você também pode agrupar configurações nos próprios JARs negociados. Tradefed reconhece automaticamente todas as configurações colocadas nas pastas de configuração no classpath.
Para ilustrar, mova o arquivo helloworld.xml
para a biblioteca principal tradefed ( <tree>/tools/tradefederation/core/res/config/example/helloworld.xml
). Reconstrua o tradefed, reinicie o console do tradefed e peça ao tradefed para exibir a lista de configurações do classpath:
tf> list configs […] example/helloworld: Runs the hello world test
Agora você pode executar a configuração helloworld usando:
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!
Interaja com um dispositivo (D, R)
Até agora, nosso HelloWorldTest não está fazendo nada de interessante. A especialidade da Tradefed é executar testes usando dispositivos Android, então vamos adicionar um dispositivo Android ao teste.
Os testes podem obter uma referência a um dispositivo Android usando TestInformation
, fornecido pela estrutura quando o método IRemoteTest#run
é chamado.
Vamos modificar a mensagem de impressão HelloWorldTest para exibir o número de série do dispositivo:
@Override public void run(TestInformation testInfo, ITestInvocationListener listener) throws DeviceNotAvailableException { CLog.i("Hello, TF World! I have device " + testInfo.getDevice().getSerialNumber()); }
Agora reconstrua o tradefed e verifique a lista de dispositivos:
tradefed.sh
tf> list devices Serial State Product Variant Build Battery 004ad9880810a548 Available mako mako JDQ39 100
Anote o número de série listado como Disponível ; esse é o dispositivo que deve ser alocado para 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
Você deverá ver a nova mensagem de impressão exibindo o número de série do dispositivo.
Enviar resultados de testes (D)
IRemoteTest
relata resultados chamando métodos na instância ITestInvocationListener fornecida ao método #run
. A própria estrutura TF é responsável por relatar o início (via ITestInvocationListener#invocationStarted ) e o fim (via ITestInvocationListener#invocationEnded ) de cada invocação.
Uma execução de teste é uma coleção lógica de testes. Para relatar os resultados do teste, IRemoteTest
é responsável por relatar o início de uma execução de teste, o início e o fim de cada teste e o final da execução de teste.
Esta é a aparência da implementação do HelloWorldTest com um único resultado de teste com falha.
@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 inclui várias implementações IRemoteTest
que você pode reutilizar em vez de escrever a sua própria do zero. Por exemplo, InstrumentationTest pode executar testes de um aplicativo Android remotamente em um dispositivo Android, analisar os resultados e encaminhá-los para ITestInvocationListener
). Para obter detalhes, consulte Tipos de teste .
Armazenar resultados de testes (I)
A implementação de ouvinte de teste padrão para uma configuração TF é TextResultReporter , que despeja os resultados de uma invocação para stdout. Para ilustrar, execute a configuração HelloWorldTest da seção anterior:
./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
Para armazenar os resultados de uma chamada em outro lugar, como em um arquivo, especifique uma implementação ITestInvocationListener
personalizada usando a tag result_reporter
em sua configuração.
O TF também inclui o ouvinte XmlResultReporter , que grava os resultados do teste em um arquivo XML em um formato semelhante ao usado pelo gravador Ant JUnit XML. Para especificar o result_reporter na configuração, edite a configuração …/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>
Agora reconstrua o tradefed e execute novamente a amostra 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
Observe a mensagem de log informando que um arquivo XML foi gerado; o arquivo gerado deve ficar assim:
<?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>
Você também pode escrever seus próprios ouvintes de invocação personalizados — eles simplesmente precisam implementar a interface ITestInvocationListener .
Tradefed oferece suporte a vários ouvintes de invocação, para que você possa enviar resultados de testes para vários destinos independentes. Para fazer isso, basta especificar várias tags <result_reporter>
em sua configuração.
Instalações de registro (D, I, R)
As instalações de registro da TF incluem a capacidade de:
- Capture logs do dispositivo (também conhecido como logcat do dispositivo)
- Registrar logs da estrutura da Trade Federation em execução na máquina host (também conhecido como log de host)
A estrutura TF captura automaticamente o logcat do dispositivo alocado e o envia ao ouvinte de invocação para processamento. XmlResultReporter
salva o logcat do dispositivo capturado como um arquivo.
Os logs do host TF são relatados usando o wrapper CLog para a classe ddmlib Log. Vamos converter a chamada System.out.println
anterior em HelloWorldTest em uma chamada CLog
:
@Override public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { CLog.i("Hello, TF World! I have device %s", getDevice().getSerialNumber());
CLog
lida diretamente com a interpolação de strings, semelhante a String.format
. Ao reconstruir e executar novamente o TF, você deverá ver a mensagem de log no stdout:
tf> run example/helloworld … 05-16 21:30:46 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548 …
Por padrão, tradefed envia mensagens de log do host para stdout . TF também inclui uma implementação de log que grava mensagens em um arquivo: FileLogger . Para adicionar o registro de arquivos, adicione uma tag logger
à configuração, especificando o nome completo da 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>
Agora, reconstrua e execute o exemplo helloworld novamente:
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 …
A mensagem de log indica o caminho do log do host, que, quando visualizado, deve conter sua mensagem de log HelloWorldTest:
more /tmp/0/inv_6390011618174565918/host_log_4255420317120216614.txt
Exemplo de saída:
… 05-16 21:38:21 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548
Opções de manuseio (D, I, R)
Objetos carregados de uma configuração TF (também conhecidos como objetos de configuração ) também podem receber dados de argumentos de linha de comando por meio do uso da anotação @Option
.
Para participar, uma classe de objeto Configuration aplica a anotação @Option
a um campo membro e fornece a ele um nome exclusivo. Isso permite que o valor do campo membro seja preenchido por meio de uma opção de linha de comando (e também adiciona automaticamente essa opção ao sistema de ajuda de configuração).
Nota: Nem todos os tipos de campo são suportados. Para obter uma descrição dos tipos suportados, consulte OptionSetter .
Vamos adicionar um @Option
ao 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";
A seguir, vamos adicionar uma mensagem de log para exibir o valor da opção em HelloWorldTest para que possamos demonstrar que ela foi recebida corretamente:
@Override public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { … CLog.logAndDisplay(LogLevel.INFO, "I received option '%s'", mMyOption);
Finalmente, reconstrua o TF e execute helloworld; você deverá ver uma mensagem de log com o valor padrão my_option
:
tf> run example/helloworld … 05-24 18:30:05 I/HelloWorldTest: I received option 'thisisthedefault'
Passar valores da linha de comando
Passe um valor para my_option
; você deverá ver my_option
preenchido com esse valor:
tf> run example/helloworld --my_option foo … 05-24 18:33:44 I/HelloWorldTest: I received option 'foo'
As configurações do TF também incluem um sistema de ajuda, que exibe automaticamente o texto de ajuda para os campos @Option
. Experimente agora e você verá o texto de ajuda para 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.
Observe a mensagem sobre “imprimir apenas as opções importantes”. Para reduzir a confusão de ajuda de opções, TF usa o atributo Option#importance
para determinar se deve mostrar um texto de ajuda de campo @Option
específico quando --help
é especificado. --help-all
sempre mostra ajuda para todos os campos @Option
, independentemente da importância. Para obter detalhes, consulte Option.Importance .
Passar valores de uma configuração
Você também pode especificar um valor Option dentro da configuração adicionando um elemento <option name="" value="">
. Teste-o usando helloworld.xml
:
<test class="com.android.tradefed.example.HelloWorldTest" > <option name="my_option" value="fromxml" /> </test>
A reconstrução e execução do helloworld agora deve produzir esta saída:
05-24 20:38:25 I/HelloWorldTest: I received option 'fromxml'
A ajuda de configuração também deve ser atualizada para indicar o valor padrão de my_option
:
tf> run example/helloworld --help test options: -m, --my_option this is the option's help text Default: fromxml.
Outros objetos de configuração incluídos na configuração helloworld, como FileLogger
, também aceitam opções. A opção --log-level-display
é interessante porque filtra os logs que aparecem no stdout. No início do tutorial, você deve ter notado que a mensagem de log "Olá, TF World! Tenho um dispositivo…' parou de ser exibida no stdout depois que passamos a usar FileLogger
. Você pode aumentar o detalhamento do registro no stdout passando o --log-level-display
arg --log-level-display
.
Tente fazer isso agora e você verá a mensagem de log 'Tenho dispositivo' reaparecer no stdout, além de ser logado em um arquivo:
tf> run example/helloworld --log-level-display info … 05-24 18:53:50 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548
Isso é tudo, pessoal!
Como lembrete, se você estiver preso em alguma coisa, o código-fonte da Trade Federation contém muitas informações úteis que não estão expostas na documentação. Se tudo mais falhar, tente perguntar no Grupo Google da plataforma Android , com "Trade Federation" no assunto da mensagem.