В этом учебном пособии вы узнаете, как создать тестовую конфигурацию Торговой федерации (TF) "hello world" и ознакомитесь с фреймворком TF. Начиная со среды разработки, вы создадите простую конфигурацию и добавите функции.
В учебнике процесс разработки тестов представлен в виде набора упражнений, каждое из которых состоит из нескольких шагов, которые демонстрируют, как создавать и постепенно улучшать конфигурацию. Предоставляется весь пример кода, необходимый для выполнения тестовой конфигурации, а название каждого упражнения снабжено аннотацией с буквой, описывающей роли, участвующие в этом шаге:
- D для разработчика
- я за интегратора
- R для запуска тестов
После прохождения руководства у вас будет работающая конфигурация TF, и вы поймете многие важные концепции фреймворка TF.
Создание торговой федерации
Подробнее о настройке среды разработки TF см. в разделе Настройка машины . В остальной части этого руководства предполагается, что у вас открыта оболочка, которая была инициализирована в среде TF.
Для простоты в этом руководстве показано добавление конфигурации и ее классов в базовую библиотеку платформы TF. Это может быть расширено для разработки модулей вне исходного дерева путем компиляции обменного JAR-файла, а затем компиляции ваших модулей для этого JAR-файла.
Создание тестового класса (D)
Давайте создадим тест hello world, который просто выводит сообщение на стандартный вывод. Тест tradefed обычно реализует интерфейс 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!
Вы должны увидеть "Hello, 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
Обратите внимание на серийный номер, указанный как Available ; это устройство, которое должно быть выделено для 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
сообщает о результатах, вызывая методы экземпляра ITestInvocationListener , предоставленного методу #run
. Сама структура TF отвечает за отчет о начале (через ITestInvocationListener#invocationStarted ) и конце (через ITestInvocationListener#invocationEnded ) каждого вызова.
Тестовый прогон — это логический набор тестов. Чтобы сообщить результаты теста, 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>
Вы также можете написать свои собственные прослушиватели вызовов — им просто нужно реализовать интерфейс ITestInvocationListener .
Tradefed поддерживает несколько прослушивателей вызовов, поэтому вы можете отправлять результаты тестирования нескольким независимым адресатам. Для этого достаточно указать в конфиге несколько тегов <result_reporter>
.
Ведение журнала (D, I, R)
Средства регистрации TF включают в себя возможность:
- Захват журналов с устройства (ака устройство logcat)
- Запись журналов из платформы Trade Federation, работающей на хост-компьютере (он же журнал хоста).
Инфраструктура TF автоматически захватывает logcat с выделенного устройства и отправляет его прослушивателю вызовов для обработки. XmlResultReporter
сохраняет захваченный logcat устройства в виде файла.
Журналы хоста TF создаются с помощью оболочки CLog для класса Log 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
.
Для участия класс объекта Configuration применяет аннотацию @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
интересен тем, что он фильтрует журналы, отображаемые на стандартном выводе. Ранее в руководстве вы, возможно, заметили, что сообщение журнала «Привет, TF World! У меня есть устройство…» перестало отображаться на стандартном выводе после того, как мы переключились на использование FileLogger
. Вы можете увеличить уровень детализации ведения журнала на стандартный вывод, передав --log-level-display
Аргумент --log-level-display
.
Попробуйте это сейчас, и вы должны увидеть, что сообщение журнала «У меня есть устройство» снова появится на стандартном выводе, в дополнение к записи в файл:
tf> run example/helloworld --log-level-display info … 05-24 18:53:50 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548
Вот и все, народ!
Напоминаю, если вы застряли на чем-то, исходный код Торговой федерации содержит много полезной информации, которой нет в документации. Если ничего не помогает, попробуйте задать вопрос в группе Google для платформы Android , указав «Trade Federation» в теме сообщения.