Home | History | Annotate | Download | only in tradefed
      1 <html devsite>
      2   <head>
      3     <title>End-to-End Test Example</title>
      4     <meta name="project_path" value="/_project.yaml" />
      5     <meta name="book_path" value="/_book.yaml" />
      6   </head>
      7   <body>
      8   <!--
      9       Copyright 2017 The Android Open Source Project
     10 
     11       Licensed under the Apache License, Version 2.0 (the "License");
     12       you may not use this file except in compliance with the License.
     13       You may obtain a copy of the License at
     14 
     15           http://www.apache.org/licenses/LICENSE-2.0
     16 
     17       Unless required by applicable law or agreed to in writing, software
     18       distributed under the License is distributed on an "AS IS" BASIS,
     19       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     20       See the License for the specific language governing permissions and
     21       limitations under the License.
     22   -->
     23 
     24 
     25 
     26 <p>This tutorial guides you through creating a "hello world" Trade Federation
     27 (TF) test configuration and gives you a hands-on introduction to the TF
     28 framework. Starting from a development environment, you will create a simple
     29 configuration and add features.</p>
     30 
     31 <p>The tutorial presents the test development process as a set of exercises,
     32 each consisting of several steps, that demonstrate how to build and gradually
     33 refine your configuration. All sample code you need to complete the test
     34 configuration is provided, and the title of each exercise is annotated with a
     35 letter describing the roles involved in that step:</p>
     36 <ul>
     37 <li><strong>D</strong> for Developer</li>
     38 <li><strong>I</strong> for Integrator</li>
     39 <li><strong>R</strong> for Test Runner</li>
     40 </ul>
     41 
     42 <p>After completing the tutorial, you will have a functioning TF configuration
     43 and understand many important concepts in the TF framework.</p>
     44 
     45 <h2 id="setup">Setting up the Trade Federation development environment</h2>
     46 <p>For details on seting up the TF development environment, see
     47 <a href="/devices/tech/test_infra/tradefed/fundamentals/machine_setup.html">Machine
     48 Setup</a>. The rest of this tutorial assumes you have a shell open that has been
     49 initialized to the TF environment.</p>
     50 
     51 <p>For simplicity, this tutorial illustrates adding a configuration and its
     52 classes to the TF framework core library. This can be extended to developing
     53 modules outside the source tree by compiling the tradefed JAR, then compiling
     54 your modules against that JAR.</p>
     55 
     56 <h2 id="testclass">Creating a test class (D)</h2>
     57 <p>Lets create a hello world test that just dumps a message to stdout. A
     58 tradefed test generally implements the
     59 <a href="/reference/com/android/tradefed/testtype/IRemoteTest.html">IRemoteTest</a>
     60 interface. Here's an implementation for the HelloWorldTest:</p>
     61 <pre class="prettyprint">
     62 package com.android.tradefed.example;
     63 
     64 import com.android.tradefed.device.DeviceNotAvailableException;
     65 import com.android.tradefed.result.ITestInvocationListener;
     66 import com.android.tradefed.testtype.IRemoteTest;
     67 
     68 public class HelloWorldTest implements IRemoteTest {
     69     &#64;Override
     70     public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
     71         System.out.println("Hello, TF World!");
     72     }
     73 }
     74 </pre>
     75 
     76 <p>Save this sample code to
     77 <code>&lt;tree&gt;/tools/tradefederation/core/prod-tests/src/com/android/tradefed/example/HelloWorldTest.java</code>
     78 and rebuild tradefed from your shell:</p>
     79 <pre class="devsite-terminal devsite-click-to-copy">
     80 m -jN
     81 </pre>
     82 
     83 <p>Note that <code>System.out</code> in the example above may not actually
     84 direct output to the console. While this is acceptable for this test example,
     85 you should establish logging in Trade Federation as described in <a
     86 href="#logging">Logging (D, I, R)</a>.</p>
     87 
     88 <p>If the build does not succeed, consult
     89 <a href="/devices/tech/test_infra/tradefed/fundamentals/machine_setup.html">Machine
     90 Setup</a> to ensure you didn't miss a step.</p>
     91 
     92 <h2 id="createconfig">Creating a Configuration (I)</h2>
     93 <p>Trade Federation tests are made executable by creating a
     94 <strong>Configuration</strong>, an XML file that instructs tradefed on which
     95 test (or tests) to run, as well as which other modules to execute and in what
     96 order.</p>
     97 
     98 <p>Lets create a new Configuration for our HelloWorldTest (note the full class
     99 name of the HelloWorldTest):</p>
    100 <pre class="prettyprint">
    101 &lt;configuration description="Runs the hello world test"&gt;
    102     &lt;test class="com.android.tradefed.example.HelloWorldTest" /&gt;
    103 &lt;/configuration&gt;</pre>
    104 
    105 <p>Save this data to a <code>helloworld.xml</code> file anywhere on your local
    106 filesystem (e.g. <code>/tmp/helloworld.xml</code>). TF will parse the
    107 Configuration XML file (aka <b>config</b>), load the specified class using
    108 reflection, instantiate it, cast it to a <code>IRemoteTest</code>, and call its
    109 <code>run</code> method.</p>
    110 
    111 <h2 id="runconfig">Running the config (R)</h2>
    112 <p>From your shell, launch the tradefed console:</p>
    113 <pre class="devsite-terminal devsite-click-to-copy">
    114 tradefed.sh
    115 </pre>
    116 
    117 <p>Ensure a device is connected to the host machine and is visible to tradefed:</p>
    118 <pre class="devsite-click-to-copy">
    119 tf&gt; list devices
    120 Serial            State      Product   Variant   Build   Battery
    121 004ad9880810a548  Available  mako      mako      JDQ39   100
    122 </pre>
    123 
    124 <p>Configurations can be executed using the <code>run &lt;config&gt;</code>
    125 console command. Try:</p>
    126 <pre class="devsite-click-to-copy">
    127 tf&gt; run /tmp/helloworld.xml
    128 05-12 13:19:36 I/TestInvocation: Starting invocation for target stub on build 0 on device 004ad9880810a548
    129 Hello, TF World!
    130 </pre>
    131 <p>You should see "Hello, TF World!" output on the terminal.</p>
    132 
    133 <h2 id="addconfig">Adding the config to the Classpath (D, I, R)</h2>
    134 <p>For convenience of deployment, you can also bundle configs into the tradefed
    135 JARs themselves. Tradefed automatically recognizes all configurations placed in
    136 <em>config</em> folders on the classpath.</p>
    137 
    138 <p>To illustrate, move the <code>helloworld.xml</code> file into the tradefed
    139 core library
    140 (<code>&lt;tree&gt;/tools/tradefederation/core/prod-tests/res/config/example/helloworld.xml</code>).
    141 Rebuild tradefed, restart the tradefed console, then ask tradefed to display the
    142 list of configurations from the classpath:</p>
    143 <pre class="devsite-click-to-copy">
    144 tf&gt; list configs
    145 []
    146 example/helloworld: Runs the hello world test
    147 </pre>
    148 
    149 <p>You can now run the helloworld config using:</p>
    150 <pre class="devsite-click-to-copy">
    151 tf&gt; run example/helloworld
    152 05-12 13:21:21 I/TestInvocation: Starting invocation for target stub on build 0 on device 004ad9880810a548
    153 Hello, TF World!
    154 </pre>
    155 
    156 <h2 id="deviceinteract">Interacting with a device (D, R)</h2>
    157 <p>So far, our HelloWorldTest isn't doing anything interesting. Tradefed's
    158 specialty is running tests using Android devices, so lets add an Android device
    159 to the test.</p>
    160 
    161 <p>Tests can get a reference to an Android device by implementing the
    162 <a href="/reference/com/android/tradefed/testtype/IDeviceTest.html">IDeviceTest</a>
    163 interface. Here's a sample implementation of what this looks like:</p>
    164 <pre class="prettyprint">
    165 public class HelloWorldTest implements IRemoteTest, IDeviceTest {
    166     private ITestDevice mDevice;
    167     &#64;Override
    168     public void setDevice(ITestDevice device) {
    169         mDevice = device;
    170     }
    171 
    172     &#64;Override
    173     public ITestDevice getDevice() {
    174         return mDevice;
    175     }
    176 
    177 }
    178 </pre>
    179 
    180 <p>The Trade Federation framework will inject the <code>ITestDevice</code>
    181 reference into your test via the <code>IDeviceTest#setDevice</code> method,
    182 before the <code>IRemoteTest#run</code> method is called.</p>
    183 
    184 <p>Let's modify the HelloWorldTest print message to display the serial number of
    185 the device:</p>
    186 <pre class="prettyprint">
    187 &#64;Override
    188 public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
    189     System.out.println("Hello, TF World! I have device " + getDevice().getSerialNumber());
    190 }
    191 </pre>
    192 
    193 <p>Now rebuild tradefed and check the list of devices:</p>
    194 <pre class="devsite-terminal devsite-click-to-copy">
    195 tradefed.sh
    196 </pre>
    197 <pre class="devsite-click-to-copy">
    198 tf&gt; list devices
    199 Serial            State      Product   Variant   Build   Battery
    200 004ad9880810a548  Available  mako      mako      JDQ39   100
    201 </pre>
    202 
    203 <p>Take note of the serial number listed as <strong>Available</strong>; that is
    204 the device that should be allocated to HelloWorld:</p>
    205 <pre class="devsite-click-to-copy">
    206 tf&gt; run example/helloworld
    207 05-12 13:26:18 I/TestInvocation: Starting invocation for target stub on build 0 on device 004ad9880810a548
    208 Hello, TF World! I have device 004ad9880810a548
    209 </pre>
    210 
    211 <p>You should see the new print message displaying the serial number of the
    212 device.</p>
    213 
    214 <h2 id="sendresults">Sending test results (D)</h2>
    215 <p><code>IRemoteTest</code> reports results by calling methods on the
    216 <a href="/reference/com/android/tradefed/result/ITestInvocationListener.html">ITestInvocationListener</a>
    217 instance provided to the <code>#run</code> method. The TF framework itself is
    218 responsible for reporting the start (via
    219 <a href="/reference/com/android/tradefed/result/ITestInvocationListener.html#invocationStarted(com.android.tradefed.build.IBuildInfo)">ITestInvocationListener#invocationStarted</a>)
    220 and end (via
    221 <a href="/reference/com/android/tradefed/result/ITestInvocationListener.html#invocationEnded(long)"
    222 >ITestInvocationListener#invocationEnded</a>)
    223 of each Invocation.</p>
    224 
    225 <p>A <b>test run</b> is a logical collection of tests. To report test results,
    226 <code>IRemoteTest</code> is responsible for reporting the start of a test run,
    227 the start and end of each test, and the end of the test run.</p>
    228 
    229 <p>Here's what the HelloWorldTest implementation might look like with a single
    230 failed test result.</p>
    231 <pre class="prettyprint">
    232 &#64;Override
    233 public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
    234     System.out.println("Hello, TF World! I have device " + getDevice().getSerialNumber());
    235 
    236     TestIdentifier testId = new TestIdentifier("com.example.TestClassName", "sampleTest");
    237     listener.testRunStarted("helloworldrun", 1);
    238     listener.testStarted(testId);
    239     listener.testFailed(testId, "oh noes, test failed");
    240     listener.testEnded(testId, Collections.emptyMap());
    241     listener.testRunEnded(0, Collections.emptyMap());
    242 }
    243 </pre>
    244 
    245 <p>TF includes several <code>IRemoteTest</code> implementations you can reuse
    246 instead of writing your own from scratch. For example,
    247 <a href="/reference/com/android/tradefed/testtype/InstrumentationTest.html">InstrumentationTest</a>
    248 can run an Android application's tests remotely on an Android device, parse the
    249 results, and forward those results to the <code>ITestInvocationListener</code>).
    250 For details, see
    251 <a href="/reference/com/android/tradefed/testtype/package-summary.html">Test
    252 Types</a>.</p>
    253 
    254 <h2 id="storeresults">Storing test results (I)</h2>
    255 <p>The default test listener implementation for a TF config is
    256 <a href="/reference/com/android/tradefed/result/TextResultReporter.html">TextResultReporter</a>,
    257 which dumps the results of an invocation to stdout. To illustrate, run the
    258 HelloWorldTest config from the previous section:</p>
    259 <pre class="devsite-terminal devsite-click-to-copy">
    260 ./tradefed.sh
    261 </pre>
    262 <pre class="devsite-click-to-copy">
    263 tf&gt; run example/helloworld
    264 05-16 20:03:15 I/TestInvocation: Starting invocation for target stub on build 0 on device 004ad9880810a548
    265 Hello, TF World! I have device 004ad9880810a548
    266 05-16 20:03:15 I/InvocationToJUnitResultForwarder: run helloworldrun started: 1 tests
    267 Test FAILURE: com.example.TestClassName#sampleTest
    268  stack: oh noes, test failed
    269 05-16 20:03:15 I/InvocationToJUnitResultForwarder: run ended 0 ms
    270 </pre>
    271 
    272 <p>To store the results of an invocation elsewhere, such as in a file, specify a
    273 custom <code>ITestInvocationListener</code> implementation using the
    274 <code>result_reporter</code> tag in your configuration.</p>
    275 
    276 <p>TF also includes the
    277 <a href="/reference/com/android/tradefed/result/XmlResultReporter.html">XmlResultReporter</a>
    278 listener, which writes test results to an XML file in a format similar to that
    279 used by the <em>ant</em> JUnit XML writer. To specify the result_reporter in the
    280 configuration, edit the <code>/res/config/example/helloworld.xml</code>
    281 config:</p>
    282 <pre class="prettyprint">
    283 &lt;configuration description="Runs the hello world test"&gt;
    284     &lt;test class="com.android.tradefed.example.HelloWorldTest" /&gt;
    285     &lt;result_reporter class="com.android.tradefed.result.XmlResultReporter" /&gt;
    286 &lt;/configuration&gt;
    287 </pre>
    288 
    289 <p>Now rebuild tradefed and re-run the hello world sample:</p>
    290 <pre class="devsite-click-to-copy">
    291 tf&gt; run example/helloworld
    292 05-16 21:07:07 I/TestInvocation: Starting invocation for target stub on build 0 on device 004ad9880810a548
    293 Hello, TF World! I have device 004ad9880810a548
    294 05-16 21:07:07 I/XmlResultReporter: Saved device_logcat log to /tmp/0/inv_2991649128735283633/device_logcat_6999997036887173857.txt
    295 05-16 21:07:07 I/XmlResultReporter: Saved host_log log to /tmp/0/inv_2991649128735283633/host_log_6307746032218561704.txt
    296 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
    297 </pre>
    298 
    299 <p>Notice the log message stating that an XML file has been generated; the
    300 generated file should look like this:</p>
    301 <pre class="prettyprint">
    302 &lt;?xml version='1.0' encoding='UTF-8' ?&gt;
    303 &lt;testsuite name="stub" tests="1" failures="1" errors="0" time="9" timestamp="2011-05-17T04:07:07" hostname="localhost"&gt;
    304   &lt;properties /&gt;
    305   &lt;testcase name="sampleTest" classname="com.example.TestClassName" time="0"&gt;
    306     &lt;failure&gt;oh noes, test failed
    307     &lt;/failure&gt;
    308   &lt;/testcase&gt;
    309 &lt;/testsuite&gt;
    310 </pre>
    311 
    312 <p>You can also write your own custom invocation listeners&mdash;they simply
    313 need to implement the
    314 <a href="/reference/com/android/tradefed/result/ITestInvocationListener.html">ITestInvocationListener</a>
    315 interface.</p>
    316 
    317 <p>Tradefed supports multiple invocation listeners, so you can send test results
    318 to multiple independent destinations. To do this, just specify multiple
    319 <code>&lt;result_reporter&gt;</code> tags in your config.</p>
    320 
    321 <h2 id="logging">Logging (D, I, R)</h2>
    322 <p>TF's logging facilities include the ability to:</p>
    323 <ol>
    324 <li>Capture logs from the device (aka device logcat)</li>
    325 <li>Record logs from the TradeFederation framework running on the host machine
    326 (aka host log)</li>
    327 </ol>
    328 
    329 <p>The TF framework automatically captures the logcat from the allocated device
    330 and sends it to the invocation listener for processing.
    331 <code>XmlResultReporter</code> then saves the captured device logcat as a file.
    332 </p>
    333 
    334 <p>TF host logs are reported using the
    335 <a href="/reference/com/android/tradefed/log/LogUtil.CLog.html">CLog wrapper</a>
    336 for the ddmlib Log class. Let's convert the
    337 previous <code>System.out.println</code> call in HelloWorldTest to a
    338 <code>CLog</code> call:</p>
    339 <pre class="prettyprint">
    340 &#64;Override
    341 public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
    342     CLog.i("Hello, TF World! I have device %s", getDevice().getSerialNumber());
    343 </pre>
    344 
    345 <p><code>CLog</code> handles string interpolation directly, similar to
    346 <code>String.format</code>. When you rebuild and rerun TF, you should see the
    347 log message on stdout:</p>
    348 <pre class="devsite-click-to-copy">
    349 tf&gt; run example/helloworld
    350 
    351 05-16 21:30:46 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548
    352 
    353 </pre>
    354 
    355 <p>By default, tradefed
    356 <a href"/reference/com/android/tradefed/log/StdoutLogger.html">outputs host log
    357 messages to stdout</a>. TF also includes a log implementation that writes
    358 messages to a file:
    359 <a href="/reference/com/android/tradefed/log/FileLogger.html">FileLogger</a>.
    360 To add file logging, add a <code>logger</code> tag to the config, specifying the
    361 full class name of <code>FileLogger</code>:</p>
    362 <pre class="prettyprint">
    363 &lt;configuration description="Runs the hello world test"&gt;
    364     &lt;test class="com.android.tradefed.example.HelloWorldTest" /&gt;
    365     &lt;result_reporter class="com.android.tradefed.result.XmlResultReporter" /&gt;
    366     &lt;logger class="com.android.tradefed.log.FileLogger" /&gt;
    367 &lt;/configuration&gt;
    368 </pre>
    369 
    370 <p>Now, rebuild and run the helloworld example again:</p>
    371 <pre class="devsite-click-to-copy">
    372 tf &gt;run example/helloworld
    373 
    374 05-16 21:38:21 I/XmlResultReporter: Saved device_logcat log to /tmp/0/inv_6390011618174565918/device_logcat_1302097394309452308.txt
    375 05-16 21:38:21 I/XmlResultReporter: Saved host_log log to /tmp/0/inv_6390011618174565918/host_log_4255420317120216614.txt
    376 
    377 </pre>
    378 <p>The log message indicates the path of the host log, which, when viewed,
    379 should contain your HelloWorldTest log message:</p>
    380 <pre class="devsite-terminal devsite-click-to-copy">
    381 more /tmp/0/inv_6390011618174565918/host_log_4255420317120216614.txt</pre>
    382 <p>Example output:</p>
    383 <pre class="devsite-click-to-copy">
    384 
    385 05-16 21:38:21 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548
    386 </pre>
    387 
    388 <h2 id="optionhandling">Handling options (D, I, R)</h2>
    389 <p>Objects loaded from a TF Configuration (aka <b>Configuration objects</b>)
    390 can also receive data from command line arguments through the use of the
    391 <code>@Option</code> annotation.<p>
    392 
    393 <p>To participate, a Configuration object class applies the <code>@Option</code>
    394 annotation to a member field and provides it a unique name. This enables that
    395 member field value to be populated via a command line option (and also
    396 automatically adds that option to the configuration help system).</p>
    397 
    398 <p class="note"><strong>Note:</strong> Not all field types are supported. For a
    399 description of supported types, see
    400 <a href="/reference/com/android/tradefed/config/OptionSetter.html">OptionSetter</a>.
    401 </p>
    402 
    403 <p>Let's add an <code>@Option</code> to HelloWorldTest:</p>
    404 <pre class="prettyprint">
    405 @Option(name="my_option",
    406         shortName='m',
    407         description="this is the option's help text",
    408         // always display this option in the default help text
    409         importance=Importance.ALWAYS)
    410 private String mMyOption = "thisisthedefault";
    411 </pre>
    412 
    413 <p>Next, let's add a log message to display the value of the option in
    414 HelloWorldTest so we can demonstrate it was received correctly:</p>
    415 <pre class="prettyprint">
    416 &#64;Override
    417 public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
    418     
    419     CLog.logAndDisplay(LogLevel.INFO, "I received option '%s'", mMyOption);
    420 </pre>
    421 
    422 <p>Finally, rebuild TF and run helloworld; you should see a log message with the
    423 <code>my_option</code> default value:</p>
    424 <pre class="devsite-click-to-copy">
    425 tf&gt; run example/helloworld
    426 
    427 05-24 18:30:05 I/HelloWorldTest: I received option 'thisisthedefault'
    428 </pre>
    429 
    430 <h3 id="passclivalues">Passing values from the command line</h3>
    431 <p>Pass in a value for <code>my_option</code>; you should see
    432 <code>my_option</code> populated with that value:</p>
    433 <pre class="devsite-click-to-copy">
    434 tf&gt; run example/helloworld --my_option foo
    435 
    436 05-24 18:33:44 I/HelloWorldTest: I received option 'foo'
    437 </pre>
    438 
    439 <p>TF configurations also include a help system, which automatically displays
    440 help text for <code>@Option</code> fields. Try it now, and you should see the
    441 help text for <code>my_option</code>:</p>
    442 <pre class="devsite-click-to-copy">
    443 tf&gt; run example/helloworld --help
    444 Printing help for only the important options. To see help for all options, use the --help-all flag
    445 
    446   cmd_options options:
    447     --[no-]help          display the help text for the most important/critical options. Default: false.
    448     --[no-]help-all      display the full help text for all options. Default: false.
    449     --[no-]loop          keep running continuously. Default: false.
    450 
    451   test options:
    452     -m, --my_option      this is the option's help text Default: thisisthedefault.
    453 
    454   'file' logger options:
    455     --log-level-display  the minimum log level to display on stdout. Must be one of verbose, debug, info, warn, error, assert. Default: error.
    456 </pre>
    457 
    458 <p>Note the message about "printing only the important options." To reduce
    459 option help clutter, TF uses the <code>Option#importance</code> attribute to
    460 determine whether to show a particular <code>@Option</code> field help text when
    461 <code>--help</code> is specified. <code>--help-all</code> always shows help for
    462 all <code>@Option</code> fields, regardless of importance. For details, see
    463 <a href="/reference/com/android/tradefed/config/Option.Importance.html">Option.Importance</a>.
    464 </p>
    465 
    466 <h3 id="passconfvalues">Passing values from a configuration</h3>
    467 <p>You can also specify an Option value within the config by adding a
    468 <code>&lt;option name="" value=""&gt;</code> element. Test it using
    469 <code>helloworld.xml</code>:</p>
    470 <pre class="prettyprint">
    471 &lt;test class="com.android.tradefed.example.HelloWorldTest" &gt;
    472     &lt;option name="my_option" value="fromxml" /&gt;
    473 &lt;/test&gt;
    474 </pre>
    475 
    476 <p>Re-building and running helloworld should now produce this output:</p>
    477 <pre class="devsite-click-to-copy">
    478 05-24 20:38:25 I/HelloWorldTest: I received option 'fromxml'
    479 </pre>
    480 
    481 <p>The configuration help should also update to indicate the default value of
    482 <code>my_option</code>:</p>
    483 <pre class="devsite-click-to-copy">
    484 tf&gt; run example/helloworld --help
    485   test options:
    486     -m, --my_option      this is the option's help text Default: fromxml.
    487 </pre>
    488 
    489 <p>Other configuration objects included in the helloworld config, such as
    490 <code>FileLogger</code>, also accept options. The option
    491 <code>--log-level-display</code> is interesting because it filters the logs that
    492 show up on stdout. Earlier in the tutorial, you may have noticed the "Hello, TF
    493 World! I have device ' log message stopped being displayed on stdout after we
    494 switched to using <code>FileLogger</code>. You can increase the verbosity of
    495 logging to stdout by passing in the <code>--log-level-display</code> arg.</p>
    496 
    497 <p>Try this now, and you should see the 'I have device' log message reappear on
    498 stdout, in addition to being logged to a file:</p>
    499 <pre class="devsite-click-to-copy">
    500 tf&gt; run example/helloworld --log-level-display info
    501 
    502 05-24 18:53:50 I/HelloWorldTest: Hello, TF World! I have device 004ad9880810a548
    503 </pre>
    504 
    505 <h2 id="conclusion">That's all, folks!</h2>
    506 <p>As a reminder, if you're stuck on something, the
    507 <a href="https://android.googlesource.com/platform/tools/tradefederation/+/master">Trade
    508 Federation source code</a> has a lot of useful information that isn't exposed in
    509 the documentation. If all else fails, try asking on the
    510 <a href="/source/community.html">android-platform</a> Google Group,
    511 with "Trade Federation" in the message subject.</p>
    512 
    513   </body>
    514 </html>
    515