blob: fe6dfad0dc4651bc3ea0185a663a2a0679d14e8f [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")
98 parser.add_option("-u", "--user-tests-file", dest="user_tests_file",
99 metavar="FILE", default=user_test_default,
100 help="Alternate source of user test definitions")
101 parser.add_option("-o", "--coverage", dest="coverage",
102 default=False, action="store_true",
103 help="Generate code coverage metrics for test(s)")
104 parser.add_option("-t", "--all-tests", dest="all_tests",
105 default=False, action="store_true",
106 help="Run all defined tests")
107 parser.add_option("--continuous", dest="continuous_tests",
108 default=False, action="store_true",
109 help="Run all tests defined as part of the continuous "
110 "test set")
111
112 group = optparse.OptionGroup(
113 parser, "Targets", "Use these options to direct tests to a specific "
114 "Android target")
115 group.add_option("-e", "--emulator", dest="emulator", default=False,
116 action="store_true", help="use emulator")
117 group.add_option("-d", "--device", dest="device", default=False,
118 action="store_true", help="use device")
119 group.add_option("-s", "--serial", dest="serial",
120 help="use specific serial")
121 parser.add_option_group(group)
122
123 self._options, self._test_args = parser.parse_args()
124
125 if (not self._options.only_list_tests and not self._options.all_tests
126 and not self._options.continuous_tests and len(self._test_args) < 1):
127 parser.print_help()
128 logger.SilentLog("at least one test name must be specified")
129 raise errors.AbortError
130
131 self._adb = adb_interface.AdbInterface()
132 if self._options.emulator:
133 self._adb.SetEmulatorTarget()
134 elif self._options.device:
135 self._adb.SetDeviceTarget()
136 elif self._options.serial is not None:
137 self._adb.SetTargetSerial(self._options.serial)
138
139 if self._options.verbose:
140 logger.SetVerbose(True)
141
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700142 self._known_tests = self._ReadTests()
143
144 self._coverage_gen = coverage.CoverageGenerator(
145 android_root_path=self._root_path, adb_interface=self._adb)
146
147 def _ReadTests(self):
148 """Parses the set of test definition data.
149
150 Returns:
151 A TestDefinitions object that contains the set of parsed tests.
152 Raises:
153 AbortError: If a fatal error occurred when parsing the tests.
154 """
155 core_test_path = os.path.join(self._root_path, self._CORE_TEST_PATH)
156 try:
157 known_tests = test_defs.TestDefinitions()
158 known_tests.Parse(core_test_path)
Brett Chabot2d85c0e2009-03-31 15:19:13 -0700159 # read all <android root>/vendor/*/tests/testinfo/test_defs.xml paths
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700160 vendor_tests_pattern = os.path.join(self._root_path,
161 self._VENDOR_TEST_PATH)
162 test_file_paths = glob.glob(vendor_tests_pattern)
163 for test_file_path in test_file_paths:
164 known_tests.Parse(test_file_path)
165 if os.path.isfile(self._options.user_tests_file):
166 known_tests.Parse(self._options.user_tests_file)
167 return known_tests
168 except errors.ParseError:
169 raise errors.AbortError
170
171 def _DumpTests(self):
172 """Prints out set of defined tests."""
173 print "The following tests are currently defined:"
174 for test in self._known_tests:
Niko Catania2e990b92009-04-02 16:52:26 -0700175 print "%-15s %s" % (test.GetName(), test.GetDescription())
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700176
177 def _DoBuild(self):
178 logger.SilentLog("Building tests...")
179 target_set = Set()
Niko Cataniaa6dc2ab2009-04-03 14:12:46 -0700180 extra_args_set = Set()
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700181 for test_suite in self._GetTestsToRun():
Niko Cataniaa6dc2ab2009-04-03 14:12:46 -0700182 self._AddBuildTarget(test_suite, target_set, extra_args_set)
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700183
184 if target_set:
185 if self._options.coverage:
186 self._coverage_gen.EnableCoverageBuild()
Brett Chabot2b6643b2009-04-07 18:35:27 -0700187 self._AddBuildTargetPath(self._coverage_gen.GetEmmaBuildPath(),
188 target_set)
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700189 target_build_string = " ".join(list(target_set))
Niko Cataniaa6dc2ab2009-04-03 14:12:46 -0700190 extra_args_string = " ".join(list(extra_args_set))
Brett Chabot2b6643b2009-04-07 18:35:27 -0700191 # log the user-friendly equivalent make command, so developers can
192 # replicate this step
193 logger.Log("mmm %s %s" % (target_build_string, extra_args_string))
194 # mmm cannot be used from python, so perform a similiar operation using
195 # ONE_SHOT_MAKEFILE
Niko Cataniaa6dc2ab2009-04-03 14:12:46 -0700196 cmd = 'ONE_SHOT_MAKEFILE="%s" make -C "%s" files %s' % (
197 target_build_string, self._root_path, extra_args_string)
Niko Cataniaa6dc2ab2009-04-03 14:12:46 -0700198
Brett Chabot72731f32009-03-31 11:14:05 -0700199 if self._options.preview:
200 # in preview mode, just display to the user what command would have been
201 # run
202 logger.Log("adb sync")
203 else:
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700204 run_command.RunCommand(cmd, return_output=False)
205 logger.Log("Syncing to device...")
206 self._adb.Sync()
207
Niko Cataniaa6dc2ab2009-04-03 14:12:46 -0700208 def _AddBuildTarget(self, test_suite, target_set, extra_args_set):
209 build_dir = test_suite.GetBuildPath()
Brett Chabot2b6643b2009-04-07 18:35:27 -0700210 if self._AddBuildTargetPath(build_dir, target_set):
211 extra_args_set.add(test_suite.GetExtraMakeArgs())
212
213 def _AddBuildTargetPath(self, build_dir, target_set):
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700214 if build_dir is not None:
215 build_file_path = os.path.join(build_dir, "Android.mk")
216 if os.path.isfile(os.path.join(self._root_path, build_file_path)):
217 target_set.add(build_file_path)
Brett Chabot2b6643b2009-04-07 18:35:27 -0700218 return True
219 return False
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700220
221 def _GetTestsToRun(self):
222 """Get a list of TestSuite objects to run, based on command line args."""
223 if self._options.all_tests:
224 return self._known_tests.GetTests()
225 if self._options.continuous_tests:
226 return self._known_tests.GetContinuousTests()
227 tests = []
228 for name in self._test_args:
229 test = self._known_tests.GetTest(name)
230 if test is None:
231 logger.Log("Error: Could not find test %s" % name)
232 self._DumpTests()
233 raise errors.AbortError
234 tests.append(test)
235 return tests
236
237 def _RunTest(self, test_suite):
238 """Run the provided test suite.
239
240 Builds up an adb instrument command using provided input arguments.
241
242 Args:
243 test_suite: TestSuite to run
244 """
245
246 test_class = test_suite.GetClassName()
247 if self._options.test_class is not None:
248 test_class = self._options.test_class
249 if self._options.test_method is not None:
250 test_class = "%s#%s" % (test_class, self._options.test_method)
251
252 instrumentation_args = {}
253 if test_class is not None:
254 instrumentation_args["class"] = test_class
255 if self._options.wait_for_debugger:
256 instrumentation_args["debug"] = "true"
257 if self._options.suite_assign_mode:
258 instrumentation_args["suiteAssignment"] = "true"
259 if self._options.coverage:
260 instrumentation_args["coverage"] = "true"
261 if self._options.preview:
262 adb_cmd = self._adb.PreviewInstrumentationCommand(
263 package_name=test_suite.GetPackageName(),
264 runner_name=test_suite.GetRunnerName(),
265 raw_mode=self._options.raw_mode,
266 instrumentation_args=instrumentation_args)
267 logger.Log(adb_cmd)
268 else:
269 self._adb.StartInstrumentationNoResults(
270 package_name=test_suite.GetPackageName(),
271 runner_name=test_suite.GetRunnerName(),
272 raw_mode=self._options.raw_mode,
273 instrumentation_args=instrumentation_args)
274 if self._options.coverage and test_suite.GetTargetName() is not None:
275 coverage_file = self._coverage_gen.ExtractReport(test_suite)
276 if coverage_file is not None:
277 logger.Log("Coverage report generated at %s" % coverage_file)
278
Nicolas Cataniaff096c12009-05-01 11:55:36 -0700279 def _CollectTestSources(self, test_list, dirname, files):
280 """For each directory, find tests source file and add them to the list.
281
282 Test files must match one of the following pattern:
283 - test_*.[cc|cpp]
284 - *_test.[cc|cpp]
285 - *_unittest.[cc|cpp]
286
287 This method is a callback for os.path.walk.
288
289 Args:
290 test_list: Where new tests should be inserted.
291 dirname: Current directory.
292 files: List of files in the current directory.
293 """
294 for f in files:
295 (name, ext) = os.path.splitext(f)
296 if ext == ".cc" or ext == ".cpp":
297 if re.search("_test$|_test_$|_unittest$|_unittest_$|^test_", name):
298 logger.SilentLog("Found %s" % f)
299 test_list.append(str(os.path.join(dirname, f)))
300
301 def _FilterOutMissing(self, path, sources):
302 """Filter out from the sources list missing tests.
303
304 Sometimes some test source are not built for the target, i.e there
305 is no binary corresponding to the source file. We need to filter
306 these out.
307
308 Args:
309 path: Where the binaries should be.
310 sources: List of tests source path.
311 Returns:
312 A list of test binaries built from the sources.
313 """
314 binaries = []
315 for f in sources:
316 binary = os.path.basename(f)
317 binary = os.path.splitext(binary)[0]
318 full_path = os.path.join(path, binary)
319 if os.path.exists(full_path):
320 binaries.append(binary)
321 return binaries
322
Niko Catania2e990b92009-04-02 16:52:26 -0700323 def _RunNativeTest(self, test_suite):
324 """Run the provided *native* test suite.
325
Nicolas Cataniaff096c12009-05-01 11:55:36 -0700326 The test_suite must contain a build path where the native test
327 files are. Subdirectories are automatically scanned as well.
328
329 Each test's name must have a .cc or .cpp extension and match one
330 of the following patterns:
331 - test_*
332 - *_test.[cc|cpp]
333 - *_unittest.[cc|cpp]
Niko Catania2e990b92009-04-02 16:52:26 -0700334 A successful test must return 0. Any other value will be considered
335 as an error.
336
337 Args:
338 test_suite: TestSuite to run
339 """
340 # find all test files, convert unicode names to ascii, take the basename
341 # and drop the .cc/.cpp extension.
Nicolas Cataniaff096c12009-05-01 11:55:36 -0700342 source_list = []
343 build_path = test_suite.GetBuildPath()
344 os.path.walk(build_path, self._CollectTestSources, source_list)
345 logger.SilentLog("Tests source %s" % source_list)
346
347 # Host tests are under out/host/<os>-<arch>/bin.
348 host_list = self._FilterOutMissing(android_build.GetHostBin(), source_list)
349 logger.SilentLog("Host tests %s" % host_list)
350
351 # Target tests are under $ANDROID_PRODUCT_OUT/system/bin.
352 target_list = self._FilterOutMissing(android_build.GetTargetSystemBin(),
353 source_list)
354 logger.SilentLog("Target tests %s" % target_list)
Niko Catania2e990b92009-04-02 16:52:26 -0700355
Nicolas Catania97b24c42009-04-22 11:08:32 -0700356 # Run on the host
357 logger.Log("\nRunning on host")
Nicolas Cataniaff096c12009-05-01 11:55:36 -0700358 for f in host_list:
Nicolas Catania97b24c42009-04-22 11:08:32 -0700359 if run_command.RunHostCommand(f) != 0:
360 logger.Log("%s... failed" % f)
361 else:
362 if run_command.RunHostCommand(f, valgrind=True) == 0:
363 logger.Log("%s... ok\t\t[valgrind: ok]" % f)
364 else:
365 logger.Log("%s... ok\t\t[valgrind: failed]" % f)
366
367 # Run on the device
368 logger.Log("\nRunning on target")
Nicolas Cataniaff096c12009-05-01 11:55:36 -0700369 for f in target_list:
370 full_path = os.path.join(os.sep, "system", "bin", f)
Niko Catania2e990b92009-04-02 16:52:26 -0700371
Niko Cataniafa14bd52009-04-09 16:50:54 -0700372 # Single quotes are needed to prevent the shell splitting it.
373 status = self._adb.SendShellCommand("'%s >/dev/null 2>&1;echo -n $?'" %
Niko Catania2e990b92009-04-02 16:52:26 -0700374 full_path)
375 logger.Log("%s... %s" % (f, status == "0" and "ok" or "failed"))
376
377 # Cleanup
378 self._adb.SendShellCommand("rm %s" % full_path)
379
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700380 def RunTests(self):
381 """Main entry method - executes the tests according to command line args."""
382 try:
383 run_command.SetAbortOnError()
384 self._ProcessOptions()
385 if self._options.only_list_tests:
386 self._DumpTests()
387 return
388
Brett Chabot72731f32009-03-31 11:14:05 -0700389 if not self._adb.IsDevicePresent():
390 logger.Log("Error: specified device cannot be found")
391 return
392
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700393 if not self._options.skip_build:
394 self._DoBuild()
395
396 for test_suite in self._GetTestsToRun():
Niko Catania2e990b92009-04-02 16:52:26 -0700397 if test_suite.IsNative():
398 self._RunNativeTest(test_suite)
399 else:
400 self._RunTest(test_suite)
The Android Open Source Project6ffae012009-03-18 17:39:43 -0700401 except KeyboardInterrupt:
402 logger.Log("Exiting...")
403 except errors.AbortError:
404 logger.SilentLog("Exiting due to AbortError...")
405 except errors.WaitForResponseTimedOutError:
406 logger.Log("Timed out waiting for response")
407
408
409def RunTests():
410 runner = TestRunner()
411 runner.RunTests()
412
413if __name__ == "__main__":
414 RunTests()