blob: 046eb7c31b4703ea36616b7c13757aa012b66664 [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)")
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700118
119 group = optparse.OptionGroup(
120 parser, "Targets", "Use these options to direct tests to a specific "
121 "Android target")
122 group.add_option("-e", "--emulator", dest="emulator", default=False,
123 action="store_true", help="use emulator")
124 group.add_option("-d", "--device", dest="device", default=False,
125 action="store_true", help="use device")
126 group.add_option("-s", "--serial", dest="serial",
127 help="use specific serial")
128 parser.add_option_group(group)
129
130 self._options, self._test_args = parser.parse_args()
131
132 if (not self._options.only_list_tests and not self._options.all_tests
133 and not self._options.continuous_tests and len(self._test_args) < 1):
134 parser.print_help()
135 logger.SilentLog("at least one test name must be specified")
136 raise errors.AbortError
137
138 self._adb = adb_interface.AdbInterface()
139 if self._options.emulator:
140 self._adb.SetEmulatorTarget()
141 elif self._options.device:
142 self._adb.SetDeviceTarget()
143 elif self._options.serial is not None:
144 self._adb.SetTargetSerial(self._options.serial)
145
146 if self._options.verbose:
147 logger.SetVerbose(True)
148
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700149 self._known_tests = self._ReadTests()
150
151 self._coverage_gen = coverage.CoverageGenerator(
152 android_root_path=self._root_path, adb_interface=self._adb)
153
154 def _ReadTests(self):
155 """Parses the set of test definition data.
156
157 Returns:
158 A TestDefinitions object that contains the set of parsed tests.
159 Raises:
160 AbortError: If a fatal error occurred when parsing the tests.
161 """
162 core_test_path = os.path.join(self._root_path, self._CORE_TEST_PATH)
163 try:
164 known_tests = test_defs.TestDefinitions()
165 known_tests.Parse(core_test_path)
Brett Chabot2d85c0e2009-03-31 15:19:13 -0700166 # read all <android root>/vendor/*/tests/testinfo/test_defs.xml paths
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700167 vendor_tests_pattern = os.path.join(self._root_path,
168 self._VENDOR_TEST_PATH)
169 test_file_paths = glob.glob(vendor_tests_pattern)
170 for test_file_path in test_file_paths:
171 known_tests.Parse(test_file_path)
172 if os.path.isfile(self._options.user_tests_file):
173 known_tests.Parse(self._options.user_tests_file)
174 return known_tests
175 except errors.ParseError:
176 raise errors.AbortError
177
178 def _DumpTests(self):
179 """Prints out set of defined tests."""
180 print "The following tests are currently defined:"
181 for test in self._known_tests:
Niko Catania2e990b92009-04-02 16:52:26 -0700182 print "%-15s %s" % (test.GetName(), test.GetDescription())
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700183
184 def _DoBuild(self):
185 logger.SilentLog("Building tests...")
186 target_set = Set()
Niko Cataniaa6dc2ab2009-04-03 14:12:46 -0700187 extra_args_set = Set()
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700188 for test_suite in self._GetTestsToRun():
Niko Cataniaa6dc2ab2009-04-03 14:12:46 -0700189 self._AddBuildTarget(test_suite, target_set, extra_args_set)
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700190
191 if target_set:
192 if self._options.coverage:
193 self._coverage_gen.EnableCoverageBuild()
Brett Chabot2b6643b2009-04-07 18:35:27 -0700194 self._AddBuildTargetPath(self._coverage_gen.GetEmmaBuildPath(),
195 target_set)
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700196 target_build_string = " ".join(list(target_set))
Niko Cataniaa6dc2ab2009-04-03 14:12:46 -0700197 extra_args_string = " ".join(list(extra_args_set))
Brett Chabot2b6643b2009-04-07 18:35:27 -0700198 # log the user-friendly equivalent make command, so developers can
199 # replicate this step
200 logger.Log("mmm %s %s" % (target_build_string, extra_args_string))
201 # mmm cannot be used from python, so perform a similiar operation using
202 # ONE_SHOT_MAKEFILE
Niko Cataniaa6dc2ab2009-04-03 14:12:46 -0700203 cmd = 'ONE_SHOT_MAKEFILE="%s" make -C "%s" files %s' % (
204 target_build_string, self._root_path, extra_args_string)
Niko Cataniaa6dc2ab2009-04-03 14:12:46 -0700205
Brett Chabot72731f32009-03-31 11:14:05 -0700206 if self._options.preview:
207 # in preview mode, just display to the user what command would have been
208 # run
209 logger.Log("adb sync")
210 else:
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700211 run_command.RunCommand(cmd, return_output=False)
212 logger.Log("Syncing to device...")
213 self._adb.Sync()
214
Niko Cataniaa6dc2ab2009-04-03 14:12:46 -0700215 def _AddBuildTarget(self, test_suite, target_set, extra_args_set):
216 build_dir = test_suite.GetBuildPath()
Brett Chabot2b6643b2009-04-07 18:35:27 -0700217 if self._AddBuildTargetPath(build_dir, target_set):
218 extra_args_set.add(test_suite.GetExtraMakeArgs())
219
220 def _AddBuildTargetPath(self, build_dir, target_set):
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700221 if build_dir is not None:
222 build_file_path = os.path.join(build_dir, "Android.mk")
223 if os.path.isfile(os.path.join(self._root_path, build_file_path)):
224 target_set.add(build_file_path)
Brett Chabot2b6643b2009-04-07 18:35:27 -0700225 return True
226 return False
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700227
228 def _GetTestsToRun(self):
229 """Get a list of TestSuite objects to run, based on command line args."""
230 if self._options.all_tests:
231 return self._known_tests.GetTests()
232 if self._options.continuous_tests:
233 return self._known_tests.GetContinuousTests()
234 tests = []
235 for name in self._test_args:
236 test = self._known_tests.GetTest(name)
237 if test is None:
238 logger.Log("Error: Could not find test %s" % name)
239 self._DumpTests()
240 raise errors.AbortError
241 tests.append(test)
242 return tests
243
244 def _RunTest(self, test_suite):
245 """Run the provided test suite.
246
247 Builds up an adb instrument command using provided input arguments.
248
249 Args:
250 test_suite: TestSuite to run
251 """
252
253 test_class = test_suite.GetClassName()
254 if self._options.test_class is not None:
Brett Chabot292df412009-05-07 11:09:40 -0700255 test_class = self._options.test_class.lstrip()
256 if test_class.startswith("."):
257 test_class = test_suite.GetPackageName() + test_class
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700258 if self._options.test_method is not None:
259 test_class = "%s#%s" % (test_class, self._options.test_method)
260
261 instrumentation_args = {}
262 if test_class is not None:
263 instrumentation_args["class"] = test_class
Brett Chabot8a101cb2009-05-05 12:56:39 -0700264 if self._options.test_package:
265 instrumentation_args["package"] = self._options.test_package
266 if self._options.test_size:
267 instrumentation_args["size"] = self._options.test_size
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700268 if self._options.wait_for_debugger:
269 instrumentation_args["debug"] = "true"
270 if self._options.suite_assign_mode:
271 instrumentation_args["suiteAssignment"] = "true"
272 if self._options.coverage:
273 instrumentation_args["coverage"] = "true"
274 if self._options.preview:
275 adb_cmd = self._adb.PreviewInstrumentationCommand(
276 package_name=test_suite.GetPackageName(),
277 runner_name=test_suite.GetRunnerName(),
278 raw_mode=self._options.raw_mode,
279 instrumentation_args=instrumentation_args)
280 logger.Log(adb_cmd)
Brett Chabotae68f1a2009-05-28 18:29:24 -0700281 elif self._options.coverage:
282 # need to parse test output to determine path to coverage file
283 logger.Log("Running in coverage mode, suppressing test output")
284 try:
285 (test_results, status_map) = self._adb.StartInstrumentationForPackage(
286 package_name=test_suite.GetPackageName(),
287 runner_name=test_suite.GetRunnerName(),
288 timeout_time=60*60,
289 instrumentation_args=instrumentation_args)
290 except errors.InstrumentationError, errors.DeviceUnresponsiveError:
291 return
292 self._PrintTestResults(test_results)
293 device_coverage_path = status_map.get("coverageFilePath", None)
294 if device_coverage_path is None:
295 logger.Log("Error: could not find coverage data on device")
296 return
297 coverage_file = self._coverage_gen.ExtractReport(test_suite, device_coverage_path)
298 if coverage_file is not None:
299 logger.Log("Coverage report generated at %s" % coverage_file)
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700300 else:
301 self._adb.StartInstrumentationNoResults(
302 package_name=test_suite.GetPackageName(),
303 runner_name=test_suite.GetRunnerName(),
304 raw_mode=self._options.raw_mode,
305 instrumentation_args=instrumentation_args)
Brett Chabotae68f1a2009-05-28 18:29:24 -0700306
307 def _PrintTestResults(self, test_results):
308 """Prints a summary of test result data to stdout.
309
310 Args:
311 test_results: a list of am_instrument_parser.TestResult
312 """
313 total_count = 0
314 error_count = 0
315 fail_count = 0
316 for test_result in test_results:
317 if test_result.GetStatusCode() == -1: # error
318 logger.Log("Error in %s: %s" % (test_result.GetTestName(),
319 test_result.GetFailureReason()))
320 error_count+=1
321 elif test_result.GetStatusCode() == -2: # failure
322 logger.Log("Failure in %s: %s" % (test_result.GetTestName(),
323 test_result.GetFailureReason()))
324 fail_count+=1
325 total_count+=1
326 logger.Log("Tests run: %d, Failures: %d, Errors: %d" %
327 (total_count, fail_count, error_count))
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700328
Nicolas Cataniaff096c12009-05-01 11:55:36 -0700329 def _CollectTestSources(self, test_list, dirname, files):
330 """For each directory, find tests source file and add them to the list.
331
332 Test files must match one of the following pattern:
333 - test_*.[cc|cpp]
334 - *_test.[cc|cpp]
335 - *_unittest.[cc|cpp]
336
337 This method is a callback for os.path.walk.
338
339 Args:
340 test_list: Where new tests should be inserted.
341 dirname: Current directory.
342 files: List of files in the current directory.
343 """
344 for f in files:
345 (name, ext) = os.path.splitext(f)
346 if ext == ".cc" or ext == ".cpp":
347 if re.search("_test$|_test_$|_unittest$|_unittest_$|^test_", name):
348 logger.SilentLog("Found %s" % f)
349 test_list.append(str(os.path.join(dirname, f)))
350
351 def _FilterOutMissing(self, path, sources):
352 """Filter out from the sources list missing tests.
353
354 Sometimes some test source are not built for the target, i.e there
355 is no binary corresponding to the source file. We need to filter
356 these out.
357
358 Args:
359 path: Where the binaries should be.
360 sources: List of tests source path.
361 Returns:
362 A list of test binaries built from the sources.
363 """
364 binaries = []
365 for f in sources:
366 binary = os.path.basename(f)
367 binary = os.path.splitext(binary)[0]
368 full_path = os.path.join(path, binary)
369 if os.path.exists(full_path):
370 binaries.append(binary)
371 return binaries
372
Niko Catania2e990b92009-04-02 16:52:26 -0700373 def _RunNativeTest(self, test_suite):
374 """Run the provided *native* test suite.
375
Nicolas Cataniaff096c12009-05-01 11:55:36 -0700376 The test_suite must contain a build path where the native test
377 files are. Subdirectories are automatically scanned as well.
378
379 Each test's name must have a .cc or .cpp extension and match one
380 of the following patterns:
381 - test_*
382 - *_test.[cc|cpp]
383 - *_unittest.[cc|cpp]
Niko Catania2e990b92009-04-02 16:52:26 -0700384 A successful test must return 0. Any other value will be considered
385 as an error.
386
387 Args:
388 test_suite: TestSuite to run
389 """
390 # find all test files, convert unicode names to ascii, take the basename
391 # and drop the .cc/.cpp extension.
Nicolas Cataniaff096c12009-05-01 11:55:36 -0700392 source_list = []
393 build_path = test_suite.GetBuildPath()
394 os.path.walk(build_path, self._CollectTestSources, source_list)
395 logger.SilentLog("Tests source %s" % source_list)
396
397 # Host tests are under out/host/<os>-<arch>/bin.
398 host_list = self._FilterOutMissing(android_build.GetHostBin(), source_list)
399 logger.SilentLog("Host tests %s" % host_list)
400
401 # Target tests are under $ANDROID_PRODUCT_OUT/system/bin.
402 target_list = self._FilterOutMissing(android_build.GetTargetSystemBin(),
403 source_list)
404 logger.SilentLog("Target tests %s" % target_list)
Niko Catania2e990b92009-04-02 16:52:26 -0700405
Nicolas Catania97b24c42009-04-22 11:08:32 -0700406 # Run on the host
407 logger.Log("\nRunning on host")
Nicolas Cataniaff096c12009-05-01 11:55:36 -0700408 for f in host_list:
Nicolas Catania97b24c42009-04-22 11:08:32 -0700409 if run_command.RunHostCommand(f) != 0:
410 logger.Log("%s... failed" % f)
411 else:
Nicolas Cataniabcd93dc2009-05-14 12:25:23 -0700412 if run_command.HasValgrind():
413 if run_command.RunHostCommand(f, valgrind=True) == 0:
414 logger.Log("%s... ok\t\t[valgrind: ok]" % f)
415 else:
416 logger.Log("%s... ok\t\t[valgrind: failed]" % f)
Nicolas Catania97b24c42009-04-22 11:08:32 -0700417 else:
Nicolas Cataniabcd93dc2009-05-14 12:25:23 -0700418 logger.Log("%s... ok\t\t[valgrind: missing]" % f)
Nicolas Catania97b24c42009-04-22 11:08:32 -0700419
420 # Run on the device
421 logger.Log("\nRunning on target")
Nicolas Cataniaff096c12009-05-01 11:55:36 -0700422 for f in target_list:
423 full_path = os.path.join(os.sep, "system", "bin", f)
Niko Catania2e990b92009-04-02 16:52:26 -0700424
Niko Cataniafa14bd52009-04-09 16:50:54 -0700425 # Single quotes are needed to prevent the shell splitting it.
Nicolas Cataniabcd93dc2009-05-14 12:25:23 -0700426 output = self._adb.SendShellCommand("'%s 2>&1;echo -n exit code:$?'" %
Wei-Ta Chen97752d42009-05-21 16:24:04 -0700427 full_path,
428 int(self._options.timeout))
Nicolas Cataniabcd93dc2009-05-14 12:25:23 -0700429 success = output.endswith("exit code:0")
430 logger.Log("%s... %s" % (f, success and "ok" or "failed"))
431 # Print the captured output when the test failed.
432 if not success or self._options.verbose:
433 pos = output.rfind("exit code")
434 output = output[0:pos]
435 logger.Log(output)
Niko Catania2e990b92009-04-02 16:52:26 -0700436
437 # Cleanup
438 self._adb.SendShellCommand("rm %s" % full_path)
439
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700440 def RunTests(self):
441 """Main entry method - executes the tests according to command line args."""
442 try:
443 run_command.SetAbortOnError()
444 self._ProcessOptions()
445 if self._options.only_list_tests:
446 self._DumpTests()
447 return
448
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700449 if not self._options.skip_build:
450 self._DoBuild()
451
452 for test_suite in self._GetTestsToRun():
Niko Catania2e990b92009-04-02 16:52:26 -0700453 if test_suite.IsNative():
454 self._RunNativeTest(test_suite)
455 else:
456 self._RunTest(test_suite)
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700457 except KeyboardInterrupt:
458 logger.Log("Exiting...")
Brett Chabot8a101cb2009-05-05 12:56:39 -0700459 except errors.AbortError, e:
460 logger.Log(e.msg)
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700461 logger.SilentLog("Exiting due to AbortError...")
462 except errors.WaitForResponseTimedOutError:
463 logger.Log("Timed out waiting for response")
464
465
466def RunTests():
467 runner = TestRunner()
468 runner.RunTests()
469
470if __name__ == "__main__":
471 RunTests()