Exemplo de teste completo do TF

Neste tutorial, orientamos você na criação de um "Hello World" Federação comercial (Tradefed ou TF) e oferece uma introdução prática ao TF de análise de dados em nuvem. Em um ambiente de desenvolvimento, você vai criar um configuração e adicionar recursos.

O tutorial apresenta o processo de desenvolvimento de testes como um conjunto de exercícios, cada uma composta de várias etapas, que demonstram como construir e refinar a configuração. Todo o exemplo de código necessário para concluir o teste é fornecida, e o título de cada exercício é anotado com um que descreve os papéis envolvidos nessa etapa:

  • D para Desenvolvedor
  • I para integrador
  • R para Executor de testes

Depois de concluir o tutorial, você terá uma configuração do TF em funcionamento. e entender vários conceitos importantes no framework do TF.

Configurar a Trade Federation

Para detalhes sobre como configurar o ambiente de desenvolvimento do TF, consulte Máquina Configuração. O restante deste tutorial pressupõe que você tem um shell aberto que foi inicializado no ambiente TF.

Para simplificar, este tutorial ilustra como adicionar uma configuração e seus classes para a biblioteca principal do framework do TF. Isso pode ser estendido ao desenvolvimento fora da árvore de origem compilando o JAR comercializado e, em seguida, compilando seus módulos com esse JAR.

Criar uma classe de teste (D)

Vamos criar um teste "Hello World" que envia uma mensagem para a stdout. Um teste negociado geralmente implementa a IRemoteTest (link em inglês) interface gráfica do usuário. Confira uma implementação do 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!");
    }
}

Salvar este exemplo de código em <tree>/tools/tradefederation/core/src/com/android/tradefed/example/HelloWorldTest.java e recompilar o comércio a partir do shell:

m -jN

Observe que CLog.i no exemplo acima é usado para direcionar a saída para o console. Mais informações sobre login na Trade Federation estão descritas em Logging (D, I, R).

Se o build não for bem-sucedido, consulte Máquina Configure para não perder nenhuma etapa.

Criar uma configuração (I)

Os testes da Trade Federation são executados criando uma Configuration, um arquivo XML que instrui o valor (ou testes) para executar, bem como quais outros módulos executar e em que ordem.

Vamos criar uma nova configuração para o HelloWorldTest. Observe que nome do 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 da sua rede local do sistema de arquivos (por exemplo, /tmp/helloworld.xml). o TF vai analisar Arquivo XML de configuração (também conhecido como config), carregue a classe especificada usando reflexão, instanciar, transmitir para um IRemoteTest e chamar o método run.

Executar a configuração (R)

No shell, inicie o console do Tradefed:

tradefed.sh

Verifique se um dispositivo está conectado à máquina host e visível para troca:

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

As configurações podem ser executadas usando o run <config> comando do console do Cloud. Tente:

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ê verá "Hello, TF World!" no terminal.

Confirme se um comando já foi executado usando list invocations ou l i no prompt do console. Ele não exibirá nada. Se os comandos estiverem em execução, elas são exibidas 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}'

Adicionar a configuração ao caminho de classe (D, I, R)

Para facilitar a implantação, você também pode agrupar configurações no pacote os próprios JARs. O Tradefed reconhece automaticamente todas as configurações colocadas em config no caminho de classe.

Para ilustrar, mova o arquivo helloworld.xml para a tabela biblioteca principal <tree>/tools/tradefederation/core/res/config/example/helloworld.xml). Recrie o Tradefed, reinicie o console negociado e peça para o Tradefed mostrar lista de configurações do caminho de classe:

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!

Interagir com um dispositivo (D, R)

Até agora, nosso HelloWorldTest não fez nada interessante. da Tradefed. é executar testes usando dispositivos Android. Por isso, vamos adicionar um dispositivo para o teste.

Os testes podem receber uma referência a um dispositivo Android usando TestInformation, fornecido pelo framework quando o método IRemoteTest#run é chamado.

Vamos modificar a mensagem de impressão HelloWorldTest para mostrar o número de série dispositivo:

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

Agora, recrie 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. que é o dispositivo que será alocado para o 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ê verá a nova mensagem de impressão com o número de série do dispositivo.

Enviar os resultados do teste (D)

IRemoteTest gera relatórios de resultados chamando métodos na ITestInvocationListener. fornecida ao método #run. O framework do TF é responsável por informar o início (via ITestInvocationListener#invocationStarted). e terminar (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 fim da execução do teste.

Veja como pode ser a implementação do HelloWorldTest com uma única resultado do 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());
}

O TF inclui várias implementações de IRemoteTest que podem ser reutilizadas em vez de escrever o seu do zero. Por exemplo: InstrumentationTest (em inglês) pode executar os testes de um aplicativo Android remotamente em um dispositivo Android, analisar o resultados e encaminhe esses resultados para ITestInvocationListener). Para mais detalhes, consulte Teste Tipos.

