Пример сквозного теста TF

Это руководство поможет вам создать тестовую конфигурацию «привет, мир» Торговой федерации (Tradefed или TF) и даст вам практическое введение в структуру TF. Начиная со среды разработки, вы создадите простую конфигурацию и добавите функции.

В руководстве процесс разработки тестов представлен как набор упражнений, каждое из которых состоит из нескольких шагов, демонстрирующих, как создавать и постепенно совершенствовать конфигурацию. Предоставляется весь пример кода, необходимый для завершения конфигурации теста, а заголовок каждого упражнения сопровождается буквой, описывающей роли, участвующие в этом шаге:

  • D для разработчика
  • Я за Интегратора
  • R для тестировщика

После завершения руководства вы получите функционирующую конфигурацию TF и ​​поймете многие важные концепции структуры TF.

Создать Торговую Федерацию

Подробности о настройке среды разработки TF см. в разделе Настройка машины . В оставшейся части этого руководства предполагается, что у вас открыта оболочка, инициализированная для среды TF.

Для простоты в этом руководстве показано добавление конфигурации и ее классов в базовую библиотеку платформы TF. Это можно распространить на разработку модулей вне дерева исходного кода, скомпилировав обменный JAR, а затем скомпилировав ваши модули с этим JAR.

Создайте тестовый класс (D)

Давайте создадим тест hello world, который просто выгружает сообщение на стандартный вывод. Тест с обменом обычно реализует интерфейс IRemoteTest . Вот реализация 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!");
    }
}

Сохраните этот пример кода в <tree>/tools/tradefederation/core/src/com/android/tradefed/example/HelloWorldTest.java и пересоберите tradefed из своей оболочки:

m -jN

Обратите внимание, что CLog.i в приведенном выше примере используется для направления вывода на консоль. Дополнительная информация о входе в Trade Federation описана в разделе «Ведение журнала» (D, I, R) .

Если сборка не удалась, обратитесь к настройке машины , чтобы убедиться, что вы не пропустили ни одного шага.

Создайте конфигурацию (I)

Тесты Trade Federation становятся исполняемыми путем создания Configuration — XML-файла, который указывает tradefed, какой тест (или тесты) запускать, а также какие другие модули выполнять и в каком порядке.

Давайте создадим новую конфигурацию для нашего HelloWorldTest (обратите внимание на полное имя класса HelloWorldTest):

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

Сохраните эти данные в файле helloworld.xml в любом месте вашей локальной файловой системы (например /tmp/helloworld.xml ). TF проанализирует XML-файл конфигурации (он же config ), загрузит указанный класс с помощью отражения, создаст его экземпляр, приведет к IRemoteTest и вызовет его метод run .

Запустите конфигурацию (R)

Из вашей оболочки запустите консоль tradefed:

tradefed.sh

Убедитесь, что устройство подключено к хост-компьютеру и видимо для tradefed:

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

Конфигурации можно выполнить с помощью консольной команды run <config> . Пытаться:

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!

Вы должны увидеть «Привет, TF World!» вывод на терминал.

Вы можете подтвердить, что команда выполнена, используя list invocations или li в командной строке консоли, и она не должна ничего печатать. Если команды в данный момент выполняются, они отображаются следующим образом:

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

Добавьте конфигурацию в путь к классам (D, I, R)

Для удобства развертывания вы также можете объединить конфигурации в сами передаваемые JAR-файлы. Tradefed автоматически распознает все конфигурации, помещенные в папки конфигурации в пути к классам.

Для иллюстрации переместите файл helloworld.xml в основную библиотеку tradefed ( <tree>/tools/tradefederation/core/res/config/example/helloworld.xml ). Пересоберите tradefed, перезапустите консоль tradefed, затем попросите tradefed отобразить список конфигураций из пути к классам:

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

Теперь вы можете запустить конфигурацию helloworld, используя:

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!

Взаимодействовать с устройством (D, R)

Пока что наш HelloWorldTest не делает ничего интересного. Компания Tradefed специализируется на проведении тестов с использованием устройств Android, поэтому давайте добавим в тест устройство Android.

Тесты могут получить ссылку на устройство Android с помощью TestInformation , предоставляемого платформой при вызове метода IRemoteTest#run .

Давайте изменим сообщение печати HelloWorldTest, чтобы оно отображало серийный номер устройства:

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

Теперь пересобираем tradefed и проверяем список устройств:

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

Обратите внимание на серийный номер, указанный как Доступный ; это устройство, которое должно быть выделено для 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

Вы должны увидеть новое печатное сообщение с серийным номером устройства.

Отправить результаты теста (D)

IRemoteTest сообщает о результатах, вызывая методы экземпляра ITestInvocateListener , предоставленные методу #run . Сама платформа TF отвечает за отчет о начале (через ITestInvocateListener#invocatedStarted ) и окончании (через ITestInvocateListener#invoctionEnded ) каждого вызова.

Тестовый запуск — это логический набор тестов. Чтобы сообщить о результатах тестирования, IRemoteTest отвечает за отчет о начале выполнения теста, начале и окончании каждого теста, а также об окончании выполнения теста.

Вот как может выглядеть реализация HelloWorldTest с одним неудачным результатом теста.

@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 включает в себя несколько реализаций IRemoteTest , которые вы можете использовать повторно вместо того, чтобы писать свои собственные с нуля. Например, InstrumentationTest может удаленно запускать тесты приложения Android на устройстве Android, анализировать результаты и пересылать их в ITestInvocationListener ). Подробности см. в разделе «Типы тестов» .

