blob: 4ded27eedc3cd738f7b2d7432c95b57d7586a3b2 [file] [log] [blame]
[email protected]fbe29322013-07-09 09:03:261#!/usr/bin/env python
2#
3# Copyright 2013 The Chromium Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Runs all types of tests from one unified interface.
8
9TODO(gkanwar):
10* Add options to run Monkey tests.
11"""
12
13import collections
14import optparse
15import os
[email protected]6bc1bda22013-07-19 22:08:3716import shutil
[email protected]fbe29322013-07-09 09:03:2617import sys
18
[email protected]fbe29322013-07-09 09:03:2619from pylib import constants
20from pylib import ports
21from pylib.base import base_test_result
[email protected]6bc1bda22013-07-19 22:08:3722from pylib.base import test_dispatcher
[email protected]6bc1bda22013-07-19 22:08:3723from pylib.gtest import gtest_config
[email protected]2a684222013-08-01 16:59:2224from pylib.gtest import setup as gtest_setup
25from pylib.gtest import test_options as gtest_test_options
[email protected]37ee0c792013-08-06 19:10:1326from pylib.host_driven import setup as host_driven_setup
[email protected]6bc1bda22013-07-19 22:08:3727from pylib.instrumentation import setup as instrumentation_setup
[email protected]2a684222013-08-01 16:59:2228from pylib.instrumentation import test_options as instrumentation_test_options
[email protected]3dbdfa42013-08-08 01:08:1429from pylib.monkey import setup as monkey_setup
30from pylib.monkey import test_options as monkey_test_options
[email protected]ec3170b2013-08-14 14:39:4731from pylib.perf import setup as perf_setup
32from pylib.perf import test_options as perf_test_options
33from pylib.perf import test_runner as perf_test_runner
[email protected]6bc1bda22013-07-19 22:08:3734from pylib.uiautomator import setup as uiautomator_setup
[email protected]2a684222013-08-01 16:59:2235from pylib.uiautomator import test_options as uiautomator_test_options
[email protected]6bc1bda22013-07-19 22:08:3736from pylib.utils import report_results
37from pylib.utils import run_tests_helper
[email protected]fbe29322013-07-09 09:03:2638
39
40_SDK_OUT_DIR = os.path.join(constants.DIR_SOURCE_ROOT, 'out')
41
42
43def AddBuildTypeOption(option_parser):
44 """Adds the build type option to |option_parser|."""
45 default_build_type = 'Debug'
46 if 'BUILDTYPE' in os.environ:
47 default_build_type = os.environ['BUILDTYPE']
48 option_parser.add_option('--debug', action='store_const', const='Debug',
49 dest='build_type', default=default_build_type,
50 help=('If set, run test suites under out/Debug. '
51 'Default is env var BUILDTYPE or Debug.'))
52 option_parser.add_option('--release', action='store_const',
53 const='Release', dest='build_type',
54 help=('If set, run test suites under out/Release.'
55 ' Default is env var BUILDTYPE or Debug.'))
56
57
[email protected]fbe29322013-07-09 09:03:2658def AddCommonOptions(option_parser):
59 """Adds all common options to |option_parser|."""
60
61 AddBuildTypeOption(option_parser)
62
[email protected]fbe29322013-07-09 09:03:2663 option_parser.add_option('-c', dest='cleanup_test_files',
64 help='Cleanup test files on the device after run',
65 action='store_true')
66 option_parser.add_option('--num_retries', dest='num_retries', type='int',
67 default=2,
68 help=('Number of retries for a test before '
69 'giving up.'))
70 option_parser.add_option('-v',
71 '--verbose',
72 dest='verbose_count',
73 default=0,
74 action='count',
75 help='Verbose level (multiple times for more)')
[email protected]fbe29322013-07-09 09:03:2676 option_parser.add_option('--tool',
77 dest='tool',
78 help=('Run the test under a tool '
79 '(use --tool help to list them)'))
80 option_parser.add_option('--flakiness-dashboard-server',
81 dest='flakiness_dashboard_server',
82 help=('Address of the server that is hosting the '
83 'Chrome for Android flakiness dashboard.'))
84 option_parser.add_option('--skip-deps-push', dest='push_deps',
85 action='store_false', default=True,
86 help=('Do not push dependencies to the device. '
87 'Use this at own risk for speeding up test '
88 'execution on local machine.'))
[email protected]fbe29322013-07-09 09:03:2689 option_parser.add_option('-d', '--device', dest='test_device',
90 help=('Target device for the test suite '
91 'to run on.'))
92
93
94def ProcessCommonOptions(options):
95 """Processes and handles all common options."""
[email protected]fbe29322013-07-09 09:03:2696 run_tests_helper.SetLogLevel(options.verbose_count)
97
98
[email protected]fbe29322013-07-09 09:03:2699def AddGTestOptions(option_parser):
100 """Adds gtest options to |option_parser|."""
101
102 option_parser.usage = '%prog gtest [options]'
103 option_parser.command_list = []
104 option_parser.example = '%prog gtest -s base_unittests'
105
[email protected]6bc1bda22013-07-19 22:08:37106 # TODO(gkanwar): Make this option required
107 option_parser.add_option('-s', '--suite', dest='suite_name',
[email protected]fbe29322013-07-09 09:03:26108 help=('Executable name of the test suite to run '
109 '(use -s help to list them).'))
[email protected]9e689252013-07-30 20:14:36110 option_parser.add_option('-f', '--gtest_filter', dest='test_filter',
111 help='googletest-style filter string.')
112 option_parser.add_option('-a', '--test_arguments', dest='test_arguments',
113 help='Additional arguments to pass to the test.')
114 option_parser.add_option('-t', dest='timeout',
115 help='Timeout to wait for each test',
116 type='int',
117 default=60)
[email protected]fbe29322013-07-09 09:03:26118 # TODO(gkanwar): Move these to Common Options once we have the plumbing
119 # in our other test types to handle these commands
[email protected]fbe29322013-07-09 09:03:26120 AddCommonOptions(option_parser)
121
122
[email protected]6bc1bda22013-07-19 22:08:37123def ProcessGTestOptions(options):
124 """Intercept test suite help to list test suites.
125
126 Args:
127 options: Command line options.
[email protected]6bc1bda22013-07-19 22:08:37128 """
129 if options.suite_name == 'help':
130 print 'Available test suites are:'
[email protected]9e689252013-07-30 20:14:36131 for test_suite in (gtest_config.STABLE_TEST_SUITES +
132 gtest_config.EXPERIMENTAL_TEST_SUITES):
133 print test_suite
[email protected]2a684222013-08-01 16:59:22134 sys.exit(0)
[email protected]6bc1bda22013-07-19 22:08:37135
136 # Convert to a list, assuming all test suites if nothing was specified.
137 # TODO(gkanwar): Require having a test suite
138 if options.suite_name:
139 options.suite_name = [options.suite_name]
140 else:
[email protected]9e689252013-07-30 20:14:36141 options.suite_name = [s for s in gtest_config.STABLE_TEST_SUITES]
[email protected]6bc1bda22013-07-19 22:08:37142
143
[email protected]fbe29322013-07-09 09:03:26144def AddJavaTestOptions(option_parser):
145 """Adds the Java test options to |option_parser|."""
146
147 option_parser.add_option('-f', '--test_filter', dest='test_filter',
148 help=('Test filter (if not fully qualified, '
149 'will run all matches).'))
150 option_parser.add_option(
151 '-A', '--annotation', dest='annotation_str',
152 help=('Comma-separated list of annotations. Run only tests with any of '
153 'the given annotations. An annotation can be either a key or a '
154 'key-values pair. A test that has no annotation is considered '
155 '"SmallTest".'))
156 option_parser.add_option(
157 '-E', '--exclude-annotation', dest='exclude_annotation_str',
158 help=('Comma-separated list of annotations. Exclude tests with these '
159 'annotations.'))
[email protected]fbe29322013-07-09 09:03:26160 option_parser.add_option('--screenshot', dest='screenshot_failures',
161 action='store_true',
162 help='Capture screenshots of test failures')
163 option_parser.add_option('--save-perf-json', action='store_true',
164 help='Saves the JSON file for each UI Perf test.')
[email protected]37ee0c792013-08-06 19:10:13165 option_parser.add_option('--official-build', action='store_true',
166 help='Run official build tests.')
[email protected]fbe29322013-07-09 09:03:26167 option_parser.add_option('--keep_test_server_ports',
168 action='store_true',
169 help=('Indicates the test server ports must be '
170 'kept. When this is run via a sharder '
171 'the test server ports should be kept and '
172 'should not be reset.'))
[email protected]fbe29322013-07-09 09:03:26173 option_parser.add_option('--test_data', action='append', default=[],
174 help=('Each instance defines a directory of test '
175 'data that should be copied to the target(s) '
176 'before running the tests. The argument '
177 'should be of the form <target>:<source>, '
178 '<target> is relative to the device data'
179 'directory, and <source> is relative to the '
180 'chromium build directory.'))
181
182
183def ProcessJavaTestOptions(options, error_func):
184 """Processes options/arguments and populates |options| with defaults."""
185
[email protected]fbe29322013-07-09 09:03:26186 if options.annotation_str:
187 options.annotations = options.annotation_str.split(',')
188 elif options.test_filter:
189 options.annotations = []
190 else:
[email protected]6bc1bda22013-07-19 22:08:37191 options.annotations = ['Smoke', 'SmallTest', 'MediumTest', 'LargeTest',
192 'EnormousTest']
[email protected]fbe29322013-07-09 09:03:26193
194 if options.exclude_annotation_str:
195 options.exclude_annotations = options.exclude_annotation_str.split(',')
196 else:
197 options.exclude_annotations = []
198
199 if not options.keep_test_server_ports:
200 if not ports.ResetTestServerPortAllocation():
201 raise Exception('Failed to reset test server port.')
202
203
204def AddInstrumentationTestOptions(option_parser):
205 """Adds Instrumentation test options to |option_parser|."""
206
207 option_parser.usage = '%prog instrumentation [options]'
208 option_parser.command_list = []
[email protected]fb7ab5e82013-07-26 18:31:20209 option_parser.example = ('%prog instrumentation '
[email protected]fbe29322013-07-09 09:03:26210 '--test-apk=ChromiumTestShellTest')
211
212 AddJavaTestOptions(option_parser)
213 AddCommonOptions(option_parser)
214
[email protected]37ee0c792013-08-06 19:10:13215 option_parser.add_option('-j', '--java_only', action='store_true',
216 default=False, help='Run only the Java tests.')
217 option_parser.add_option('-p', '--python_only', action='store_true',
218 default=False,
219 help='Run only the host-driven tests.')
[email protected]67954f822013-08-14 18:09:08220 option_parser.add_option('--python_test_root', '--host-driven-root',
221 dest='host_driven_root',
[email protected]37ee0c792013-08-06 19:10:13222 help='Root of the host-driven tests.')
[email protected]fbe29322013-07-09 09:03:26223 option_parser.add_option('-w', '--wait_debugger', dest='wait_for_debugger',
224 action='store_true',
225 help='Wait for debugger.')
[email protected]fbe29322013-07-09 09:03:26226 option_parser.add_option(
227 '--test-apk', dest='test_apk',
228 help=('The name of the apk containing the tests '
229 '(without the .apk extension; e.g. "ContentShellTest"). '
230 'Alternatively, this can be a full path to the apk.'))
231
232
233def ProcessInstrumentationOptions(options, error_func):
[email protected]2a684222013-08-01 16:59:22234 """Processes options/arguments and populate |options| with defaults.
235
236 Args:
237 options: optparse.Options object.
238 error_func: Function to call with the error message in case of an error.
239
240 Returns:
241 An InstrumentationOptions named tuple which contains all options relevant to
242 instrumentation tests.
243 """
[email protected]fbe29322013-07-09 09:03:26244
245 ProcessJavaTestOptions(options, error_func)
246
[email protected]37ee0c792013-08-06 19:10:13247 if options.java_only and options.python_only:
248 error_func('Options java_only (-j) and python_only (-p) '
249 'are mutually exclusive.')
250 options.run_java_tests = True
251 options.run_python_tests = True
252 if options.java_only:
253 options.run_python_tests = False
254 elif options.python_only:
255 options.run_java_tests = False
256
[email protected]67954f822013-08-14 18:09:08257 if not options.host_driven_root:
[email protected]37ee0c792013-08-06 19:10:13258 options.run_python_tests = False
259
[email protected]fbe29322013-07-09 09:03:26260 if not options.test_apk:
261 error_func('--test-apk must be specified.')
262
263 if os.path.exists(options.test_apk):
264 # The APK is fully qualified, assume the JAR lives along side.
265 options.test_apk_path = options.test_apk
266 options.test_apk_jar_path = (os.path.splitext(options.test_apk_path)[0] +
267 '.jar')
268 else:
269 options.test_apk_path = os.path.join(_SDK_OUT_DIR,
270 options.build_type,
271 constants.SDK_BUILD_APKS_DIR,
272 '%s.apk' % options.test_apk)
273 options.test_apk_jar_path = os.path.join(
274 _SDK_OUT_DIR, options.build_type, constants.SDK_BUILD_TEST_JAVALIB_DIR,
275 '%s.jar' % options.test_apk)
276
[email protected]2a684222013-08-01 16:59:22277 return instrumentation_test_options.InstrumentationOptions(
278 options.build_type,
279 options.tool,
280 options.cleanup_test_files,
281 options.push_deps,
282 options.annotations,
283 options.exclude_annotations,
284 options.test_filter,
285 options.test_data,
286 options.save_perf_json,
287 options.screenshot_failures,
[email protected]2a684222013-08-01 16:59:22288 options.wait_for_debugger,
289 options.test_apk,
290 options.test_apk_path,
291 options.test_apk_jar_path)
292
[email protected]fbe29322013-07-09 09:03:26293
294def AddUIAutomatorTestOptions(option_parser):
295 """Adds UI Automator test options to |option_parser|."""
296
297 option_parser.usage = '%prog uiautomator [options]'
298 option_parser.command_list = []
299 option_parser.example = (
300 '%prog uiautomator --test-jar=chromium_testshell_uiautomator_tests'
301 ' --package-name=org.chromium.chrome.testshell')
302 option_parser.add_option(
303 '--package-name',
304 help='The package name used by the apk containing the application.')
305 option_parser.add_option(
306 '--test-jar', dest='test_jar',
307 help=('The name of the dexed jar containing the tests (without the '
308 '.dex.jar extension). Alternatively, this can be a full path '
309 'to the jar.'))
310
311 AddJavaTestOptions(option_parser)
312 AddCommonOptions(option_parser)
313
314
315def ProcessUIAutomatorOptions(options, error_func):
[email protected]2a684222013-08-01 16:59:22316 """Processes UIAutomator options/arguments.
317
318 Args:
319 options: optparse.Options object.
320 error_func: Function to call with the error message in case of an error.
321
322 Returns:
323 A UIAutomatorOptions named tuple which contains all options relevant to
[email protected]3dbdfa42013-08-08 01:08:14324 uiautomator tests.
[email protected]2a684222013-08-01 16:59:22325 """
[email protected]fbe29322013-07-09 09:03:26326
327 ProcessJavaTestOptions(options, error_func)
328
329 if not options.package_name:
330 error_func('--package-name must be specified.')
331
332 if not options.test_jar:
333 error_func('--test-jar must be specified.')
334
335 if os.path.exists(options.test_jar):
336 # The dexed JAR is fully qualified, assume the info JAR lives along side.
337 options.uiautomator_jar = options.test_jar
338 else:
339 options.uiautomator_jar = os.path.join(
340 _SDK_OUT_DIR, options.build_type, constants.SDK_BUILD_JAVALIB_DIR,
341 '%s.dex.jar' % options.test_jar)
342 options.uiautomator_info_jar = (
343 options.uiautomator_jar[:options.uiautomator_jar.find('.dex.jar')] +
344 '_java.jar')
345
[email protected]2a684222013-08-01 16:59:22346 return uiautomator_test_options.UIAutomatorOptions(
347 options.build_type,
348 options.tool,
349 options.cleanup_test_files,
350 options.push_deps,
351 options.annotations,
352 options.exclude_annotations,
353 options.test_filter,
354 options.test_data,
355 options.save_perf_json,
356 options.screenshot_failures,
[email protected]2a684222013-08-01 16:59:22357 options.uiautomator_jar,
358 options.uiautomator_info_jar,
359 options.package_name)
360
[email protected]fbe29322013-07-09 09:03:26361
[email protected]3dbdfa42013-08-08 01:08:14362def AddMonkeyTestOptions(option_parser):
363 """Adds monkey test options to |option_parser|."""
[email protected]fb81b982013-08-09 00:07:12364
365 option_parser.usage = '%prog monkey [options]'
366 option_parser.command_list = []
367 option_parser.example = (
368 '%prog monkey --package-name=org.chromium.content_shell_apk'
369 ' --activity-name=.ContentShellActivity')
370
[email protected]3dbdfa42013-08-08 01:08:14371 option_parser.add_option('--package-name', help='Allowed package.')
372 option_parser.add_option(
[email protected]fb81b982013-08-09 00:07:12373 '--activity-name', help='Name of the activity to start.')
[email protected]3dbdfa42013-08-08 01:08:14374 option_parser.add_option(
375 '--event-count', default=10000, type='int',
376 help='Number of events to generate [default: %default].')
377 option_parser.add_option(
378 '--category', default='',
[email protected]fb81b982013-08-09 00:07:12379 help='A list of allowed categories.')
[email protected]3dbdfa42013-08-08 01:08:14380 option_parser.add_option(
381 '--throttle', default=100, type='int',
382 help='Delay between events (ms) [default: %default]. ')
383 option_parser.add_option(
384 '--seed', type='int',
385 help=('Seed value for pseudo-random generator. Same seed value generates '
386 'the same sequence of events. Seed is randomized by default.'))
387 option_parser.add_option(
388 '--extra-args', default='',
389 help=('String of other args to pass to the command verbatim '
390 '[default: "%default"].'))
391
392 AddCommonOptions(option_parser)
393
394
395def ProcessMonkeyTestOptions(options, error_func):
396 """Processes all monkey test options.
397
398 Args:
399 options: optparse.Options object.
400 error_func: Function to call with the error message in case of an error.
401
402 Returns:
403 A MonkeyOptions named tuple which contains all options relevant to
404 monkey tests.
405 """
406 if not options.package_name:
407 error_func('Package name is required.')
408
409 category = options.category
410 if category:
411 category = options.category.split(',')
412
413 return monkey_test_options.MonkeyOptions(
414 options.build_type,
415 options.verbose_count,
416 options.package_name,
417 options.activity_name,
418 options.event_count,
419 category,
420 options.throttle,
421 options.seed,
422 options.extra_args)
423
424
[email protected]ec3170b2013-08-14 14:39:47425def AddPerfTestOptions(option_parser):
426 """Adds perf test options to |option_parser|."""
427
428 option_parser.usage = '%prog perf [options]'
429 option_parser.command_list = []
430 option_parser.example = ('%prog perf --steps perf_steps.json')
431
432 option_parser.add_option('--steps', help='JSON file containing the list '
433 'of perf steps to run.')
434 option_parser.add_option('--flaky-steps',
435 help='A JSON file containing steps that are flaky '
436 'and will have its exit code ignored.')
437 option_parser.add_option('--print-step', help='The name of a previously '
438 'executed perf step to print.')
439
440 AddCommonOptions(option_parser)
441
442
443def ProcessPerfTestOptions(options, error_func):
444 """Processes all perf test options.
445
446 Args:
447 options: optparse.Options object.
448 error_func: Function to call with the error message in case of an error.
449
450 Returns:
451 A PerfOptions named tuple which contains all options relevant to
452 perf tests.
453 """
454 if not options.steps and not options.print_step:
455 error_func('Please specify --steps or --print-step')
456 return perf_test_options.PerfOptions(
457 options.steps, options.flaky_steps, options.print_step)
458
459
460
[email protected]6bc1bda22013-07-19 22:08:37461def _RunGTests(options, error_func):
462 """Subcommand of RunTestsCommands which runs gtests."""
[email protected]2a684222013-08-01 16:59:22463 ProcessGTestOptions(options)
[email protected]6bc1bda22013-07-19 22:08:37464
465 exit_code = 0
466 for suite_name in options.suite_name:
[email protected]2a684222013-08-01 16:59:22467 # TODO(gkanwar): Move this into ProcessGTestOptions once we require -s for
468 # the gtest command.
469 gtest_options = gtest_test_options.GTestOptions(
470 options.build_type,
471 options.tool,
472 options.cleanup_test_files,
473 options.push_deps,
474 options.test_filter,
475 options.test_arguments,
476 options.timeout,
477 suite_name)
478 runner_factory, tests = gtest_setup.Setup(gtest_options)
[email protected]6bc1bda22013-07-19 22:08:37479
480 results, test_exit_code = test_dispatcher.RunTests(
481 tests, runner_factory, False, options.test_device,
482 shard=True,
483 build_type=options.build_type,
484 test_timeout=None,
485 num_retries=options.num_retries)
486
487 if test_exit_code and exit_code != constants.ERROR_EXIT_CODE:
488 exit_code = test_exit_code
489
490 report_results.LogFull(
491 results=results,
492 test_type='Unit test',
493 test_package=suite_name,
494 build_type=options.build_type,
495 flakiness_server=options.flakiness_dashboard_server)
496
497 if os.path.isdir(constants.ISOLATE_DEPS_DIR):
498 shutil.rmtree(constants.ISOLATE_DEPS_DIR)
499
500 return exit_code
501
502
[email protected]6bc1bda22013-07-19 22:08:37503def _RunInstrumentationTests(options, error_func):
504 """Subcommand of RunTestsCommands which runs instrumentation tests."""
[email protected]2a684222013-08-01 16:59:22505 instrumentation_options = ProcessInstrumentationOptions(options, error_func)
[email protected]6bc1bda22013-07-19 22:08:37506
507 results = base_test_result.TestRunResults()
508 exit_code = 0
509
510 if options.run_java_tests:
[email protected]2a684222013-08-01 16:59:22511 runner_factory, tests = instrumentation_setup.Setup(instrumentation_options)
[email protected]6bc1bda22013-07-19 22:08:37512
513 test_results, exit_code = test_dispatcher.RunTests(
514 tests, runner_factory, options.wait_for_debugger,
515 options.test_device,
516 shard=True,
517 build_type=options.build_type,
518 test_timeout=None,
519 num_retries=options.num_retries)
520
521 results.AddTestRunResults(test_results)
522
523 if options.run_python_tests:
[email protected]37ee0c792013-08-06 19:10:13524 runner_factory, tests = host_driven_setup.InstrumentationSetup(
[email protected]67954f822013-08-14 18:09:08525 options.host_driven_root, options.official_build,
[email protected]37ee0c792013-08-06 19:10:13526 instrumentation_options)
527
[email protected]34020022013-08-06 23:35:34528 if tests:
529 test_results, test_exit_code = test_dispatcher.RunTests(
530 tests, runner_factory, False,
531 options.test_device,
532 shard=True,
533 build_type=options.build_type,
534 test_timeout=None,
535 num_retries=options.num_retries)
[email protected]6bc1bda22013-07-19 22:08:37536
[email protected]34020022013-08-06 23:35:34537 results.AddTestRunResults(test_results)
[email protected]6bc1bda22013-07-19 22:08:37538
[email protected]34020022013-08-06 23:35:34539 # Only allow exit code escalation
540 if test_exit_code and exit_code != constants.ERROR_EXIT_CODE:
541 exit_code = test_exit_code
[email protected]6bc1bda22013-07-19 22:08:37542
543 report_results.LogFull(
544 results=results,
545 test_type='Instrumentation',
546 test_package=os.path.basename(options.test_apk),
547 annotation=options.annotations,
548 build_type=options.build_type,
549 flakiness_server=options.flakiness_dashboard_server)
550
551 return exit_code
552
553
554def _RunUIAutomatorTests(options, error_func):
555 """Subcommand of RunTestsCommands which runs uiautomator tests."""
[email protected]2a684222013-08-01 16:59:22556 uiautomator_options = ProcessUIAutomatorOptions(options, error_func)
[email protected]6bc1bda22013-07-19 22:08:37557
[email protected]37ee0c792013-08-06 19:10:13558 runner_factory, tests = uiautomator_setup.Setup(uiautomator_options)
[email protected]6bc1bda22013-07-19 22:08:37559
[email protected]37ee0c792013-08-06 19:10:13560 results, exit_code = test_dispatcher.RunTests(
561 tests, runner_factory, False, options.test_device,
562 shard=True,
563 build_type=options.build_type,
564 test_timeout=None,
565 num_retries=options.num_retries)
[email protected]6bc1bda22013-07-19 22:08:37566
567 report_results.LogFull(
568 results=results,
569 test_type='UIAutomator',
570 test_package=os.path.basename(options.test_jar),
571 annotation=options.annotations,
572 build_type=options.build_type,
573 flakiness_server=options.flakiness_dashboard_server)
574
575 return exit_code
576
577
[email protected]3dbdfa42013-08-08 01:08:14578def _RunMonkeyTests(options, error_func):
579 """Subcommand of RunTestsCommands which runs monkey tests."""
580 monkey_options = ProcessMonkeyTestOptions(options, error_func)
581
582 runner_factory, tests = monkey_setup.Setup(monkey_options)
583
584 results, exit_code = test_dispatcher.RunTests(
[email protected]78e1f6822013-08-12 21:09:44585 tests, runner_factory, False, None, shard=False, test_timeout=None)
[email protected]3dbdfa42013-08-08 01:08:14586
587 report_results.LogFull(
588 results=results,
589 test_type='Monkey',
590 test_package='Monkey',
591 build_type=options.build_type)
592
593 return exit_code
594
595
[email protected]ec3170b2013-08-14 14:39:47596def _RunPerfTests(options, error_func):
597 """Subcommand of RunTestsCommands which runs perf tests."""
598 perf_options = ProcessPerfTestOptions(options, error_func)
599 # Just print the results from a single previously executed step.
600 if perf_options.print_step:
601 return perf_test_runner.PrintTestOutput(perf_options.print_step)
602
603 runner_factory, tests = perf_setup.Setup(perf_options)
604
605 results, exit_code = test_dispatcher.RunTests(
606 tests, runner_factory, False, None, shard=True, test_timeout=None)
607
608 report_results.LogFull(
609 results=results,
610 test_type='Perf',
611 test_package='Perf',
612 build_type=options.build_type)
613
614 return exit_code
615
[email protected]3dbdfa42013-08-08 01:08:14616
[email protected]fbe29322013-07-09 09:03:26617def RunTestsCommand(command, options, args, option_parser):
618 """Checks test type and dispatches to the appropriate function.
619
620 Args:
621 command: String indicating the command that was received to trigger
622 this function.
623 options: optparse options dictionary.
624 args: List of extra args from optparse.
625 option_parser: optparse.OptionParser object.
626
627 Returns:
628 Integer indicated exit code.
[email protected]b3873892013-07-10 04:57:10629
630 Raises:
631 Exception: Unknown command name passed in, or an exception from an
632 individual test runner.
[email protected]fbe29322013-07-09 09:03:26633 """
634
[email protected]d82f0252013-07-12 23:22:57635 # Check for extra arguments
636 if len(args) > 2:
637 option_parser.error('Unrecognized arguments: %s' % (' '.join(args[2:])))
638 return constants.ERROR_EXIT_CODE
639
[email protected]fbe29322013-07-09 09:03:26640 ProcessCommonOptions(options)
641
[email protected]fbe29322013-07-09 09:03:26642 if command == 'gtest':
[email protected]6bc1bda22013-07-19 22:08:37643 return _RunGTests(options, option_parser.error)
[email protected]fbe29322013-07-09 09:03:26644 elif command == 'instrumentation':
[email protected]6bc1bda22013-07-19 22:08:37645 return _RunInstrumentationTests(options, option_parser.error)
[email protected]fbe29322013-07-09 09:03:26646 elif command == 'uiautomator':
[email protected]6bc1bda22013-07-19 22:08:37647 return _RunUIAutomatorTests(options, option_parser.error)
[email protected]3dbdfa42013-08-08 01:08:14648 elif command == 'monkey':
649 return _RunMonkeyTests(options, option_parser.error)
[email protected]ec3170b2013-08-14 14:39:47650 elif command == 'perf':
651 return _RunPerfTests(options, option_parser.error)
[email protected]fbe29322013-07-09 09:03:26652 else:
[email protected]6bc1bda22013-07-19 22:08:37653 raise Exception('Unknown test type.')
[email protected]fbe29322013-07-09 09:03:26654
[email protected]fbe29322013-07-09 09:03:26655
656def HelpCommand(command, options, args, option_parser):
657 """Display help for a certain command, or overall help.
658
659 Args:
660 command: String indicating the command that was received to trigger
661 this function.
662 options: optparse options dictionary.
663 args: List of extra args from optparse.
664 option_parser: optparse.OptionParser object.
665
666 Returns:
667 Integer indicated exit code.
668 """
669 # If we don't have any args, display overall help
670 if len(args) < 3:
671 option_parser.print_help()
672 return 0
[email protected]d82f0252013-07-12 23:22:57673 # If we have too many args, print an error
674 if len(args) > 3:
675 option_parser.error('Unrecognized arguments: %s' % (' '.join(args[3:])))
676 return constants.ERROR_EXIT_CODE
[email protected]fbe29322013-07-09 09:03:26677
678 command = args[2]
679
680 if command not in VALID_COMMANDS:
681 option_parser.error('Unrecognized command.')
682
683 # Treat the help command as a special case. We don't care about showing a
684 # specific help page for itself.
685 if command == 'help':
686 option_parser.print_help()
687 return 0
688
689 VALID_COMMANDS[command].add_options_func(option_parser)
690 option_parser.usage = '%prog ' + command + ' [options]'
691 option_parser.command_list = None
692 option_parser.print_help()
693
694 return 0
695
696
697# Define a named tuple for the values in the VALID_COMMANDS dictionary so the
698# syntax is a bit prettier. The tuple is two functions: (add options, run
699# command).
700CommandFunctionTuple = collections.namedtuple(
701 'CommandFunctionTuple', ['add_options_func', 'run_command_func'])
702VALID_COMMANDS = {
703 'gtest': CommandFunctionTuple(AddGTestOptions, RunTestsCommand),
[email protected]fbe29322013-07-09 09:03:26704 'instrumentation': CommandFunctionTuple(
705 AddInstrumentationTestOptions, RunTestsCommand),
706 'uiautomator': CommandFunctionTuple(
707 AddUIAutomatorTestOptions, RunTestsCommand),
[email protected]3dbdfa42013-08-08 01:08:14708 'monkey': CommandFunctionTuple(
709 AddMonkeyTestOptions, RunTestsCommand),
[email protected]ec3170b2013-08-14 14:39:47710 'perf': CommandFunctionTuple(
711 AddPerfTestOptions, RunTestsCommand),
[email protected]fbe29322013-07-09 09:03:26712 'help': CommandFunctionTuple(lambda option_parser: None, HelpCommand)
713 }
714
715
716class CommandOptionParser(optparse.OptionParser):
717 """Wrapper class for OptionParser to help with listing commands."""
718
719 def __init__(self, *args, **kwargs):
720 self.command_list = kwargs.pop('command_list', [])
721 self.example = kwargs.pop('example', '')
722 optparse.OptionParser.__init__(self, *args, **kwargs)
723
724 #override
725 def get_usage(self):
726 normal_usage = optparse.OptionParser.get_usage(self)
727 command_list = self.get_command_list()
728 example = self.get_example()
729 return self.expand_prog_name(normal_usage + example + command_list)
730
731 #override
732 def get_command_list(self):
733 if self.command_list:
734 return '\nCommands:\n %s\n' % '\n '.join(sorted(self.command_list))
735 return ''
736
737 def get_example(self):
738 if self.example:
739 return '\nExample:\n %s\n' % self.example
740 return ''
741
[email protected]b3873892013-07-10 04:57:10742
[email protected]fbe29322013-07-09 09:03:26743def main(argv):
744 option_parser = CommandOptionParser(
745 usage='Usage: %prog <command> [options]',
746 command_list=VALID_COMMANDS.keys())
747
748 if len(argv) < 2 or argv[1] not in VALID_COMMANDS:
[email protected]f8513e52013-08-09 01:08:52749 option_parser.error('Invalid command.')
[email protected]fbe29322013-07-09 09:03:26750 command = argv[1]
751 VALID_COMMANDS[command].add_options_func(option_parser)
752 options, args = option_parser.parse_args(argv)
[email protected]b3873892013-07-10 04:57:10753 return VALID_COMMANDS[command].run_command_func(
[email protected]fbe29322013-07-09 09:03:26754 command, options, args, option_parser)
755
[email protected]fbe29322013-07-09 09:03:26756
757if __name__ == '__main__':
758 sys.exit(main(sys.argv))