En este instructivo, se explica cómo crear una configuración de prueba de Trade Federation (Tradefed o TF) de "Hello World" y se proporciona una introducción práctica al framework de TF. A partir de un entorno de desarrollo, crearás una configuración simple y agregarás funciones.
En el instructivo, se presenta el proceso de desarrollo de pruebas como un conjunto de ejercicios, cada uno de los cuales consta de varios pasos, que demuestran cómo compilar y definir mejor gradualmente tu configuración. Se proporciona todo el código de muestra que necesitas para completar la configuración de la prueba, y el título de cada ejercicio está anotado con una letra que describe los roles involucrados en ese paso:
- D para desarrollador
- I para integrador
- R para Test Runner
Después de completar el instructivo, tendrás una configuración de TF que funcione y comprenderás muchos conceptos importantes del framework de TF.
Configura la Federación de comercio
Para obtener detalles sobre la configuración del entorno de desarrollo de TF, consulta Configuración de la máquina. En el resto de este instructivo, se supone que tienes un shell abierto que se inicializó en el entorno de TF.
Para simplificar, en este instructivo, se muestra cómo agregar una configuración y sus clases a la biblioteca principal del framework de TF. Esto se puede extender al desarrollo de módulos fuera del árbol de origen compilando el JAR de Tradefed y, luego, compilando tus módulos con ese JAR.
Crear una clase de prueba (D)
Crear una prueba de Hello World que solo vuelque un mensaje a stdout Por lo general, una prueba de intercambio de información implementa la interfaz IRemoteTest. A continuación, se muestra una implementación de 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!"); } }
Guarda este código de muestra en <tree>/tools/tradefederation/core/src/com/android/tradefed/example/HelloWorldTest.java
y vuelve a compilar tradefed desde tu shell:
m -jN
Ten en cuenta que CLog.i
en el ejemplo anterior se usa para dirigir el resultado a la consola. En Registro (D, I, R), se describe más información sobre el acceso a Trade Federation.
Si la compilación no se realiza correctamente, consulta Configuración de la máquina para asegurarte de no haber omitido ningún paso.
Crea una configuración (I)
Para que las pruebas de Trade Federation sean ejecutables, se debe crear una configuración, un archivo en formato XML que le indica a tradefed qué pruebas ejecutar, así como qué otros módulos ejecutar y en qué orden.
Crear una nueva configuración para nuestro HelloWorldTest (ten en cuenta el nombre completo de la clase de HelloWorldTest):
<configuration description="Runs the hello world test"> <test class="com.android.tradefed.example.HelloWorldTest" /> </configuration>
Guarda estos datos en un archivo helloworld.xml
en cualquier lugar de tu sistema de archivos local (p.ej., /tmp/helloworld.xml
). TF analizará el archivo en formato XML de configuración (también conocido como config), cargará la clase especificada con reflexión, creará una instancia, la convertirá en un IRemoteTest
y llamará a su método run
.
Ejecuta la configuración (R)
Desde la shell, inicia la consola de tradefed:
tradefed.sh
Asegúrate de que un dispositivo esté conectado a la máquina host y sea visible para tradefed:
tf> list devices Serial State Product Variant Build Battery 004ad9880810a548 Available mako mako JDQ39 100
Las configuraciones se pueden ejecutar con el comando de consola run <config>
. Prueba lo siguiente:
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!
Deberías ver el resultado “Hello, TF World!” en la terminal.
Para confirmar que un comando se ejecutó, usa list invocations
o l i
en la instrucción de la consola. No debería imprimir nada. Si los comandos se están ejecutando, se muestran de la siguiente manera:
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}'
Agrega la configuración a la ruta de clase (D, I, R)
Para facilitar la implementación, también puedes agrupar las configuraciones en los archivos JAR de tradefed. Tradefed reconoce automáticamente todas las configuraciones que se colocan en las carpetas config en la ruta de acceso a clases.
A modo de ejemplo, mueve el archivo helloworld.xml
a la biblioteca principal de tradefed (<tree>/tools/tradefederation/core/res/config/example/helloworld.xml
). Vuelve a compilar tradefed, reinicia la consola de tradefed y, luego, pídele a tradefed que muestre la lista de configuraciones de la ruta de clase:
tf> list configs […] example/helloworld: Runs the hello world test
Ahora puedes ejecutar la configuración de helloworld con lo siguiente:
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!
Interactuar con un dispositivo (D, R)
Hasta el momento, nuestro HelloWorldTest no hace nada interesante. La especialidad de Tradefed es ejecutar pruebas con dispositivos Android, así que agreguemos un dispositivo Android a la prueba.
Las pruebas pueden obtener una referencia a un dispositivo Android con TestInformation
, que proporciona el framework cuando se llama al método IRemoteTest#run
.
Modifiquemos el mensaje de impresión de HelloWorldTest para mostrar el número de serie del dispositivo:
@Override public void run(TestInformation testInfo, ITestInvocationListener listener) throws DeviceNotAvailableException { CLog.i("Hello, TF World! I have device " + testInfo.getDevice().getSerialNumber()); }
Ahora vuelve a compilar tradefed y revisa la lista de dispositivos:
tradefed.sh
tf> list devices Serial State Product Variant Build Battery 004ad9880810a548 Available mako mako JDQ39 100
Anota el número de serie que aparece como Disponible, que es el dispositivo que se debe asignar 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
Deberías ver el nuevo mensaje de impresión que muestra el número de serie del dispositivo.
Enviar los resultados de la prueba (D)
IRemoteTest
informa los resultados llamando a métodos en la instancia de ITestInvocationListener proporcionada al método #run
. El framework de TF es responsable de informar el inicio (a través de ITestInvocationListener#invocationStarted) y el final (a través de ITestInvocationListener#invocationEnded) de cada invocación.
Una ejecución de prueba es una colección lógica de pruebas. Para informar los resultados de las pruebas, IRemoteTest
es responsable de informar el inicio de una ejecución de prueba, el inicio y el final de cada prueba, y el final de la ejecución de prueba.
A continuación, se muestra cómo podría verse la implementación de HelloWorldTest con un solo resultado de prueba fallido.
@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 incluye varias implementaciones de IRemoteTest
que puedes volver a usar en lugar de escribir las tuyas desde cero. Por ejemplo, InstrumentationTest puede ejecutar las pruebas de una aplicación para Android de forma remota en un dispositivo Android, analizar los resultados y reenviar esos resultados a ITestInvocationListener
). Para obtener más información, consulta Tipos de pruebas.
Almacena los resultados de las pruebas (I)
La implementación predeterminada del objeto de escucha de pruebas para una configuración de TF es TextResultReporter, que vuelca los resultados de una invocación a stdout. A modo de ejemplo, ejecuta la configuración de HelloWorldTest de la sección 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 almacenar los resultados de una invocación en otro lugar, como en un archivo, especifica una implementación ITestInvocationListener
personalizada con la etiqueta result_reporter
en tu configuración.
TF también incluye el objeto de escucha XmlResultReporter, que escribe los resultados de las pruebas en un archivo en formato XML similar al que usa el escritor de XML de JUnit ant. Para especificar el generador de informes de resultados en la configuración, edita la configuración de …/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>
Ahora, vuelve a compilar tradefed y vuelve a ejecutar la muestra de 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
Observa el mensaje de registro que indica que se generó un archivo en formato XML. El archivo generado debería verse de la siguiente manera:
<?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>
También puedes escribir tus propios objetos de escucha de invocación personalizados, que solo deben implementar la interfaz ITestInvocationListener.
Tradefed admite varios objetos de escucha de invocación, por lo que puedes enviar los resultados de las pruebas a varios destinos independientes. Para ello, simplemente especifica varias etiquetas <result_reporter>
en tu configuración.
Instalaciones de registro (D, I, R)
Las funciones de registro de TF incluyen la capacidad de hacer lo siguiente:
- Captura registros del dispositivo (también conocido como logcat del dispositivo)
- Registrar registros del framework de Trade Federation que se ejecuta en la máquina host (también conocido como registro del host)
El framework de TF captura automáticamente el logcat del dispositivo asignado y lo envía al objeto de escucha de invocación para su procesamiento.
Luego, XmlResultReporter
guarda el logcat del dispositivo capturado como un archivo.
Los registros del host de TF se informan con el wrapper de CLog para la clase de registro de ddmlib. Convirtamos la llamada System.out.println
anterior en HelloWorldTest a una llamada CLog
:
@Override public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { CLog.i("Hello, TF World! I have device %s", getDevice().getSerialNumber());
CLog
controla la interpolación de cadenas directamente, de manera similar a String.format
. Cuando vuelvas a compilar y ejecutar TF, deberías ver el mensaje de registro en stdout:
tf> run example/helloworld … 05-16 21:30:46 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548 …
De forma predeterminada, tradefed envía los mensajes de registro del host a stdout. TF también incluye una implementación de registro que escribe
mensajes en un archivo:
FileLogger.
Para agregar el registro de archivos, agrega una etiqueta logger
a la configuración y especifica el nombre de clase completo 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>
Ahora, vuelve a compilar y ejecutar el ejemplo de 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 …
El mensaje de registro indica la ruta de acceso del registro del host, que, cuando se ve, debería contener el mensaje de registro de HelloWorldTest:
more /tmp/0/inv_6390011618174565918/host_log_4255420317120216614.txt
Resultado de ejemplo:
… 05-16 21:38:21 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548
Opciones de manejo (D, I, R)
Los objetos cargados desde una configuración de TF (también conocidos como objetos de configuración)
también pueden recibir datos de argumentos de línea de comandos a través del uso de la
anotaciones @Option
.
Para participar, una clase de objeto de configuración aplica la anotación @Option
a un campo de miembro y le proporciona un nombre único. Esto permite que el valor del campo de miembro se propague a través de una opción de línea de comandos (y también agrega automáticamente esa opción al sistema de ayuda de configuración).
Nota: No se admiten todos los tipos de campos. Para obtener una descripción de los tipos admitidos, consulta OptionSetter.
Agreguemos 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";
A continuación, agreguemos un mensaje de registro para mostrar el valor de la opción en HelloWorldTest, de modo que podamos demostrar que se recibió correctamente:
@Override public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { … CLog.logAndDisplay(LogLevel.INFO, "I received option '%s'", mMyOption);
Por último, vuelve a compilar TF y ejecuta helloworld. Deberías ver un mensaje de registro con el valor predeterminado de my_option
:
tf> run example/helloworld … 05-24 18:30:05 I/HelloWorldTest: I received option 'thisisthedefault'
Pasa valores desde la línea de comandos
Pasa un valor para my_option
. Deberías ver
my_option
propagado con ese valor:
tf> run example/helloworld --my_option foo … 05-24 18:33:44 I/HelloWorldTest: I received option 'foo'
Las configuraciones de TF también incluyen un sistema de ayuda, que muestra automáticamente
texto de ayuda para los campos @Option
. Pruébalo ahora y deberías ver el texto de ayuda de 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.
Ten en cuenta el mensaje sobre "imprimir solo las opciones importantes". Para reducir la confusión de ayuda de opciones, TF usa el atributo Option#importance
para determinar si se debe mostrar un texto de ayuda de campo @Option
en particular cuando se especifica --help
. --help-all
siempre muestra ayuda para todos los campos @Option
, independientemente de la importancia. Para obtener más información, consulta Option.Importance.
Pasa valores de una configuración
También puedes especificar un valor de Option dentro de la configuración si agregas un elemento <option name="" value="">
. Pruébala con helloworld.xml
:
<test class="com.android.tradefed.example.HelloWorldTest" > <option name="my_option" value="fromxml" /> </test>
Si vuelves a compilar y ejecutar helloworld, deberías obtener este resultado:
05-24 20:38:25 I/HelloWorldTest: I received option 'fromxml'
La ayuda de configuración también debe actualizarse para indicar el valor predeterminado de my_option
:
tf> run example/helloworld --help test options: -m, --my_option this is the option's help text Default: fromxml.
Otros objetos de configuración incluidos en la configuración de helloworld, como FileLogger
, también aceptan opciones. La opción --log-level-display
es interesante porque filtra los registros que aparecen en stdout. Antes en el instructivo, es posible que hayas notado el mensaje "Hello, TF
World! El mensaje de registro "Tengo el dispositivo…" dejó de mostrarse en stdout después de que cambiamos a FileLogger
. Puedes aumentar la verbosidad de la
registración en stdout pasando el argumento --log-level-display
.
Pruébalo ahora y deberías ver que el mensaje de registro "I have device" vuelve a aparecer en stdout, además de registrarse en un archivo:
tf> run example/helloworld --log-level-display info … 05-24 18:53:50 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548
¡Eso es todo, amigos!
Recuerda que, si tienes algún problema, el código fuente de Trade Federation tiene mucha información útil que no se expone en la documentación. Si todo lo demás falla, intenta hacer la pregunta en el Grupo de Google android-platform con "Trade Federation" en el asunto del mensaje.