Exemple de test TF de bout en bout

Restez organisé à l'aide des collections Enregistrez et classez les contenus selon vos préférences.

Ce didacticiel vous guide dans la création d'une configuration de test "hello world" Trade Federation (TF) et vous donne une introduction pratique au framework TF. À partir d'un environnement de développement, vous créerez une configuration simple et ajouterez des fonctionnalités.

Le didacticiel présente le processus de développement de test sous la forme d'un ensemble d'exercices, chacun composé de plusieurs étapes, qui montrent comment créer et affiner progressivement votre configuration. Tous les exemples de code dont vous avez besoin pour terminer la configuration du test sont fournis, et le titre de chaque exercice est annoté d'une lettre décrivant les rôles impliqués dans cette étape :

  • D pour Développeur
  • I pour Intégrateur
  • R pour testeur

Après avoir terminé le didacticiel, vous disposerez d'une configuration TF fonctionnelle et comprendrez de nombreux concepts importants du framework TF.

Création d'une fédération professionnelle

Pour plus de détails sur la configuration de l'environnement de développement TF, voir Configuration de la machine . Le reste de ce didacticiel suppose que vous avez ouvert un shell qui a été initialisé dans l'environnement TF.

Pour plus de simplicité, ce didacticiel illustre l'ajout d'une configuration et de ses classes à la bibliothèque principale du framework TF. Cela peut être étendu au développement de modules en dehors de l'arborescence des sources en compilant le JAR tradefed, puis en compilant vos modules avec ce JAR.

Création d'une classe de test (D)

Créons un test hello world qui envoie simplement un message à stdout. Un test tradefed implémente généralement l'interface IRemoteTest . Voici une implémentation pour le 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!");
    }
}

Enregistrez cet exemple de code dans <tree>/tools/tradefederation/core/src/com/android/tradefed/example/HelloWorldTest.java et reconstruisez tradefed depuis votre shell :

m -jN

Notez que CLog.i dans l'exemple ci-dessus est utilisé pour diriger la sortie vers la console. Plus d'informations sur la connexion à Trade Federation sont décrites dans Logging (D, I, R) .

Si la construction ne réussit pas, consultez Machine Setup pour vous assurer que vous n'avez pas manqué une étape.

Créer une configuration (I)

Les tests Trade Federation sont rendus exécutables en créant une Configuration , un fichier XML qui indique à tradefed quel test (ou tests) exécuter, ainsi que les autres modules à exécuter et dans quel ordre.

Créons une nouvelle configuration pour notre HelloWorldTest (notez le nom complet de la classe de HelloWorldTest):

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

Enregistrez ces données dans un fichier helloworld.xml n'importe où sur votre système de fichiers local (par exemple /tmp/helloworld.xml ). TF analysera le fichier XML de configuration (alias config ), chargera la classe spécifiée à l'aide de la réflexion, l'instanciera, la convertira en un IRemoteTest et appellera sa méthode d' run .

Exécution de la configuration (R)

Depuis votre shell, lancez la console tradefed :

tradefed.sh

Assurez-vous qu'un appareil est connecté à la machine hôte et qu'il est visible par tradefed :

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

Les configurations peuvent être exécutées à l'aide de la commande run <config> console. Essayer:

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!

Vous devriez voir "Hello, TF World!" sortie sur le terminal.

Vous pouvez confirmer qu'une commande est en cours d'exécution en utilisant list invocations ou li dans l'invite de la console, et elle ne devrait rien imprimer. Si des commandes sont en cours d'exécution, elles s'affichent comme suit :

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}'

Ajout de la configuration au classpath (D, I, R)

Pour faciliter le déploiement, vous pouvez également regrouper les configurations dans les JAR tradefed eux-mêmes. Tradefed reconnaît automatiquement toutes les configurations placées dans les dossiers de configuration sur le classpath.

Pour illustrer, déplacez le fichier helloworld.xml dans la bibliothèque principale de tradefed ( <tree>/tools/tradefederation/core/res/config/example/helloworld.xml ). Reconstruisez tradefed, redémarrez la console tradefed, puis demandez à tradefed d'afficher la liste des configurations du classpath :

