blob: 03eddbf70a0b48fc1538fd891d27ed0f984f4d40 [file] [log] [blame]
The Android Open Source Project6ffae012009-03-18 17:39:43 -07001#!/usr/bin/python2.4
2#
3# Copyright 2008, The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""Command line utility for running a pre-defined test.
18
19Based on previous <androidroot>/development/tools/runtest shell script.
20"""
21
22# Python imports
23import glob
24import optparse
25import os
Niko Catania2e990b92009-04-02 16:52:26 -070026import re
The Android Open Source Project6ffae012009-03-18 17:39:43 -070027from sets import Set
28import sys
29
30# local imports
31import adb_interface
32import android_build
33import coverage
34import errors
35import logger
36import run_command
37import test_defs
38
39
40class TestRunner(object):
41 """Command line utility class for running pre-defined Android test(s)."""
42
Brett Chabotf61f43e2009-04-02 11:52:48 -070043 _TEST_FILE_NAME = "test_defs.xml"
44
The Android Open Source Project6ffae012009-03-18 17:39:43 -070045 # file path to android core platform tests, relative to android build root
46 # TODO move these test data files to another directory
Nicolas Catania97b24c42009-04-22 11:08:32 -070047 _CORE_TEST_PATH = os.path.join("development", "testrunner",
Brett Chabotf61f43e2009-04-02 11:52:48 -070048 _TEST_FILE_NAME)
The Android Open Source Project6ffae012009-03-18 17:39:43 -070049
50 # vendor glob file path patterns to tests, relative to android
51 # build root
52 _VENDOR_TEST_PATH = os.path.join("vendor", "*", "tests", "testinfo",
Brett Chabotf61f43e2009-04-02 11:52:48 -070053 _TEST_FILE_NAME)
The Android Open Source Project6ffae012009-03-18 17:39:43 -070054
55 _RUNTEST_USAGE = (
56 "usage: runtest.py [options] short-test-name[s]\n\n"
57 "The runtest script works in two ways. You can query it "
58 "for a list of tests, or you can launch one or more tests.")
59
Brett Chabot72731f32009-03-31 11:14:05 -070060 def __init__(self):
61 # disable logging of timestamp
Niko Catania2e990b92009-04-02 16:52:26 -070062 self._root_path = android_build.GetTop()
Nicolas Catania97b24c42009-04-22 11:08:32 -070063 logger.SetTimestampLogging(False)
Brett Chabot72731f32009-03-31 11:14:05 -070064
The Android Open Source Project6ffae012009-03-18 17:39:43 -070065 def _ProcessOptions(self):
66 """Processes command-line options."""
67 # TODO error messages on once-only or mutually-exclusive options.
68 user_test_default = os.path.join(os.environ.get("HOME"), ".android",
Brett Chabotf61f43e2009-04-02 11:52:48 -070069 self._TEST_FILE_NAME)
The Android Open Source Project6ffae012009-03-18 17:39:43 -070070
71 parser = optparse.OptionParser(usage=self._RUNTEST_USAGE)
72
73 parser.add_option("-l", "--list-tests", dest="only_list_tests",
74 default=False, action="store_true",
75 help="To view the list of tests")
76 parser.add_option("-b", "--skip-build", dest="skip_build", default=False,
77 action="store_true", help="Skip build - just launch")
78 parser.add_option("-n", "--skip_execute", dest="preview", default=False,
79 action="store_true",
80 help="Do not execute, just preview commands")
81 parser.add_option("-r", "--raw-mode", dest="raw_mode", default=False,
82 action="store_true",
83 help="Raw mode (for output to other tools)")
84 parser.add_option("-a", "--suite-assign", dest="suite_assign_mode",
85 default=False, action="store_true",
86 help="Suite assignment (for details & usage see "
87 "InstrumentationTestRunner)")
88 parser.add_option("-v", "--verbose", dest="verbose", default=False,
89 action="store_true",
90 help="Increase verbosity of %s" % sys.argv[0])
91 parser.add_option("-w", "--wait-for-debugger", dest="wait_for_debugger",
92 default=False, action="store_true",
93 help="Wait for debugger before launching tests")
94 parser.add_option("-c", "--test-class", dest="test_class",
95 help="Restrict test to a specific class")
96 parser.add_option("-m", "--test-method", dest="test_method",
97 help="Restrict test to a specific method")
Brett Chabot8a101cb2009-05-05 12:56:39 -070098 parser.add_option("-p", "--test-package", dest="test_package",
99 help="Restrict test to a specific java package")
100 parser.add_option("-z", "--size", dest="test_size",
101 help="Restrict test to a specific test size")
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700102 parser.add_option("-u", "--user-tests-file", dest="user_tests_file",
103 metavar="FILE", default=user_test_default,
104 help="Alternate source of user test definitions")
105 parser.add_option("-o", "--coverage", dest="coverage",
106 default=False, action="store_true",
107 help="Generate code coverage metrics for test(s)")
108 parser.add_option("-t", "--all-tests", dest="all_tests",
109 default=False, action="store_true",
110 help="Run all defined tests")
111 parser.add_option("--continuous", dest="continuous_tests",
112 default=False, action="store_true",
113 help="Run all tests defined as part of the continuous "
114 "test set")
Wei-Ta Chen97752d42009-05-21 16:24:04 -0700115 parser.add_option("--timeout", dest="timeout",
116 default=300, help="Set a timeout limit (in sec) for "
117 "running native tests on a device (default: 300 secs)")
Brett Chabot49b77112009-06-02 11:46:04 -0700118 parser.add_option("--cts", dest="cts_tests",
119 default=False, action="store_true",
120 help="Run all tests defined as part of the "
121 "compatibility test suite")
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700122 group = optparse.OptionGroup(
123 parser, "Targets", "Use these options to direct tests to a specific "
124 "Android target")
125 group.add_option("-e", "--emulator", dest="emulator", default=False,
126 action="store_true", help="use emulator")
127 group.add_option("-d", "--device", dest="device", default=False,
128 action="store_true", help="use device")
129 group.add_option("-s", "--serial", dest="serial",
130 help="use specific serial")
131 parser.add_option_group(group)
132
133 self._options, self._test_args = parser.parse_args()
134
Brett Chabot49b77112009-06-02 11:46:04 -0700135 if (not self._options.only_list_tests
136 and not self._options.all_tests
137 and not self._options.continuous_tests
138 and not self._options.cts_tests
139 and len(self._test_args) < 1):
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700140 parser.print_help()
141 logger.SilentLog("at least one test name must be specified")
142 raise errors.AbortError
143
144 self._adb = adb_interface.AdbInterface()
145 if self._options.emulator:
146 self._adb.SetEmulatorTarget()
147 elif self._options.device:
148 self._adb.SetDeviceTarget()
149 elif self._options.serial is not None:
150 self._adb.SetTargetSerial(self._options.serial)
151
152 if self._options.verbose:
153 logger.SetVerbose(True)
154
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700155 self._known_tests = self._ReadTests()
156
157 self._coverage_gen = coverage.CoverageGenerator(
158 android_root_path=self._root_path, adb_interface=self._adb)
159
160 def _ReadTests(self):
161 """Parses the set of test definition data.
162
163 Returns:
164 A TestDefinitions object that contains the set of parsed tests.
165 Raises:
166 AbortError: If a fatal error occurred when parsing the tests.
167 """
168 core_test_path = os.path.join(self._root_path, self._CORE_TEST_PATH)
169 try:
170 known_tests = test_defs.TestDefinitions()
171 known_tests.Parse(core_test_path)
Brett Chabot2d85c0e2009-03-31 15:19:13 -0700172 # read all <android root>/vendor/*/tests/testinfo/test_defs.xml paths
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700173 vendor_tests_pattern = os.path.join(self._root_path,
174 self._VENDOR_TEST_PATH)
175 test_file_paths = glob.glob(vendor_tests_pattern)
176 for test_file_path in test_file_paths:
177 known_tests.Parse(test_file_path)
178 if os.path.isfile(self._options.user_tests_file):
179 known_tests.Parse(self._options.user_tests_file)
180 return known_tests
181 except errors.ParseError:
182 raise errors.AbortError
183
184 def _DumpTests(self):
185 """Prints out set of defined tests."""
186 print "The following tests are currently defined:"
187 for test in self._known_tests:
Niko Catania2e990b92009-04-02 16:52:26 -0700188 print "%-15s %s" % (test.GetName(), test.GetDescription())
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700189
190 def _DoBuild(self):
191 logger.SilentLog("Building tests...")
192 target_set = Set()
Niko Cataniaa6dc2ab2009-04-03 14:12:46 -0700193 extra_args_set = Set()
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700194 for test_suite in self._GetTestsToRun():
Niko Cataniaa6dc2ab2009-04-03 14:12:46 -0700195 self._AddBuildTarget(test_suite, target_set, extra_args_set)
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700196
197 if target_set:
198 if self._options.coverage:
199 self._coverage_gen.EnableCoverageBuild()
Brett Chabot2b6643b2009-04-07 18:35:27 -0700200 self._AddBuildTargetPath(self._coverage_gen.GetEmmaBuildPath(),
201 target_set)
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700202 target_build_string = " ".join(list(target_set))
Niko Cataniaa6dc2ab2009-04-03 14:12:46 -0700203 extra_args_string = " ".join(list(extra_args_set))
Brett Chabot2b6643b2009-04-07 18:35:27 -0700204 # log the user-friendly equivalent make command, so developers can
205 # replicate this step
206 logger.Log("mmm %s %s" % (target_build_string, extra_args_string))
207 # mmm cannot be used from python, so perform a similiar operation using
208 # ONE_SHOT_MAKEFILE
Niko Cataniaa6dc2ab2009-04-03 14:12:46 -0700209 cmd = 'ONE_SHOT_MAKEFILE="%s" make -C "%s" files %s' % (
210 target_build_string, self._root_path, extra_args_string)
Niko Cataniaa6dc2ab2009-04-03 14:12:46 -0700211
Brett Chabot72731f32009-03-31 11:14:05 -0700212 if self._options.preview:
213 # in preview mode, just display to the user what command would have been
214 # run
215 logger.Log("adb sync")
216 else:
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700217 run_command.RunCommand(cmd, return_output=False)
218 logger.Log("Syncing to device...")
219 self._adb.Sync()
220
Niko Cataniaa6dc2ab2009-04-03 14:12:46 -0700221 def _AddBuildTarget(self, test_suite, target_set, extra_args_set):
222 build_dir = test_suite.GetBuildPath()
Brett Chabot2b6643b2009-04-07 18:35:27 -0700223 if self._AddBuildTargetPath(build_dir, target_set):
224 extra_args_set.add(test_suite.GetExtraMakeArgs())
225
226 def _AddBuildTargetPath(self, build_dir, target_set):
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700227 if build_dir is not None:
228 build_file_path = os.path.join(build_dir, "Android.mk")
229 if os.path.isfile(os.path.join(self._root_path, build_file_path)):
230 target_set.add(build_file_path)
Brett Chabot2b6643b2009-04-07 18:35:27 -0700231 return True
232 return False
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700233
234 def _GetTestsToRun(self):
235 """Get a list of TestSuite objects to run, based on command line args."""
236 if self._options.all_tests:
237 return self._known_tests.GetTests()
Brett Chabot49b77112009-06-02 11:46:04 -0700238 elif self._options.continuous_tests:
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700239 return self._known_tests.GetContinuousTests()
Brett Chabot49b77112009-06-02 11:46:04 -0700240 elif self._options.cts_tests:
241 return self._known_tests.GetCtsTests()
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700242 tests = []
243 for name in self._test_args:
244 test = self._known_tests.GetTest(name)
245 if test is None:
246 logger.Log("Error: Could not find test %s" % name)
247 self._DumpTests()
248 raise errors.AbortError
249 tests.append(test)
250 return tests
251
252 def _RunTest(self, test_suite):
253 """Run the provided test suite.
254
255 Builds up an adb instrument command using provided input arguments.
256
257 Args:
258 test_suite: TestSuite to run
259 """
260
261 test_class = test_suite.GetClassName()
262 if self._options.test_class is not None:
Brett Chabot292df412009-05-07 11:09:40 -0700263 test_class = self._options.test_class.lstrip()
264 if test_class.startswith("."):
265 test_class = test_suite.GetPackageName() + test_class
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700266 if self._options.test_method is not None:
267 test_class = "%s#%s" % (test_class, self._options.test_method)
268
269 instrumentation_args = {}
270 if test_class is not None:
271 instrumentation_args["class"] = test_class
Brett Chabot8a101cb2009-05-05 12:56:39 -0700272 if self._options.test_package:
273 instrumentation_args["package"] = self._options.test_package
274 if self._options.test_size:
275 instrumentation_args["size"] = self._options.test_size
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700276 if self._options.wait_for_debugger:
277 instrumentation_args["debug"] = "true"
278 if self._options.suite_assign_mode:
279 instrumentation_args["suiteAssignment"] = "true"
280 if self._options.coverage:
281 instrumentation_args["coverage"] = "true"
282 if self._options.preview:
283 adb_cmd = self._adb.PreviewInstrumentationCommand(
284 package_name=test_suite.GetPackageName(),
285 runner_name=test_suite.GetRunnerName(),
286 raw_mode=self._options.raw_mode,
287 instrumentation_args=instrumentation_args)
288 logger.Log(adb_cmd)
Brett Chabotae68f1a2009-05-28 18:29:24 -0700289 elif self._options.coverage:
Brett Chabot97d5c502009-06-04 13:50:55 -0700290 self._adb.WaitForInstrumentation(test_suite.GetPackageName(),
291 test_suite.GetRunnerName())
Brett Chabotae68f1a2009-05-28 18:29:24 -0700292 # need to parse test output to determine path to coverage file
293 logger.Log("Running in coverage mode, suppressing test output")
294 try:
295 (test_results, status_map) = self._adb.StartInstrumentationForPackage(
296 package_name=test_suite.GetPackageName(),
297 runner_name=test_suite.GetRunnerName(),
298 timeout_time=60*60,
299 instrumentation_args=instrumentation_args)
300 except errors.InstrumentationError, errors.DeviceUnresponsiveError:
301 return
302 self._PrintTestResults(test_results)
303 device_coverage_path = status_map.get("coverageFilePath", None)
304 if device_coverage_path is None:
305 logger.Log("Error: could not find coverage data on device")
306 return
307 coverage_file = self._coverage_gen.ExtractReport(test_suite, device_coverage_path)
308 if coverage_file is not None:
309 logger.Log("Coverage report generated at %s" % coverage_file)
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700310 else:
Brett Chabot97d5c502009-06-04 13:50:55 -0700311 self._adb.WaitForInstrumentation(test_suite.GetPackageName(),
312 test_suite.GetRunnerName())
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700313 self._adb.StartInstrumentationNoResults(
314 package_name=test_suite.GetPackageName(),
315 runner_name=test_suite.GetRunnerName(),
316 raw_mode=self._options.raw_mode,
317 instrumentation_args=instrumentation_args)
Brett Chabotae68f1a2009-05-28 18:29:24 -0700318
319 def _PrintTestResults(self, test_results):
320 """Prints a summary of test result data to stdout.
321
322 Args:
323 test_results: a list of am_instrument_parser.TestResult
324 """
325 total_count = 0
326 error_count = 0
327 fail_count = 0
328 for test_result in test_results:
329 if test_result.GetStatusCode() == -1: # error
330 logger.Log("Error in %s: %s" % (test_result.GetTestName(),
331 test_result.GetFailureReason()))
332 error_count+=1
333 elif test_result.GetStatusCode() == -2: # failure
334 logger.Log("Failure in %s: %s" % (test_result.GetTestName(),
335 test_result.GetFailureReason()))
336 fail_count+=1
337 total_count+=1
338 logger.Log("Tests run: %d, Failures: %d, Errors: %d" %
339 (total_count, fail_count, error_count))
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700340
Nicolas Cataniaff096c12009-05-01 11:55:36 -0700341 def _CollectTestSources(self, test_list, dirname, files):
342 """For each directory, find tests source file and add them to the list.
343
344 Test files must match one of the following pattern:
345 - test_*.[cc|cpp]
346 - *_test.[cc|cpp]
347 - *_unittest.[cc|cpp]
348
349 This method is a callback for os.path.walk.
350
351 Args:
352 test_list: Where new tests should be inserted.
353 dirname: Current directory.
354 files: List of files in the current directory.
355 """
356 for f in files:
357 (name, ext) = os.path.splitext(f)
358 if ext == ".cc" or ext == ".cpp":
359 if re.search("_test$|_test_$|_unittest$|_unittest_$|^test_", name):
360 logger.SilentLog("Found %s" % f)
361 test_list.append(str(os.path.join(dirname, f)))
362
363 def _FilterOutMissing(self, path, sources):
364 """Filter out from the sources list missing tests.
365
366 Sometimes some test source are not built for the target, i.e there
367 is no binary corresponding to the source file. We need to filter
368 these out.
369
370 Args:
371 path: Where the binaries should be.
372 sources: List of tests source path.
373 Returns:
374 A list of test binaries built from the sources.
375 """
376 binaries = []
377 for f in sources:
378 binary = os.path.basename(f)
379 binary = os.path.splitext(binary)[0]
380 full_path = os.path.join(path, binary)
381 if os.path.exists(full_path):
382 binaries.append(binary)
383 return binaries
384
Niko Catania2e990b92009-04-02 16:52:26 -0700385 def _RunNativeTest(self, test_suite):
386 """Run the provided *native* test suite.
387
Nicolas Cataniaff096c12009-05-01 11:55:36 -0700388 The test_suite must contain a build path where the native test
389 files are. Subdirectories are automatically scanned as well.
390
391 Each test's name must have a .cc or .cpp extension and match one
392 of the following patterns:
393 - test_*
394 - *_test.[cc|cpp]
395 - *_unittest.[cc|cpp]
Niko Catania2e990b92009-04-02 16:52:26 -0700396 A successful test must return 0. Any other value will be considered
397 as an error.
398
399 Args:
400 test_suite: TestSuite to run
401 """
402 # find all test files, convert unicode names to ascii, take the basename
403 # and drop the .cc/.cpp extension.
Nicolas Cataniaff096c12009-05-01 11:55:36 -0700404 source_list = []
405 build_path = test_suite.GetBuildPath()
406 os.path.walk(build_path, self._CollectTestSources, source_list)
407 logger.SilentLog("Tests source %s" % source_list)
408
409 # Host tests are under out/host/<os>-<arch>/bin.
410 host_list = self._FilterOutMissing(android_build.GetHostBin(), source_list)
411 logger.SilentLog("Host tests %s" % host_list)
412
413 # Target tests are under $ANDROID_PRODUCT_OUT/system/bin.
414 target_list = self._FilterOutMissing(android_build.GetTargetSystemBin(),
415 source_list)
416 logger.SilentLog("Target tests %s" % target_list)
Niko Catania2e990b92009-04-02 16:52:26 -0700417
Nicolas Catania97b24c42009-04-22 11:08:32 -0700418 # Run on the host
419 logger.Log("\nRunning on host")
Nicolas Cataniaff096c12009-05-01 11:55:36 -0700420 for f in host_list:
Nicolas Catania97b24c42009-04-22 11:08:32 -0700421 if run_command.RunHostCommand(f) != 0:
422 logger.Log("%s... failed" % f)
423 else:
Nicolas Cataniabcd93dc2009-05-14 12:25:23 -0700424 if run_command.HasValgrind():
425 if run_command.RunHostCommand(f, valgrind=True) == 0:
426 logger.Log("%s... ok\t\t[valgrind: ok]" % f)
427 else:
428 logger.Log("%s... ok\t\t[valgrind: failed]" % f)
Nicolas Catania97b24c42009-04-22 11:08:32 -0700429 else:
Nicolas Cataniabcd93dc2009-05-14 12:25:23 -0700430 logger.Log("%s... ok\t\t[valgrind: missing]" % f)
Nicolas Catania97b24c42009-04-22 11:08:32 -0700431
432 # Run on the device
433 logger.Log("\nRunning on target")
Nicolas Cataniaff096c12009-05-01 11:55:36 -0700434 for f in target_list:
435 full_path = os.path.join(os.sep, "system", "bin", f)
Niko Catania2e990b92009-04-02 16:52:26 -0700436
Niko Cataniafa14bd52009-04-09 16:50:54 -0700437 # Single quotes are needed to prevent the shell splitting it.
Nicolas Cataniabcd93dc2009-05-14 12:25:23 -0700438 output = self._adb.SendShellCommand("'%s 2>&1;echo -n exit code:$?'" %
Wei-Ta Chen97752d42009-05-21 16:24:04 -0700439 full_path,
440 int(self._options.timeout))
Nicolas Cataniabcd93dc2009-05-14 12:25:23 -0700441 success = output.endswith("exit code:0")
442 logger.Log("%s... %s" % (f, success and "ok" or "failed"))
443 # Print the captured output when the test failed.
444 if not success or self._options.verbose:
445 pos = output.rfind("exit code")
446 output = output[0:pos]
447 logger.Log(output)
Niko Catania2e990b92009-04-02 16:52:26 -0700448
449 # Cleanup
450 self._adb.SendShellCommand("rm %s" % full_path)
451
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700452 def RunTests(self):
453 """Main entry method - executes the tests according to command line args."""
454 try:
455 run_command.SetAbortOnError()
456 self._ProcessOptions()
457 if self._options.only_list_tests:
458 self._DumpTests()
459 return
460
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700461 if not self._options.skip_build:
462 self._DoBuild()
463
464 for test_suite in self._GetTestsToRun():
Niko Catania2e990b92009-04-02 16:52:26 -0700465 if test_suite.IsNative():
466 self._RunNativeTest(test_suite)
467 else:
468 self._RunTest(test_suite)
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700469 except KeyboardInterrupt:
470 logger.Log("Exiting...")
Brett Chabot8a101cb2009-05-05 12:56:39 -0700471 except errors.AbortError, e:
472 logger.Log(e.msg)
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700473 logger.SilentLog("Exiting due to AbortError...")
474 except errors.WaitForResponseTimedOutError:
475 logger.Log("Timed out waiting for response")
476
477
478def RunTests():
479 runner = TestRunner()
480 runner.RunTests()
481
482if __name__ == "__main__":
483 RunTests()