Сохраните результаты испытаний (I)

Реализация прослушивателя тестов по умолчанию для конфигурации TF — TextResultReporter , которая выводит результаты вызова на стандартный вывод. Для иллюстрации запустите конфигурацию HelloWorldTest из предыдущего раздела:

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

Чтобы сохранить результаты вызова в другом месте, например в файле, укажите пользовательскую реализацию ITestInvocationListener , используя тег result_reporter в вашей конфигурации.

TF также включает в себя прослушиватель XmlResultReporter , который записывает результаты теста в XML-файл в формате, аналогичном тому, который используется модулем записи XML ant JUnit. Чтобы указать result_reporter в конфигурации, отредактируйте конфиг …/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>

Теперь пересоберите tradefed и повторно запустите пример 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

Обратите внимание на сообщение журнала о том, что создан XML-файл; сгенерированный файл должен выглядеть так:

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

Вы также можете написать свои собственные прослушиватели вызовов — им просто нужно реализовать интерфейс ITestInvocateListener .

Tradefed поддерживает несколько прослушивателей вызовов, поэтому вы можете отправлять результаты тестов в несколько независимых мест назначения. Для этого просто укажите в конфигурации несколько тегов <result_reporter> .

Лесозаготовительные устройства (D, I, R)

Средства регистрации TF включают в себя возможность:

  1. Захват журналов с устройства (также известный как logcat устройства)
  2. Записывайте журналы из инфраструктуры Trade Federation, работающей на хост-компьютере (также известный как журнал хоста).

Платформа TF автоматически захватывает logcat с выделенного устройства и отправляет его прослушивателю вызовов для обработки. Затем XmlResultReporter сохраняет записанный лог-код устройства в виде файла.

Журналы хоста TF передаются с использованием оболочки CLog для класса журнала ddmlib. Давайте преобразуем предыдущий вызов System.out.println в HelloWorldTest в вызов CLog :

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

CLog напрямую обрабатывает интерполяцию строк, аналогично String.format . Когда вы пересобираете и перезапускаете TF, вы должны увидеть сообщение журнала на стандартном выводе:

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

По умолчанию tradefed выводит сообщения журнала хоста на стандартный вывод . TF также включает реализацию журнала, записывающую сообщения в файл: FileLogger . Чтобы добавить ведение журнала файлов, добавьте в конфигурацию тег logger , указав полное имя класса 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>

Теперь пересоберите и снова запустите пример 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
…

Сообщение журнала указывает путь к журналу хоста, который при просмотре должен содержать сообщение журнала HelloWorldTest:

more /tmp/0/inv_6390011618174565918/host_log_4255420317120216614.txt

Пример вывода:

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

Варианты обработки (D, I, R)

Объекты, загруженные из конфигурации TF (также известные как объекты конфигурации ), также могут получать данные из аргументов командной строки с помощью аннотации @Option .

Для участия класс объекта конфигурации применяет аннотацию @Option к полю-члену и присваивает ему уникальное имя. Это позволяет заполнить значение этого поля элемента с помощью параметра командной строки (а также автоматически добавляет этот параметр в справочную систему конфигурации).

Примечание. Поддерживаются не все типы полей. Описание поддерживаемых типов см. в разделе OptionSetter .

Давайте добавим @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";

Далее давайте добавим сообщение журнала для отображения значения параметра в HelloWorldTest, чтобы мы могли продемонстрировать, что оно было получено правильно:

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

Наконец, пересоберите TF и ​​запустите helloworld; вы должны увидеть сообщение журнала со значением my_option по умолчанию:

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

Передача значений из командной строки

Передайте значение для my_option ; вы должны увидеть my_option , заполненный этим значением:

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

Конфигурации TF также включают справочную систему, которая автоматически отображает текст справки для полей @Option . Попробуйте сейчас, и вы должны увидеть текст справки для 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.

Обратите внимание на сообщение о «печати только важных параметров». Чтобы уменьшить беспорядок в справке по опциям, TF использует атрибут Option#importance , чтобы определить, следует ли отображать конкретный текст справки по полю @Option , когда указан --help . --help-all всегда показывает справку для всех полей @Option , независимо от их важности. Дополнительные сведения см. в разделе Option.Importance .

Передача значений из конфигурации

Вы также можете указать значение Option в конфигурации, добавив элемент <option name="" value=""> . Проверьте это, используя helloworld.xml :

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

Пересборка и запуск helloworld теперь должны привести к следующему результату:

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

Справка по конфигурации также должна обновиться, чтобы указать значение my_option по умолчанию:

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

Другие объекты конфигурации, включенные в конфигурацию helloworld, такие как FileLogger , также принимают параметры. Опция --log-level-display интересна тем, что она фильтрует журналы, отображаемые на стандартном выводе. Ранее в этом руководстве вы, возможно, заметили, что сообщение журнала «Hello, TF World! У меня есть устройство…» перестало отображаться на стандартном выводе после того, как мы переключились на использование FileLogger . Вы можете повысить степень детализации журнала на стандартный вывод, передав --log-level-display arg.

Попробуйте это сейчас, и вы увидите, что сообщение журнала «У меня есть устройство» снова появится на стандартном выводе, а также будет записано в файл:

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

Вот и все, ребята!

Напоминаем: если вы на чем-то застряли, исходный код Торговой федерации содержит много полезной информации, не представленной в документации. Если ничего не помогает, попробуйте задать вопрос в группе Google на платформе Android , указав в теме сообщения «Торговая федерация».