このチュートリアルでは、「hello world」の Trade Federation(Tradefed あるいは TF)テスト構成を作成し、TF フレームワークの実践的な説明を行います。開発環境から開始して、シンプルな構成を作成し、機能を追加します。
チュートリアルでは、テスト開発プロセスを一連の演習として示します。それぞれが複数のステップで構成されており、構成を作成して段階的に調整する方法を示します。テスト構成を完了するために必要なすべてのサンプルコードが提供され、各演習のタイトルには、そのステップに関係する役割を説明する文字が注記されています。
- D: 開発者
- I: インテグレータ
- R: テストランナー
チュートリアルを完了すると、TF 構成が機能するようになり、TF フレームワークの多くの重要な概念を理解できます。
Trade Federation を設定する
TF 開発環境の設定の詳細については、マシンのセットアップをご覧ください。このチュートリアルの残りの部分では、TF 環境で初期化されたシェルを開いていることを前提としています。
わかりやすくするために、このチュートリアルでは、構成とクラスを TF フレームワークのコアライブラリに追加する方法を説明します。これは、tradefed JAR をコンパイルしてから、その JAR に対してモジュールをコンパイルすることで、ソースツリーの外部でのモジュール開発に拡張できます。
テストクラスを作成する(D)
メッセージを stdout にダンプするだけの 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
に保存し、シェルから変換してリビルドします。
m -jN
上記の例の CLog.i
は、出力をコンソールに出力するために使用されます。Trade Federation のロギングの詳細については、ロギング(D、I、R)をご覧ください。
ビルドが成功しない場合は、マシンのセットアップを参照して、手順を見落としていないか確認します。
構成を作成する(I)
Trade Federation テストは、構成(どのテストを実行するか、また他のどのモジュールをどういった順番で実行するかを tradefed に指示する XML ファイル)を作成することで、実行可能ファイルを作成します。
HelloWorldTest の新しい構成を作成しましょう(HelloWorldTest の完全なクラス名に注意します)。
<configuration description="Runs the hello world test"> <test class="com.android.tradefed.example.HelloWorldTest" /> </configuration>
このデータをローカル ファイル システムの任意の場所(例: /tmp/helloworld.xml
)にある 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
または l i
を使用すると、コマンドの実行を確認できます。出力は出力されません。コマンドが実行中の場合は、次のように表示されます。
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)
デプロイの便宜上、構成を tradefed JAR 自体にバンドルすることもできます。Tradefed はクラスパスの config フォルダにあるすべての構成を自動的に認識します。
説明のため、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 デバイスをテストに追加しましょう。
テストは、IRemoteTest#run
メソッドが呼び出されたときにフレームワークによって提供される TestInformation
を使用して、Android デバイスへの参照を取得できます。
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
は、#run
メソッドに提供される ITestInvocationListener インスタンスのメソッドを呼び出すことで、結果を報告します。TF フレームワーク自体は、各呼び出しの開始(ITestInvocationListener#invocationStarted 経由)と終了(ITestInvocationListener#invocationEnded 経由)の報告を担当します。
テスト実行は、テストの論理的な集合です。テスト結果を報告するために、IRemoteTest
はテスト実行の開始、各テストの開始と終了、テスト実行の終了を報告します。
下記は、1 つの不合格のテスト結果によって 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 であり、呼び出しの結果を stdout にダンプします。説明のため、前のセクションの 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
呼び出しの結果を、ファイルなどの別の場所に格納するには、構成内で result_reporter
タグを使用して、カスタムの ITestInvocationListener
実装を指定します。
TF には XmlResultReporter リスナーも含まれており、テスト結果は、ant JUnit XML ライターが使用するものと同様の形式で XML ファイルに書き込まれます。構成で 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 ホストログは、ddmlib ログクラスの CLog ラッパーを使用して報告されます。HelloWorldTest の、前の System.out.println
呼び出しを CLog
呼び出しに変換しましょう。
@Override public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { CLog.i("Hello, TF World! I have device %s", getDevice().getSerialNumber());
CLog
は、String.format
と同様に、文字列補間を直接処理します。TF をリビルドして再実行すると、stdout にログメッセージが表示されます。
tf> run example/helloworld … 05-16 21:30:46 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548 …
デフォルトでは、tradefed はホスト ログメッセージを stdout に出力します。TF には、メッセージをファイルに書き込む FileLogger というログ実装も含まれています。ファイル ロギングを追加するには、FileLogger
の完全なクラス名を指定して、logger
タグを構成に追加します。
<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
属性を使用して、--help
が指定されている場合に特定の @Option
フィールド ヘルプテキストを表示するかどうかを判断します。--help-all
は、重要性に関係なく、常にすべての @Option
フィールドのヘルプを表示します。詳細については、Option.Importance をご覧ください。
構成から値を渡す
<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
は、stdout に表示されるログをフィルタするため、興味深いオプションです。チュートリアルの前半で、「Hello, TF World! I have device …」というログメッセージが、FileLogger
の使用に切り替えた後、表示されなくなったことに気付いたかもしれません。--log-level-display
引数を渡すことで、stdout へのロギングの冗長性を高めることができます。
試してみると、ファイルに記録されるだけでなく、「I have device」というログメッセージが stdout に再び表示されます。
tf> run example/helloworld --log-level-display info … 05-24 18:53:50 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548
これで終わりです
何かに行き詰まっているのであれば、念のため、Trade Federation ソースコードをご覧ください。ドキュメントでは公開されていない有用な情報がたくさんあります。それ以外の問題がある場合は、メッセージの件名に「Trade Federation」を入れて、Android プラットフォーム の Google グループに問い合わせてみてください。