本教學將引導您建立「hello world」Trade Federation(Tradefed 或 TF)測試配置,並為您提供 TF 框架的實際操作介紹。從開發環境開始,您將建立簡單的配置並新增功能。
本教學以一組練習的形式介紹了測試開發流程,每個練習都包含幾個步驟,示範如何建立並逐步完善您的配置。提供了完成測試配置所需的所有範例程式碼,並且每個練習的標題都用描述該步驟中涉及的角色的字母註釋:
- D代表開發者
- I代表積分器
- R代表測試運行者
完成本教學後,您將擁有一個可正常運行的 TF 配置,並了解 TF 框架中的許多重要概念。
成立貿易聯盟
有關設定 TF 開發環境的詳細信息,請參閱機器設定。本教學的其餘部分假設您開啟了一個已初始化為 TF 環境的 shell。
為簡單起見,本教學課程示範了將組態及其類別新增至 TF 框架核心庫。這可以擴展到在原始碼樹之外開發模組,方法是編譯 tradefed 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
並從 shell 重建 tradefed:
m -jN
請注意,上例中的CLog.i
用於將輸出定向到控制台。有關登入 Trade Federation 的更多信息,請參閱日誌記錄 (D、I、R) 。
如果建置未成功,請諮詢機器設定以確保您沒有錯過任何步驟。
建立配置(一)
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)
從您的 shell 啟動 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)
為了方便部署,您也可以將設定捆綁到 tradefed 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 裝置新增至測試。
測試可以使用TestInformation
取得 Android 裝置的引用,該引用由框架在呼叫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
透過呼叫提供給#run
方法的ITestInitationListener實例上的方法來報告結果。 TF 框架本身負責報告每個呼叫的開始(透過ITestInvocableListener#invocateStarted )和結束(透過ITestInitationListener#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
)。有關詳細信息,請參閱測試類型。
儲存測試結果(一)
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偵聽器,它將測試結果寫入 XML 文件,其格式類似於ant JUnit 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>
您也可以編寫自己的自訂呼叫偵聽器 - 它們只需要實作ITestInvocableListener介面即可。
Tradefed 支援多個呼叫偵聽器,因此您可以將測試結果傳送至多個獨立的目的地。為此,只需在配置中指定多個<result_reporter>
標記即可。
測井設施(D、I、R)
TF 的日誌記錄設施具有以下能力:
- 從設備捕獲日誌(又稱設備 logcat)
- 記錄主機上執行的 Trade Federation 框架的日誌(也稱為主機日誌)
TF框架會自動從已指派的裝置中擷取logcat並將其傳送至呼叫監聽器進行處理。然後, XmlResultReporter
將捕獲的裝置 logcat 儲存為檔案。
TF 主機日誌使用 ddmlib Log 類別的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 時,您應該在標準輸出上看到日誌訊息:
tf> run example/helloworld … 05-16 21:30:46 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548 …
預設情況下,tradefed將主機日誌訊息輸出到 stdout 。 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 。
讓我們在 HelloWorldTest 中加入一個@Option
:
@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
很有趣,因為它過濾標準輸出上顯示的日誌。在本教學的前面,您可能已經注意到,在我們切換到使用FileLogger
後,「您好,TF World!我有裝置...」日誌訊息不再顯示在 stdout 上。您可以透過傳入--log-level-display
來增加記錄到stdout 的詳細程度--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
就這樣,夥計們!
提醒一下,如果您遇到困難,貿易聯盟原始碼有很多文件中未公開的有用資訊。如果所有其他方法都失敗,請嘗試在Android 平台Google 群組上提問,並在郵件主題中註明「Trade Federation」。