tf> list configs
[…]
example/helloworld: Runs the hello world test

Vous pouvez maintenant exécuter la configuration helloworld en utilisant :

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 avec un appareil (D, R)

Jusqu'à présent, notre HelloWorldTest ne fait rien d'intéressant. La spécialité de Tradefed est d'exécuter des tests à l'aide d'appareils Android, alors ajoutons un appareil Android au test.

Les tests peuvent obtenir une référence à un appareil Android en utilisant TestInformation , fourni par le framework lorsque la méthode IRemoteTest#run est appelée.

Modifions le message d'impression HelloWorldTest pour afficher le numéro de série de l'appareil :

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

Reconstruisez maintenant tradefed et vérifiez la liste des appareils :

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

Prenez note du numéro de série indiqué comme Disponible ; c'est l'appareil qui doit être attribué à 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

Vous devriez voir le nouveau message d'impression affichant le numéro de série de l'appareil.

Envoi des résultats des tests (D)

IRemoteTest rapporte les résultats en appelant des méthodes sur l'instance ITestInvocationListener fournie à la méthode #run . Le framework TF lui-même est chargé de signaler le début (via ITestInvocationListener#invocationStarted ) et la fin (via ITestInvocationListener#invocationEnded ) de chaque Invocation.

Une exécution de test est une collection logique de tests. Pour signaler les résultats des tests, IRemoteTest est chargé de signaler le début d'une exécution de test, le début et la fin de chaque test et la fin de l'exécution de test.

Voici à quoi pourrait ressembler l'implémentation de HelloWorldTest avec un seul résultat de test échoué.

@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 inclut plusieurs implémentations IRemoteTest que vous pouvez réutiliser au lieu d'écrire la vôtre à partir de zéro. Par exemple, InstrumentationTest peut exécuter les tests d'une application Android à distance sur un appareil Android, analyser les résultats et transmettre ces résultats à ITestInvocationListener ). Pour plus de détails, voir Types de tests .

Stockage des résultats de test (I)

L'implémentation de l'écouteur de test par défaut pour une configuration TF est TextResultReporter , qui vide les résultats d'un appel vers stdout. Pour illustrer, exécutez la configuration HelloWorldTest de la section précédente :

./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

Pour stocker les résultats d'un appel ailleurs, comme dans un fichier, spécifiez une implémentation ITestInvocationListener personnalisée à l'aide de la balise result_reporter dans votre configuration.

TF inclut également l'écouteur XmlResultReporter , qui écrit les résultats des tests dans un fichier XML dans un format similaire à celui utilisé par le rédacteur XML ant JUnit. Pour spécifier le result_reporter dans la configuration, modifiez la configuration …/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>

Reconstruisez maintenant tradefed et relancez l'exemple 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

Notez le message de journal indiquant qu'un fichier XML a été généré ; le fichier généré devrait ressembler à ceci :

<?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>

Vous pouvez également écrire vos propres écouteurs d'invocation personnalisés. Ils doivent simplement implémenter l'interface ITestInvocationListener .

Tradefed prend en charge plusieurs écouteurs d'invocation, vous pouvez donc envoyer les résultats des tests à plusieurs destinations indépendantes. Pour ce faire, spécifiez simplement plusieurs balises <result_reporter> dans votre configuration.

Journalisation (D, I, R)

Les installations d'exploitation forestière de TF incluent la capacité de :

  1. Capturer les journaux de l'appareil (également appelé logcat de l'appareil)
  2. Enregistrez les journaux du cadre de la fédération commerciale en cours d'exécution sur la machine hôte (également appelé journal de l'hôte)

L'infrastructure TF capture automatiquement le logcat du périphérique alloué et l'envoie à l'écouteur d'invocation pour traitement. XmlResultReporter enregistre ensuite le logcat du périphérique capturé sous forme de fichier.

Les journaux de l'hôte TF sont signalés à l'aide de l' encapsuleur CLog pour la classe ddmlib Log. Convertissons l'appel System.out.println précédent dans HelloWorldTest en un appel CLog :

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

CLog gère directement l'interpolation de chaîne, similaire à String.format . Lorsque vous reconstruisez et réexécutez TF, vous devriez voir le message de journal sur stdout :

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