Armazenar resultados de testes (I)

A implementação padrão do listener de teste para a configuração do TF é TextResultReporter; que despeja os resultados de uma invocação em stdout. Para ilustrar, execute o comando 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 invocação em outro lugar, como em um arquivo, especifique um implementação personalizada de ITestInvocationListener usando o tag result_reporter na sua configuração.

O TF também inclui XmlResultReporter (link em inglês) que grava os resultados do teste em um arquivo XML em um formato similar ao usado pelo gravador XML JUnit ant. Para especificar o result_reporter na de configuração, edite …/res/config/example/helloworld.xml configuração:

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

Agora, recrie o Tradefed e execute novamente o exemplo do 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 registro informando que um arquivo XML foi gerado. as 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>

Também é possível escrever seus próprios listeners de invocação personalizados. Eles simplesmente é preciso implementar ITestInvocationListener (em inglês) interface gráfica do usuário.

O Tradefed oferece suporte a vários listeners de invocação para que você possa enviar os resultados do teste. a vários destinos independentes. Para isso, basta especificar vários Tags <result_reporter> na sua configuração.

Instalações de geração de registros (D, I, R)

Os recursos de geração de registros do TF incluem a capacidade de:

  1. Capturar registros do dispositivo (também conhecido como logcat do dispositivo)
  2. Gravar registros do framework da Trade Federation em execução na máquina host (também conhecido como registro do host)

O framework do TF captura automaticamente o logcat do dispositivo alocado e a envia ao listener da invocação para processamento. Em seguida, XmlResultReporter salva o logcat do dispositivo capturado como um arquivo.

Os registros de host do TF são relatados Wrapper CLog para a classe Log ddmlib. Vamos converter chamada System.out.println anterior em HelloWorldTest para uma Chamada CLog:

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

O CLog processa a interpolação de strings diretamente, de forma semelhante a String.format. Ao recriar e executar novamente o TF, você verá a mensagem de registro na stdout:

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

Por padrão, negociada gera registro do host mensagens para stdout. O TF também inclui uma implementação de registro que grava mensagens em um arquivo: FileLogger. Para adicionar a geração de registros de arquivos, adicione uma tag logger à configuração, especificando o nome completo da classe de 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, recrie 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 registro indica o caminho do registro do host que, quando visualizado, vai conter a mensagem de registro 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 processamento (D, I, R)

Objetos carregados de uma configuração do TF (também conhecida como objetos de configuração) também podem receber dados de argumentos de linha de comando usando a chamada @Option.

Para participar, uma classe de objeto de configuração aplica o @Option a um campo de membro e fornece a ele um nome exclusivo. Isso permite que valor do campo de membro a ser preenchido por uma opção de linha de comando (e também adiciona automaticamente essa opção ao sistema de ajuda de configuração).

Observação:nem todos os tipos de campo são compatíveis. Para um descrição dos tipos compatíveis, consulte OptionSetter (link em inglês).

Adicione 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 registro para exibir o valor da opção HelloWorldTest para que possamos demonstrar que ele foi recebido corretamente:

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

Por fim, recrie o TF e execute helloworld; vai aparecer uma mensagem de registro Valor padrão de my_option:

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

Transmitir valores da linha de comando

Transmita um valor para my_option. vai aparecer 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 texto de ajuda para os campos @Option. Experimente agora para ver 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 "como imprimir apenas as opções importantes". Para reduzir desorganização, o TF usa o atributo Option#importance para determina se o texto de ajuda de um campo @Option específico será mostrado ao --help é especificado. --help-all sempre mostra ajuda para todos os campos @Option, independentemente da importância. Para mais detalhes, consulte Option.Importance.

Transmitir valores de uma configuração

Também é possível especificar um valor de opção na configuração adicionando um <option name="" value="">. Faça o teste usando helloworld.xml:

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

A recompilação e a execução de helloworld agora devem 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 do 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 aceita opções. A opção --log-level-display é interessante porque filtra os registros que vão aparecer na stdout. Mais cedo no tutorial, você deve ter notado que "Hello, TF Mundo! Eu tenho o dispositivo..." a mensagem de registro parou de ser exibida em stdout depois que passou a usar FileLogger. É possível aumentar o nível de detalhes geração de registros para stdout transmitindo o argumento --log-level-display.

Tente isso agora. A mensagem "Tenho um dispositivo" vai aparecer a mensagem de registro reaparece em stdout, além de ser registrado 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 um lembrete, se você ficar preso em algo, o Comércio O código-fonte da federação tem muitas informações úteis que não estão expostas na a documentação. Se nada der certo, pergunte no android-platform (link em inglês) Grupo do Google, com "Trade Federation" no assunto da mensagem.