blob: 05d29ec348098dfe7f91ec3fe83377f595f9a8b1 [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")
115
116 group = optparse.OptionGroup(
117 parser, "Targets", "Use these options to direct tests to a specific "
118 "Android target")
119 group.add_option("-e", "--emulator", dest="emulator", default=False,
120 action="store_true", help="use emulator")
121 group.add_option("-d", "--device", dest="device", default=False,
122 action="store_true", help="use device")
123 group.add_option("-s", "--serial", dest="serial",
124 help="use specific serial")
125 parser.add_option_group(group)
126
127 self._options, self._test_args = parser.parse_args()
128
129 if (not self._options.only_list_tests and not self._options.all_tests
130 and not self._options.continuous_tests and len(self._test_args) < 1):
131 parser.print_help()
132 logger.SilentLog("at least one test name must be specified")
133 raise errors.AbortError
134
135 self._adb = adb_interface.AdbInterface()
136 if self._options.emulator:
137 self._adb.SetEmulatorTarget()
138 elif self._options.device:
139 self._adb.SetDeviceTarget()
140 elif self._options.serial is not None:
141 self._adb.SetTargetSerial(self._options.serial)
142
143 if self._options.verbose:
144 logger.SetVerbose(True)
145
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700146 self._known_tests = self._ReadTests()
147
148 self._coverage_gen = coverage.CoverageGenerator(
149 android_root_path=self._root_path, adb_interface=self._adb)
150
151 def _ReadTests(self):
152 """Parses the set of test definition data.
153
154 Returns:
155 A TestDefinitions object that contains the set of parsed tests.
156 Raises:
157 AbortError: If a fatal error occurred when parsing the tests.
158 """
159 core_test_path = os.path.join(self._root_path, self._CORE_TEST_PATH)
160 try:
161 known_tests = test_defs.TestDefinitions()
162 known_tests.Parse(core_test_path)
Brett Chabot2d85c0e2009-03-31 15:19:13 -0700163 # read all <android root>/vendor/*/tests/testinfo/test_defs.xml paths
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700164 vendor_tests_pattern = os.path.join(self._root_path,
165 self._VENDOR_TEST_PATH)
166 test_file_paths = glob.glob(vendor_tests_pattern)
167 for test_file_path in test_file_paths:
168 known_tests.Parse(test_file_path)
169 if os.path.isfile(self._options.user_tests_file):
170 known_tests.Parse(self._options.user_tests_file)
171 return known_tests
172 except errors.ParseError:
173 raise errors.AbortError
174
175 def _DumpTests(self):
176 """Prints out set of defined tests."""
177 print "The following tests are currently defined:"
178 for test in self._known_tests:
Niko Catania2e990b92009-04-02 16:52:26 -0700179 print "%-15s %s" % (test.GetName(), test.GetDescription())
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700180
181 def _DoBuild(self):
182 logger.SilentLog("Building tests...")
183 target_set = Set()
Niko Cataniaa6dc2ab2009-04-03 14:12:46 -0700184 extra_args_set = Set()
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700185 for test_suite in self._GetTestsToRun():
Niko Cataniaa6dc2ab2009-04-03 14:12:46 -0700186 self._AddBuildTarget(test_suite, target_set, extra_args_set)
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700187
188 if target_set:
189 if self._options.coverage:
190 self._coverage_gen.EnableCoverageBuild()
Brett Chabot2b6643b2009-04-07 18:35:27 -0700191 self._AddBuildTargetPath(self._coverage_gen.GetEmmaBuildPath(),
192 target_set)
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700193 target_build_string = " ".join(list(target_set))
Niko Cataniaa6dc2ab2009-04-03 14:12:46 -0700194 extra_args_string = " ".join(list(extra_args_set))
Brett Chabot2b6643b2009-04-07 18:35:27 -0700195 # log the user-friendly equivalent make command, so developers can
196 # replicate this step
197 logger.Log("mmm %s %s" % (target_build_string, extra_args_string))
198 # mmm cannot be used from python, so perform a similiar operation using
199 # ONE_SHOT_MAKEFILE
Niko Cataniaa6dc2ab2009-04-03 14:12:46 -0700200 cmd = 'ONE_SHOT_MAKEFILE="%s" make -C "%s" files %s' % (
201 target_build_string, self._root_path, extra_args_string)
Niko Cataniaa6dc2ab2009-04-03 14:12:46 -0700202
Brett Chabot72731f32009-03-31 11:14:05 -0700203 if self._options.preview:
204 # in preview mode, just display to the user what command would have been
205 # run
206 logger.Log("adb sync")
207 else:
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700208 run_command.RunCommand(cmd, return_output=False)
209 logger.Log("Syncing to device...")
210 self._adb.Sync()
211
Niko Cataniaa6dc2ab2009-04-03 14:12:46 -0700212 def _AddBuildTarget(self, test_suite, target_set, extra_args_set):
213 build_dir = test_suite.GetBuildPath()
Brett Chabot2b6643b2009-04-07 18:35:27 -0700214 if self._AddBuildTargetPath(build_dir, target_set):
215 extra_args_set.add(test_suite.GetExtraMakeArgs())
216
217 def _AddBuildTargetPath(self, build_dir, target_set):
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700218 if build_dir is not None:
219 build_file_path = os.path.join(build_dir, "Android.mk")
220 if os.path.isfile(os.path.join(self._root_path, build_file_path)):
221 target_set.add(build_file_path)
Brett Chabot2b6643b2009-04-07 18:35:27 -0700222 return True
223 return False
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700224
225 def _GetTestsToRun(self):
226 """Get a list of TestSuite objects to run, based on command line args."""
227 if self._options.all_tests:
228 return self._known_tests.GetTests()
229 if self._options.continuous_tests:
230 return self._known_tests.GetContinuousTests()
231 tests = []
232 for name in self._test_args:
233 test = self._known_tests.GetTest(name)
234 if test is None:
235 logger.Log("Error: Could not find test %s" % name)
236 self._DumpTests()
237 raise errors.AbortError
238 tests.append(test)
239 return tests
240
241 def _RunTest(self, test_suite):
242 """Run the provided test suite.
243
244 Builds up an adb instrument command using provided input arguments.
245
246 Args:
247 test_suite: TestSuite to run
248 """
249
250 test_class = test_suite.GetClassName()
251 if self._options.test_class is not None:
Brett Chabot292df412009-05-07 11:09:40 -0700252 test_class = self._options.test_class.lstrip()
253 if test_class.startswith("."):
254 test_class = test_suite.GetPackageName() + test_class
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700255 if self._options.test_method is not None:
256 test_class = "%s#%s" % (test_class, self._options.test_method)
257
258 instrumentation_args = {}
259 if test_class is not None:
260 instrumentation_args["class"] = test_class
Brett Chabot8a101cb2009-05-05 12:56:39 -0700261 if self._options.test_package:
262 instrumentation_args["package"] = self._options.test_package
263 if self._options.test_size:
264 instrumentation_args["size"] = self._options.test_size
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700265 if self._options.wait_for_debugger:
266 instrumentation_args["debug"] = "true"
267 if self._options.suite_assign_mode:
268 instrumentation_args["suiteAssignment"] = "true"
269 if self._options.coverage:
270 instrumentation_args["coverage"] = "true"
271 if self._options.preview:
272 adb_cmd = self._adb.PreviewInstrumentationCommand(
273 package_name=test_suite.GetPackageName(),
274 runner_name=test_suite.GetRunnerName(),
275 raw_mode=self._options.raw_mode,
276 instrumentation_args=instrumentation_args)
277 logger.Log(adb_cmd)
278 else:
279 self._adb.StartInstrumentationNoResults(
280 package_name=test_suite.GetPackageName(),
281 runner_name=test_suite.GetRunnerName(),
282 raw_mode=self._options.raw_mode,
283 instrumentation_args=instrumentation_args)
284 if self._options.coverage and test_suite.GetTargetName() is not None:
285 coverage_file = self._coverage_gen.ExtractReport(test_suite)
286 if coverage_file is not None:
287 logger.Log("Coverage report generated at %s" % coverage_file)
288
Nicolas Cataniaff096c12009-05-01 11:55:36 -0700289 def _CollectTestSources(self, test_list, dirname, files):
290 """For each directory, find tests source file and add them to the list.
291
292 Test files must match one of the following pattern:
293 - test_*.[cc|cpp]
294 - *_test.[cc|cpp]
295 - *_unittest.[cc|cpp]
296
297 This method is a callback for os.path.walk.
298
299 Args:
300 test_list: Where new tests should be inserted.
301 dirname: Current directory.
302 files: List of files in the current directory.
303 """
304 for f in files:
305 (name, ext) = os.path.splitext(f)
306 if ext == ".cc" or ext == ".cpp":
307 if re.search("_test$|_test_$|_unittest$|_unittest_$|^test_", name):
308 logger.SilentLog("Found %s" % f)
309 test_list.append(str(os.path.join(dirname, f)))
310
311 def _FilterOutMissing(self, path, sources):
312 """Filter out from the sources list missing tests.
313
314 Sometimes some test source are not built for the target, i.e there
315 is no binary corresponding to the source file. We need to filter
316 these out.
317
318 Args:
319 path: Where the binaries should be.
320 sources: List of tests source path.
321 Returns:
322 A list of test binaries built from the sources.
323 """
324 binaries = []
325 for f in sources:
326 binary = os.path.basename(f)
327 binary = os.path.splitext(binary)[0]
328 full_path = os.path.join(path, binary)
329 if os.path.exists(full_path):
330 binaries.append(binary)
331 return binaries
332
Niko Catania2e990b92009-04-02 16:52:26 -0700333 def _RunNativeTest(self, test_suite):
334 """Run the provided *native* test suite.
335
Nicolas Cataniaff096c12009-05-01 11:55:36 -0700336 The test_suite must contain a build path where the native test
337 files are. Subdirectories are automatically scanned as well.
338
339 Each test's name must have a .cc or .cpp extension and match one
340 of the following patterns:
341 - test_*
342 - *_test.[cc|cpp]
343 - *_unittest.[cc|cpp]
Niko Catania2e990b92009-04-02 16:52:26 -0700344 A successful test must return 0. Any other value will be considered
345 as an error.
346
347 Args:
348 test_suite: TestSuite to run
349 """
350 # find all test files, convert unicode names to ascii, take the basename
351 # and drop the .cc/.cpp extension.
Nicolas Cataniaff096c12009-05-01 11:55:36 -0700352 source_list = []
353 build_path = test_suite.GetBuildPath()
354 os.path.walk(build_path, self._CollectTestSources, source_list)
355 logger.SilentLog("Tests source %s" % source_list)
356
357 # Host tests are under out/host/<os>-<arch>/bin.
358 host_list = self._FilterOutMissing(android_build.GetHostBin(), source_list)
359 logger.SilentLog("Host tests %s" % host_list)
360
361 # Target tests are under $ANDROID_PRODUCT_OUT/system/bin.
362 target_list = self._FilterOutMissing(android_build.GetTargetSystemBin(),
363 source_list)
364 logger.SilentLog("Target tests %s" % target_list)
Niko Catania2e990b92009-04-02 16:52:26 -0700365
Nicolas Catania97b24c42009-04-22 11:08:32 -0700366 # Run on the host
367 logger.Log("\nRunning on host")
Nicolas Cataniaff096c12009-05-01 11:55:36 -0700368 for f in host_list:
Nicolas Catania97b24c42009-04-22 11:08:32 -0700369 if run_command.RunHostCommand(f) != 0:
370 logger.Log("%s... failed" % f)
371 else:
Nicolas Cataniabcd93dc2009-05-14 12:25:23 -0700372 if run_command.HasValgrind():
373 if run_command.RunHostCommand(f, valgrind=True) == 0:
374 logger.Log("%s... ok\t\t[valgrind: ok]" % f)
375 else:
376 logger.Log("%s... ok\t\t[valgrind: failed]" % f)
Nicolas Catania97b24c42009-04-22 11:08:32 -0700377 else:
Nicolas Cataniabcd93dc2009-05-14 12:25:23 -0700378 logger.Log("%s... ok\t\t[valgrind: missing]" % f)
Nicolas Catania97b24c42009-04-22 11:08:32 -0700379
380 # Run on the device
381 logger.Log("\nRunning on target")
Nicolas Cataniaff096c12009-05-01 11:55:36 -0700382 for f in target_list:
383 full_path = os.path.join(os.sep, "system", "bin", f)
Niko Catania2e990b92009-04-02 16:52:26 -0700384
Niko Cataniafa14bd52009-04-09 16:50:54 -0700385 # Single quotes are needed to prevent the shell splitting it.
Nicolas Cataniabcd93dc2009-05-14 12:25:23 -0700386 output = self._adb.SendShellCommand("'%s 2>&1;echo -n exit code:$?'" %
Niko Catania2e990b92009-04-02 16:52:26 -0700387 full_path)
Nicolas Cataniabcd93dc2009-05-14 12:25:23 -0700388 success = output.endswith("exit code:0")
389 logger.Log("%s... %s" % (f, success and "ok" or "failed"))
390 # Print the captured output when the test failed.
391 if not success or self._options.verbose:
392 pos = output.rfind("exit code")
393 output = output[0:pos]
394 logger.Log(output)
Niko Catania2e990b92009-04-02 16:52:26 -0700395
396 # Cleanup
397 self._adb.SendShellCommand("rm %s" % full_path)
398
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700399 def RunTests(self):
400 """Main entry method - executes the tests according to command line args."""
401 try:
402 run_command.SetAbortOnError()
403 self._ProcessOptions()
404 if self._options.only_list_tests:
405 self._DumpTests()
406 return
407
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700408 if not self._options.skip_build:
409 self._DoBuild()
410
411 for test_suite in self._GetTestsToRun():
Niko Catania2e990b92009-04-02 16:52:26 -0700412 if test_suite.IsNative():
413 self._RunNativeTest(test_suite)
414 else:
415 self._RunTest(test_suite)
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700416 except KeyboardInterrupt:
417 logger.Log("Exiting...")
Brett Chabot8a101cb2009-05-05 12:56:39 -0700418 except errors.AbortError, e:
419 logger.Log(e.msg)
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700420 logger.SilentLog("Exiting due to AbortError...")
421 except errors.WaitForResponseTimedOutError:
422 logger.Log("Timed out waiting for response")
423
424
425def RunTests():
426 runner = TestRunner()
427 runner.RunTests()
428
429if __name__ == "__main__":
430 RunTests()