Par défaut, tradefed envoie les messages du journal de l'hôte à stdout . TF inclut également une implémentation de journal qui écrit des messages dans un fichier : FileLogger . Pour ajouter la journalisation des fichiers, ajoutez une balise logger à la configuration, en spécifiant le nom de classe complet 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>

Maintenant, reconstruisez et exécutez à nouveau l'exemple 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
…

Le message de journal indique le chemin du journal de l'hôte, qui, lorsqu'il est affiché, doit contenir votre message de journal HelloWorldTest :

more /tmp/0/inv_6390011618174565918/host_log_4255420317120216614.txt

Exemple de sortie :

…
05-16 21:38:21 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548

Options de manipulation (D, I, R)

Les objets chargés à partir d'une configuration TF (également appelés objets de configuration ) peuvent également recevoir des données à partir d'arguments de ligne de commande via l'utilisation de l'annotation @Option .

Pour participer, une classe d'objets Configuration applique l'annotation @Option à un champ membre et lui attribue un nom unique. Cela permet à cette valeur de champ de membre d'être renseignée via une option de ligne de commande (et ajoute également automatiquement cette option au système d'aide à la configuration).

Remarque : Tous les types de champs ne sont pas pris en charge. Pour une description des types pris en charge, voir OptionSetter .

Ajoutons une @Option à 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";

Ensuite, ajoutons un message de journal pour afficher la valeur de l'option dans HelloWorldTest afin que nous puissions démontrer qu'elle a été correctement reçue :

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

Enfin, reconstruisez TF et exécutez helloworld ; vous devriez voir un message de journal avec la valeur par défaut de my_option :

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

Passer des valeurs depuis la ligne de commande

Passez une valeur pour my_option ; vous devriez voir my_option avec cette valeur :

tf> run example/helloworld --my_option foo
…
05-24 18:33:44 I/HelloWorldTest: I received option 'foo'

Les configurations TF incluent également un système d'aide, qui affiche automatiquement le texte d'aide pour les champs @Option . Essayez-le maintenant et vous devriez voir le texte d'aide pour 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.

Notez le message concernant "imprimer uniquement les options importantes". Pour réduire l'encombrement de l'aide sur les options, TF utilise l'attribut Option#importance pour déterminer s'il faut afficher un texte d'aide de champ @Option particulier lorsque --help est spécifié. --help-all affiche toujours l'aide pour tous les champs @Option , quelle que soit leur importance. Pour plus de détails, voir Option.Importance .

Passer des valeurs d'une configuration

Vous pouvez également spécifier une valeur Option dans la configuration en ajoutant un élément <option name="" value=""> . Testez-le avec helloworld.xml :

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

La reconstruction et l'exécution de helloworld devraient maintenant produire cette sortie :

05-24 20:38:25 I/HelloWorldTest: I received option 'fromxml'

L'aide à la configuration devrait également se mettre à jour pour indiquer la valeur par défaut de my_option :

tf> run example/helloworld --help
  test options:
    -m, --my_option      this is the option's help text Default: fromxml.

D'autres objets de configuration inclus dans la configuration helloworld, tels que FileLogger , acceptent également les options. L'option --log-level-display est intéressante car elle filtre les logs qui s'affichent sur stdout. Plus tôt dans le didacticiel, vous avez peut-être remarqué que le message de journal "Hello, TF World! J'ai un appareil …" a cessé de s'afficher sur stdout après que nous sommes passés à l'utilisation de FileLogger . Vous pouvez augmenter la verbosité de la journalisation sur stdout en passant le --log-level-display arg.

Essayez ceci maintenant, et vous devriez voir le message de journal "J'ai un appareil" réapparaître sur stdout, en plus d'être connecté à un fichier :

tf> run example/helloworld --log-level-display info
…
05-24 18:53:50 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548

C'est tout, les amis !

Pour rappel, si vous êtes bloqué sur quelque chose, le code source de la Trade Federation contient de nombreuses informations utiles qui ne sont pas exposées dans la documentation. Si tout le reste échoue, essayez de demander sur le groupe Google de la plate-forme Android, avec "Trade Federation" dans l'objet du message.