PageRenderTime 27ms CodeModel.GetById 11ms RepoModel.GetById 1ms app.codeStats 0ms

/docs/source.android.com/src/tech/test_infra/tradefed/tutorial.md

https://gitlab.com/brian0218/rk3066_r-box_android4.2.2_sdk
Markdown | 424 lines | 294 code | 130 blank | 0 comment | 0 complexity | cad78487a7ce8705d0b4867415f70977 MD5 | raw file
  1. <!--
  2. Copyright 2012 The Android Open Source Project
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. -->
  13. # Tutorial
  14. This tutorial guides you through the construction of a "hello world" Trade Federation test
  15. configuration, and gives you a hands-on introduction to the Trade Federation framework. Starting
  16. from the Tf development environment, it guides you through the process of creating a simple Trade
  17. Federation config and gradually adding more features to it.
  18. The tutorial presents the TF test development process as a set of exercises, each consisting of
  19. several steps. The exercises demonstrate how to gradually build and refine your configuration, and
  20. provide all the sample code you need to complete the test configuration.
  21. When you are finished with the tutorial, you will have created a functioning TF configuration and
  22. will have learned many of the most important concepts in the TF framework.
  23. ## Set up TradeFederation development environment
  24. See (FIXME: link) for how to setup the development environment. The rest of this tutorial assumes you have a shell open that has been initialized to the TradeFederation environment.
  25. For simplicity, this tutorial will illustrate adding a configuration and its classes to the TradeFederation framework core library. Later tutorials/documentation will show how to create your own library that extends TradeFederation.
  26. ## Creating a test class
  27. Lets create a hello world test that just dumps a message to stdout. A TradeFederation test must
  28. implement the (FIXME: link) IRemoteTest interface.
  29. Here's an implementation for the HelloWorldTest:
  30. package com.android.tradefed.example;
  31. import com.android.tradefed.device.DeviceNotAvailableException;
  32. import com.android.tradefed.result.ITestInvocationListener;
  33. import com.android.tradefed.testtype.IRemoteTest;
  34. public class HelloWorldTest implements IRemoteTest {
  35. @Override
  36. public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
  37. System.out.println("Hello, TF World!");
  38. }
  39. }
  40. FIXME: prod-tests
  41. Save this sample code to
  42. `<git home>/tools/tradefederation/prod-tests/src/com/android/tradefed/example/HelloWorldTest.java`
  43. and rebuild tradefed from your shell:
  44. m -j6
  45. If the build does not succeed, please consult the (FIXME: link)Development Environment page to
  46. ensure you did not miss any steps.
  47. ## Creating a configuration
  48. Trade Federation tests are defined in a "Configuration". A Configuration is an XML file that
  49. instructs tradefed which test (or set of tests) to run.
  50. Lets create a new Configuration for our HelloWorldTest.
  51. <configuration description="Runs the hello world test">
  52. <test class="com.android.tradefed.example.HelloWorldTest" />
  53. </configuration>
  54. TF will parse the Configuration XML file, load the specified class using reflection, instantiate it,
  55. cast it to a IRemoteTest, and call its 'run' method.
  56. Note that we've specified the full class name of the HelloWorldTest. Save this data to a
  57. `helloworld.xml` file anywhere on your local filesystem (eg `/tmp/helloworld.xml`).
  58. ## Running the configuration
  59. From your shell, launch the tradefed console
  60. $ ./tradefed.sh
  61. Ensure a device is connected to the host machine that is visible to tradefed
  62. tf> list devices
  63. Configurations can be run using the `run <config>` console command. Try this now
  64. FIXME: redo this
  65. tf> run /tmp/helloworld.xml
  66. 05-12 13:19:36 I/TestInvocation: Starting invocation for target stub on build 0 on device 30315E38655500EC
  67. Hello, TF World!
  68. You should see "Hello, TF World!" outputted on the terminal.
  69. ## Adding the configuration to the classpath
  70. FIXME: prod-tests
  71. For convenience of deployment, you can also bundle configuration files into the TradeFederation jars
  72. themselves. Tradefed will automatically recognize all configurations placed in 'config' folders on
  73. the classpath.
  74. Lets illustrate this now by moving the helloworld.xml into the tradefed core library.
  75. Move the `helloworld.xml` file into
  76. `<git root>/tools/tradefederation/prod-tests/res/config/example/helloworld.xml`.
  77. Rebuild tradefed, and restart the tradefed console.
  78. Ask tradefed to display the list of configurations on the classpath:
  79. tf> list configs
  80. []
  81. example/helloworld: Runs the hello world test
  82. You can now run the helloworld config via the following command
  83. tf >run example/helloworld
  84. 05-12 13:21:21 I/TestInvocation: Starting invocation for target stub on build 0 on device 30315E38655500EC
  85. Hello, TF World!
  86. ## Interacting with a device
  87. So far our hello world test isn't doing anything interesting. Tradefed is intended to run tests using Android devices, so lets add an Android device to the test.
  88. Tests can get a reference to an Android device by implementing the IDeviceTest interface.
  89. Here's a sample implementation of what this looks like:
  90. public class HelloWorldTest implements IRemoteTest, IDeviceTest {
  91. private ITestDevice mDevice;
  92. @Override
  93. public void setDevice(ITestDevice device) {
  94. mDevice = device;
  95. }
  96. @Override
  97. public ITestDevice getDevice() {
  98. return mDevice;
  99. }
  100. }
  101. The TradeFederation framework will inject the ITestDevice reference into your test via the
  102. IDeviceTest#setDevice method, before the IRemoteTest#run method is called.
  103. Lets add an additional print message to the HelloWorldTest displaying the serial number of the
  104. device.
  105. @Override
  106. public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
  107. System.out.println("Hello, TF World! I have a device " + getDevice().getSerialNumber());
  108. }
  109. Now rebuild tradefed, and do (FIXME: update)
  110. $ tradefed.sh
  111. tf> list devices
  112. Available devices: [30315E38655500EC]
  113. Take note of the serial number listed in Available devices above. That is the device that should be allocated to HelloWorld.
  114. tf >run example/helloworld
  115. 05-12 13:26:18 I/TestInvocation: Starting invocation for target stub on build 0 on device 30315E38655500EC
  116. Hello world, TF! I have a device 30315E38655500EC
  117. You should see the new print message displaying the serial number of the device.
  118. ## Sending test results
  119. IRemoteTests report results by calling methods on the ITestInvocationListener instance provided to
  120. their `#run` method.
  121. The TradeFederation framework is responsible for reporting the start and end of an Invocation (via
  122. the ITestInvocationListener#invocationStarted and ITestInvocationListener#invocationEnded methods
  123. respectively).
  124. A `test run` is a logical collection of tests. To report test results, IRemoteTests are responsible
  125. for reporting the start of a test run, the start and end of each test, and the end of the test run.
  126. Here's what the HelloWorldTest implementation looks like with a single failed test result.
  127. @SuppressWarnings("unchecked")
  128. @Override
  129. public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
  130. System.out.println("Hello, TF World! I have a device " + getDevice().getSerialNumber());
  131. TestIdentifier testId = new TestIdentifier("com.example.MyTestClassName", "sampleTest");
  132. listener.testRunStarted("helloworldrun", 1);
  133. listener.testStarted(testId);
  134. listener.testFailed(TestFailure.FAILURE, testId, "oh noes, test failed");
  135. listener.testEnded(testId, Collections.EMPTY_MAP);
  136. listener.testRunEnded(0, Collections.EMPTY_MAP);
  137. }
  138. Note that TradeFederation also includes several IRemoteTest implementations that you can reuse
  139. instead of writing your own from scratch. (such as InstrumentationTest, which can run an Android
  140. application's tests remotely on an Android device, parse the results, and forward them to the
  141. ITestInvocationListener). See the Test Types documentation for more details.
  142. ## Storing test results
  143. By default, a TradeFederation configuration will use the TextResultReporter as the test listener
  144. implementation for the configuration. TextResultReporter will dump the results of an invocation to
  145. stdout. To illustrate, try running the hello-world config from previous section now:
  146. $ ./tradefed.sh
  147. tf >run example/helloworld
  148. 05-16 20:03:15 I/TestInvocation: Starting invocation for target stub on build 0 on device 30315E38655500EC
  149. Hello world, TF! I have a device 30315E38655500EC
  150. 05-16 20:03:15 I/InvocationToJUnitResultForwarder: run helloworldrun started: 1 tests
  151. Test FAILURE: com.example.MyTestClassName#sampleTest
  152. stack: oh noes, test failed
  153. 05-16 20:03:15 I/InvocationToJUnitResultForwarder: run ended 0 ms
  154. If you want to store the results of an invocation elsewhere, say to a file, you would need to
  155. specify a custom "result_reporter" in your configuration, that specifies the custom
  156. ITestInvocationListener class you want to use.
  157. The TradeFederation framework includes a result_reporter (XmlResultReporter) that will write test
  158. results to an XML file, in a format similar to the ant JUnit XML writer.
  159. Lets specify the result_reporter in the configuration now. Edit the
  160. `tools/tradefederation/res/config/example/helloworld.xml` like this:
  161. <configuration description="Runs the hello world test">
  162. <test class="com.android.tradefed.example.HelloWorldTest" />
  163. <result_reporter class="com.android.tradefed.result.XmlResultReporter" />
  164. </configuration>
  165. Now rebuild tradefed and re-run the hello world sample:
  166. FIXME: paths
  167. tf >run example/helloworld
  168. 05-16 21:07:07 I/TestInvocation: Starting invocation for target stub on build 0 on device 30315E38655500EC
  169. Hello world, TF! I have a device 30315E38655500EC
  170. 05-16 21:07:07 I/XmlResultReporter: Saved device_logcat log to /var/folders/++/++2Pz+++6+0++4RjPqRgNE+-4zk/-Tmp-/0/inv_2991649128735283633/device_logcat_6999997036887173857.txt
  171. 05-16 21:07:07 I/XmlResultReporter: Saved host_log log to /var/folders/++/++2Pz+++6+0++4RjPqRgNE+-4zk/-Tmp-/0/inv_2991649128735283633/host_log_6307746032218561704.txt
  172. 05-16 21:07:07 I/XmlResultReporter: XML test result file generated at /var/folders/++/++2Pz+++6+0++4RjPqRgNE+-4zk/-Tmp-/0/inv_2991649128735283633/test_result_536358148261684076.xml. Total tests 1, Failed 1, Error 0
  173. Notice the log message stating an XML file has been generated. The generated file should look like this:
  174. <?xml version='1.0' encoding='UTF-8' ?>
  175. <testsuite name="stub" tests="1" failures="1" errors="0" time="9" timestamp="2011-05-17T04:07:07" hostname="localhost">
  176. <properties />
  177. <testcase name="sampleTest" classname="com.example.MyTestClassName" time="0">
  178. <failure>oh noes, test failed
  179. </failure>
  180. </testcase>
  181. </testsuite>
  182. Note that you can write your own custom result_reporter. It just needs to implement the
  183. ITestInvocationListener interface.
  184. Also note that Tradefed supports multiple result_reporters, meaning that you can send test results
  185. to multiple independent destinations. Just specify multiple <result_reporter> tags in your config to
  186. do this.
  187. ## Logging
  188. TradeFederation includes two logging facilities:
  189. 1. ability to capture logs from the device (aka device logcat)
  190. 2. ability to record logs from the TradeFederation framework running on the host machine (aka the
  191. host log)
  192. Lets focus on 2 for now. Trade Federation's host logs are reported using the CLog wrapper for the
  193. ddmlib Log class.
  194. Lets convert the previous System.out.println call in HelloWorldTest to a CLog call:
  195. @Override
  196. public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
  197. CLog.i("Hello world, TF! I have a device " + getDevice().getSerialNumber());
  198. Now rebuild and rerun. You should see the log message on stdout.
  199. tf> run example/helloworld
  200. 05-16 21:30:46 I/HelloWorldTest: Hello world, TF! I have a device 30315E38655500EC
  201. By default, TradeFederation will output host log messages to stdout. TradeFederation also includes a
  202. log implementation that will write messages to a file: FileLogger. To add file logging, add a
  203. 'logger' tag to the configuration xml, specifying the full class name of FileLogger.
  204. <configuration description="Runs the hello world test">
  205. <test class="com.android.tradefed.example.HelloWorldTest" />
  206. <result_reporter class="com.android.tradefed.result.XmlResultReporter" />
  207. <logger class="com.android.tradefed.log.FileLogger" />
  208. </configuration>
  209. Now rebuild and run the helloworld example again.
  210. tf >run example/helloworld
  211. 05-16 21:38:21 I/XmlResultReporter: Saved device_logcat log to /var/folders/++/++2Pz+++6+0++4RjPqRgNE+-4zk/-Tmp-/0/inv_6390011618174565918/device_logcat_1302097394309452308.txt
  212. 05-16 21:38:21 I/XmlResultReporter: Saved host_log log to /tmp/0/inv_6390011618174565918/host_log_4255420317120216614.txt
  213. Note the log message indicating the path of the host log. View the contents of that file, and you
  214. should see your HelloWorldTest log message
  215. $ more /tmp/0/inv_6390011618174565918/host_log_4255420317120216614.txt
  216. 05-16 21:38:21 I/HelloWorldTest: Hello world, TF! I have a device 30315E38655500EC
  217. The TradeFederation framework will also automatically capture the logcat from the allocated device,
  218. and send it the the result_reporter for processing. XmlResultReporter will save the captured device
  219. logcat as a file.
  220. ## Command line options
  221. Objects loaded from a TradeFederation Configuration (aka "Configuration objects") also have the
  222. ability to receive data from command line arguments.
  223. This is accomplished via the `@Option` annotation. To participate, a Configuration object class
  224. would apply the `@Option` annotation to a member field, and provide it a unique name. This would
  225. allow that member field's value to be populated via a command line option, and would also
  226. automatically add that option to the configuration help system (Note: not all field types are
  227. supported: see the OptionSetter javadoc for a description of supported types).
  228. Lets add an Option to the HelloWorldTest.
  229. @Option(name="my_option",
  230. shortName='m',
  231. description="this is the option's help text",
  232. // always display this option in the default help text
  233. importance=Importance.ALWAYS)
  234. private String mMyOption = "thisisthedefault";
  235. And lets add a log message to display the value of the option in HelloWorldTest, so we can
  236. demonstrate that it was received correctly.
  237. @SuppressWarnings("unchecked")
  238. @Override
  239. public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
  240. Log.logAndDisplay(LogLevel.INFO, "HelloWorldTest", "I received this option " + mMyOption);
  241. Rebuild TF and run helloworld: you should see a log message with the my_option's default value.
  242. tf> run example/helloworld
  243. 05-24 18:30:05 I/HelloWorldTest: I received this option thisisthedefault
  244. Now pass in a value for my_option: you should see my_option getting populated with that value
  245. tf> run example/helloworld --my_option foo
  246. 05-24 18:33:44 I/HelloWorldTest: I received this option foo
  247. TF configurations also include a help system, which automatically displays help text for @Option
  248. fields. Try it now, and you should see the help text for 'my_option':
  249. tf> run --help example/helloworld
  250. Printing help for only the important options. To see help for all options, use the --help-all flag
  251. cmd_options options:
  252. --[no-]help display the help text for the most important/critical options. Default: false.
  253. --[no-]help-all display the full help text for all options. Default: false.
  254. --[no-]loop keep running continuously. Default: false.
  255. test options:
  256. -m, --my_option this is the option's help text Default: thisisthedefault.
  257. 'file' logger options:
  258. --log-level-display the minimum log level to display on stdout. Must be one of verbose, debug, info, warn, error, assert. Default: error.
  259. FIXME: redo with enum help
  260. Note the message at the top about 'printing only the important options'. To reduce option help
  261. clutter, TF uses the Option#importance attribute to determine whether to show an Option's help text
  262. when '--help' is specified. '--help-all' will always show all options' help regardless of
  263. importance. See Option.Importance javadoc for details.
  264. You can also specify an Option's value within the configuration xml by adding a
  265. `<option name="" value="">` element. Lets see how this looks in the helloworld.xml:
  266. <test class="com.android.tradefed.example.HelloWorldTest" >
  267. <option name="my_option" value="fromxml" />
  268. </test>
  269. Re-building and running helloworld should now produce this output:
  270. 05-24 20:38:25 I/HelloWorldTest: I received this option fromxml
  271. The configuration help should also be updated to indicate my_option's new default value:
  272. tf> run --help example/helloworld
  273. test options:
  274. -m, --my_option this is the option's help text Default: fromxml.
  275. Also note that other configuration objects included in the helloworld config, like FileLogger, also have options. '--log-level-display' is of interest because it filters the logs that show up on stdout. You may have noticed from earlier in the tutorial the 'Hello world, TF! I have a device ..' log message stopped getting displayed on stdout once we switched to using FileLogger. You can increase the verbosity of logging to stdout by passing in log-level-display arg.
  276. Try this now, and you should see the 'I have a device' log message reappear on stdout, in addition to getting logged to a file.
  277. tf >run --log-level-display info example/helloworld
  278. 05-24 18:53:50 I/HelloWorldTest: Hello world, TF! I have a device XXXXXX
  279. <!-- To make future debugging in this tutorial easier, edit the helloworld.xml to default log-level-display to debug:
  280. <logger class="com.android.tradefed.log.FileLogger" >
  281. <option name="log-level-display" value="debug" />
  282. </logger>
  283. -->