OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # | 2 # |
3 # Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 4 # Use of this source code is governed by a BSD-style license that can be |
5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
6 | 6 |
7 """Runs all types of tests from one unified interface. | 7 """Runs all types of tests from one unified interface. |
8 | 8 |
9 TODO(gkanwar): | 9 TODO(gkanwar): |
10 * Add options to run Monkey tests. | 10 * Add options to run Monkey tests. |
11 """ | 11 """ |
12 | 12 |
13 import collections | 13 import collections |
14 import optparse | 14 import optparse |
15 import os | 15 import os |
16 import shutil | 16 import shutil |
17 import sys | 17 import sys |
18 | 18 |
19 from pylib import constants | 19 from pylib import constants |
20 from pylib import ports | 20 from pylib import ports |
21 from pylib.base import base_test_result | 21 from pylib.base import base_test_result |
22 from pylib.base import test_dispatcher | 22 from pylib.base import test_dispatcher |
23 from pylib.gtest import gtest_config | 23 from pylib.gtest import gtest_config |
24 from pylib.gtest import setup as gtest_setup | 24 from pylib.gtest import setup as gtest_setup |
25 from pylib.gtest import test_options as gtest_test_options | 25 from pylib.gtest import test_options as gtest_test_options |
26 from pylib.host_driven import run_python_tests as python_dispatch | 26 from pylib.host_driven import setup as host_driven_setup |
27 from pylib.instrumentation import setup as instrumentation_setup | 27 from pylib.instrumentation import setup as instrumentation_setup |
28 from pylib.instrumentation import test_options as instrumentation_test_options | 28 from pylib.instrumentation import test_options as instrumentation_test_options |
29 from pylib.uiautomator import setup as uiautomator_setup | 29 from pylib.uiautomator import setup as uiautomator_setup |
30 from pylib.uiautomator import test_options as uiautomator_test_options | 30 from pylib.uiautomator import test_options as uiautomator_test_options |
31 from pylib.utils import report_results | 31 from pylib.utils import report_results |
32 from pylib.utils import run_tests_helper | 32 from pylib.utils import run_tests_helper |
33 | 33 |
34 | 34 |
35 _SDK_OUT_DIR = os.path.join(constants.DIR_SOURCE_ROOT, 'out') | 35 _SDK_OUT_DIR = os.path.join(constants.DIR_SOURCE_ROOT, 'out') |
36 | 36 |
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
152 option_parser.add_option( | 152 option_parser.add_option( |
153 '-A', '--annotation', dest='annotation_str', | 153 '-A', '--annotation', dest='annotation_str', |
154 help=('Comma-separated list of annotations. Run only tests with any of ' | 154 help=('Comma-separated list of annotations. Run only tests with any of ' |
155 'the given annotations. An annotation can be either a key or a ' | 155 'the given annotations. An annotation can be either a key or a ' |
156 'key-values pair. A test that has no annotation is considered ' | 156 'key-values pair. A test that has no annotation is considered ' |
157 '"SmallTest".')) | 157 '"SmallTest".')) |
158 option_parser.add_option( | 158 option_parser.add_option( |
159 '-E', '--exclude-annotation', dest='exclude_annotation_str', | 159 '-E', '--exclude-annotation', dest='exclude_annotation_str', |
160 help=('Comma-separated list of annotations. Exclude tests with these ' | 160 help=('Comma-separated list of annotations. Exclude tests with these ' |
161 'annotations.')) | 161 'annotations.')) |
162 option_parser.add_option('-j', '--java_only', action='store_true', | |
163 default=False, help='Run only the Java tests.') | |
164 option_parser.add_option('-p', '--python_only', action='store_true', | |
165 default=False, | |
166 help='Run only the host-driven tests.') | |
167 option_parser.add_option('--screenshot', dest='screenshot_failures', | 162 option_parser.add_option('--screenshot', dest='screenshot_failures', |
168 action='store_true', | 163 action='store_true', |
169 help='Capture screenshots of test failures') | 164 help='Capture screenshots of test failures') |
170 option_parser.add_option('--save-perf-json', action='store_true', | 165 option_parser.add_option('--save-perf-json', action='store_true', |
171 help='Saves the JSON file for each UI Perf test.') | 166 help='Saves the JSON file for each UI Perf test.') |
172 option_parser.add_option('--official-build', help='Run official build tests.') | 167 option_parser.add_option('--official-build', help='Run official build tests.') |
173 option_parser.add_option('--python_test_root', | |
174 help='Root of the host-driven tests.') | |
175 option_parser.add_option('--keep_test_server_ports', | 168 option_parser.add_option('--keep_test_server_ports', |
176 action='store_true', | 169 action='store_true', |
177 help=('Indicates the test server ports must be ' | 170 help=('Indicates the test server ports must be ' |
178 'kept. When this is run via a sharder ' | 171 'kept. When this is run via a sharder ' |
179 'the test server ports should be kept and ' | 172 'the test server ports should be kept and ' |
180 'should not be reset.')) | 173 'should not be reset.')) |
181 # TODO(gkanwar): This option is deprecated. Remove it in the future. | 174 # TODO(gkanwar): This option is deprecated. Remove it in the future. |
182 option_parser.add_option('--disable_assertions', action='store_true', | 175 option_parser.add_option('--disable_assertions', action='store_true', |
183 help=('(DEPRECATED) Run with java assertions ' | 176 help=('(DEPRECATED) Run with java assertions ' |
184 'disabled.')) | 177 'disabled.')) |
185 option_parser.add_option('--test_data', action='append', default=[], | 178 option_parser.add_option('--test_data', action='append', default=[], |
186 help=('Each instance defines a directory of test ' | 179 help=('Each instance defines a directory of test ' |
187 'data that should be copied to the target(s) ' | 180 'data that should be copied to the target(s) ' |
188 'before running the tests. The argument ' | 181 'before running the tests. The argument ' |
189 'should be of the form <target>:<source>, ' | 182 'should be of the form <target>:<source>, ' |
190 '<target> is relative to the device data' | 183 '<target> is relative to the device data' |
191 'directory, and <source> is relative to the ' | 184 'directory, and <source> is relative to the ' |
192 'chromium build directory.')) | 185 'chromium build directory.')) |
193 | 186 |
194 | 187 |
195 def ProcessJavaTestOptions(options, error_func): | 188 def ProcessJavaTestOptions(options, error_func): |
196 """Processes options/arguments and populates |options| with defaults.""" | 189 """Processes options/arguments and populates |options| with defaults.""" |
197 | 190 |
198 if options.java_only and options.python_only: | |
199 error_func('Options java_only (-j) and python_only (-p) ' | |
200 'are mutually exclusive.') | |
201 options.run_java_tests = True | |
202 options.run_python_tests = True | |
203 if options.java_only: | |
204 options.run_python_tests = False | |
205 elif options.python_only: | |
206 options.run_java_tests = False | |
207 | |
208 if not options.python_test_root: | |
209 options.run_python_tests = False | |
210 | |
211 if options.annotation_str: | 191 if options.annotation_str: |
212 options.annotations = options.annotation_str.split(',') | 192 options.annotations = options.annotation_str.split(',') |
213 elif options.test_filter: | 193 elif options.test_filter: |
214 options.annotations = [] | 194 options.annotations = [] |
215 else: | 195 else: |
216 options.annotations = ['Smoke', 'SmallTest', 'MediumTest', 'LargeTest', | 196 options.annotations = ['Smoke', 'SmallTest', 'MediumTest', 'LargeTest', |
217 'EnormousTest'] | 197 'EnormousTest'] |
218 | 198 |
219 if options.exclude_annotation_str: | 199 if options.exclude_annotation_str: |
220 options.exclude_annotations = options.exclude_annotation_str.split(',') | 200 options.exclude_annotations = options.exclude_annotation_str.split(',') |
221 else: | 201 else: |
222 options.exclude_annotations = [] | 202 options.exclude_annotations = [] |
223 | 203 |
224 if not options.keep_test_server_ports: | 204 if not options.keep_test_server_ports: |
225 if not ports.ResetTestServerPortAllocation(): | 205 if not ports.ResetTestServerPortAllocation(): |
226 raise Exception('Failed to reset test server port.') | 206 raise Exception('Failed to reset test server port.') |
227 | 207 |
228 | 208 |
229 def AddInstrumentationTestOptions(option_parser): | 209 def AddInstrumentationTestOptions(option_parser): |
230 """Adds Instrumentation test options to |option_parser|.""" | 210 """Adds Instrumentation test options to |option_parser|.""" |
231 | 211 |
232 option_parser.usage = '%prog instrumentation [options]' | 212 option_parser.usage = '%prog instrumentation [options]' |
233 option_parser.command_list = [] | 213 option_parser.command_list = [] |
234 option_parser.example = ('%prog instrumentation ' | 214 option_parser.example = ('%prog instrumentation ' |
235 '--test-apk=ChromiumTestShellTest') | 215 '--test-apk=ChromiumTestShellTest') |
236 | 216 |
237 AddJavaTestOptions(option_parser) | 217 AddJavaTestOptions(option_parser) |
238 AddCommonOptions(option_parser) | 218 AddCommonOptions(option_parser) |
239 | 219 |
| 220 option_parser.add_option('-j', '--java_only', action='store_true', |
| 221 default=False, help='Run only the Java tests.') |
| 222 option_parser.add_option('-p', '--python_only', action='store_true', |
| 223 default=False, |
| 224 help='Run only the host-driven tests.') |
| 225 option_parser.add_option('--python_test_root', |
| 226 help='Root of the host-driven tests.') |
240 option_parser.add_option('-w', '--wait_debugger', dest='wait_for_debugger', | 227 option_parser.add_option('-w', '--wait_debugger', dest='wait_for_debugger', |
241 action='store_true', | 228 action='store_true', |
242 help='Wait for debugger.') | 229 help='Wait for debugger.') |
243 #TODO(craigdh): Remove option once -I is no longer passed downstream. | 230 #TODO(craigdh): Remove option once -I is no longer passed downstream. |
244 option_parser.add_option('-I', dest='install_apk', action='store_true', | 231 option_parser.add_option('-I', dest='install_apk', action='store_true', |
245 help='(DEPRECATED) Install the test apk.') | 232 help='(DEPRECATED) Install the test apk.') |
246 option_parser.add_option( | 233 option_parser.add_option( |
247 '--test-apk', dest='test_apk', | 234 '--test-apk', dest='test_apk', |
248 help=('The name of the apk containing the tests ' | 235 help=('The name of the apk containing the tests ' |
249 '(without the .apk extension; e.g. "ContentShellTest"). ' | 236 '(without the .apk extension; e.g. "ContentShellTest"). ' |
250 'Alternatively, this can be a full path to the apk.')) | 237 'Alternatively, this can be a full path to the apk.')) |
251 | 238 |
252 | 239 |
253 def ProcessInstrumentationOptions(options, error_func): | 240 def ProcessInstrumentationOptions(options, error_func): |
254 """Processes options/arguments and populate |options| with defaults. | 241 """Processes options/arguments and populate |options| with defaults. |
255 | 242 |
256 Args: | 243 Args: |
257 options: optparse.Options object. | 244 options: optparse.Options object. |
258 error_func: Function to call with the error message in case of an error. | 245 error_func: Function to call with the error message in case of an error. |
259 | 246 |
260 Returns: | 247 Returns: |
261 An InstrumentationOptions named tuple which contains all options relevant to | 248 An InstrumentationOptions named tuple which contains all options relevant to |
262 instrumentation tests. | 249 instrumentation tests. |
263 """ | 250 """ |
264 | 251 |
265 ProcessJavaTestOptions(options, error_func) | 252 ProcessJavaTestOptions(options, error_func) |
266 | 253 |
| 254 if options.java_only and options.python_only: |
| 255 error_func('Options java_only (-j) and python_only (-p) ' |
| 256 'are mutually exclusive.') |
| 257 options.run_java_tests = True |
| 258 options.run_python_tests = True |
| 259 if options.java_only: |
| 260 options.run_python_tests = False |
| 261 elif options.python_only: |
| 262 options.run_java_tests = False |
| 263 |
| 264 if not options.python_test_root: |
| 265 options.run_python_tests = False |
| 266 |
267 if not options.test_apk: | 267 if not options.test_apk: |
268 error_func('--test-apk must be specified.') | 268 error_func('--test-apk must be specified.') |
269 | 269 |
270 if os.path.exists(options.test_apk): | 270 if os.path.exists(options.test_apk): |
271 # The APK is fully qualified, assume the JAR lives along side. | 271 # The APK is fully qualified, assume the JAR lives along side. |
272 options.test_apk_path = options.test_apk | 272 options.test_apk_path = options.test_apk |
273 options.test_apk_jar_path = (os.path.splitext(options.test_apk_path)[0] + | 273 options.test_apk_jar_path = (os.path.splitext(options.test_apk_path)[0] + |
274 '.jar') | 274 '.jar') |
275 else: | 275 else: |
276 options.test_apk_path = os.path.join(_SDK_OUT_DIR, | 276 options.test_apk_path = os.path.join(_SDK_OUT_DIR, |
(...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
424 tests, runner_factory, options.wait_for_debugger, | 424 tests, runner_factory, options.wait_for_debugger, |
425 options.test_device, | 425 options.test_device, |
426 shard=True, | 426 shard=True, |
427 build_type=options.build_type, | 427 build_type=options.build_type, |
428 test_timeout=None, | 428 test_timeout=None, |
429 num_retries=options.num_retries) | 429 num_retries=options.num_retries) |
430 | 430 |
431 results.AddTestRunResults(test_results) | 431 results.AddTestRunResults(test_results) |
432 | 432 |
433 if options.run_python_tests: | 433 if options.run_python_tests: |
434 test_results, test_exit_code = ( | 434 runner_factory, tests = host_driven_setup.InstrumentationSetup( |
435 python_dispatch.DispatchPythonTests(options)) | 435 options.python_test_root, options.official_build, |
| 436 instrumentation_options) |
| 437 |
| 438 test_results, test_exit_code = test_dispatcher.RunTests( |
| 439 tests, runner_factory, False, |
| 440 options.test_device, |
| 441 shard=True, |
| 442 build_type=options.build_type, |
| 443 test_timeout=None, |
| 444 num_retries=options.num_retries) |
436 | 445 |
437 results.AddTestRunResults(test_results) | 446 results.AddTestRunResults(test_results) |
438 | 447 |
439 # Only allow exit code escalation | 448 # Only allow exit code escalation |
440 if test_exit_code and exit_code != constants.ERROR_EXIT_CODE: | 449 if test_exit_code and exit_code != constants.ERROR_EXIT_CODE: |
441 exit_code = test_exit_code | 450 exit_code = test_exit_code |
442 | 451 |
443 report_results.LogFull( | 452 report_results.LogFull( |
444 results=results, | 453 results=results, |
445 test_type='Instrumentation', | 454 test_type='Instrumentation', |
446 test_package=os.path.basename(options.test_apk), | 455 test_package=os.path.basename(options.test_apk), |
447 annotation=options.annotations, | 456 annotation=options.annotations, |
448 build_type=options.build_type, | 457 build_type=options.build_type, |
449 flakiness_server=options.flakiness_dashboard_server) | 458 flakiness_server=options.flakiness_dashboard_server) |
450 | 459 |
451 return exit_code | 460 return exit_code |
452 | 461 |
453 | 462 |
454 def _RunUIAutomatorTests(options, error_func): | 463 def _RunUIAutomatorTests(options, error_func): |
455 """Subcommand of RunTestsCommands which runs uiautomator tests.""" | 464 """Subcommand of RunTestsCommands which runs uiautomator tests.""" |
456 uiautomator_options = ProcessUIAutomatorOptions(options, error_func) | 465 uiautomator_options = ProcessUIAutomatorOptions(options, error_func) |
457 | 466 |
458 results = base_test_result.TestRunResults() | 467 results = base_test_result.TestRunResults() |
459 exit_code = 0 | 468 exit_code = 0 |
460 | 469 |
461 if options.run_java_tests: | 470 runner_factory, tests = uiautomator_setup.Setup(uiautomator_options) |
462 runner_factory, tests = uiautomator_setup.Setup(uiautomator_options) | |
463 | 471 |
464 test_results, exit_code = test_dispatcher.RunTests( | 472 results, exit_code = test_dispatcher.RunTests( |
465 tests, runner_factory, False, options.test_device, | 473 tests, runner_factory, False, options.test_device, |
466 shard=True, | 474 shard=True, |
467 build_type=options.build_type, | 475 build_type=options.build_type, |
468 test_timeout=None, | 476 test_timeout=None, |
469 num_retries=options.num_retries) | 477 num_retries=options.num_retries) |
470 | |
471 results.AddTestRunResults(test_results) | |
472 | |
473 if options.run_python_tests: | |
474 test_results, test_exit_code = ( | |
475 python_dispatch.DispatchPythonTests(options)) | |
476 | |
477 results.AddTestRunResults(test_results) | |
478 | |
479 # Only allow exit code escalation | |
480 if test_exit_code and exit_code != constants.ERROR_EXIT_CODE: | |
481 exit_code = test_exit_code | |
482 | 478 |
483 report_results.LogFull( | 479 report_results.LogFull( |
484 results=results, | 480 results=results, |
485 test_type='UIAutomator', | 481 test_type='UIAutomator', |
486 test_package=os.path.basename(options.test_jar), | 482 test_package=os.path.basename(options.test_jar), |
487 annotation=options.annotations, | 483 annotation=options.annotations, |
488 build_type=options.build_type, | 484 build_type=options.build_type, |
489 flakiness_server=options.flakiness_dashboard_server) | 485 flakiness_server=options.flakiness_dashboard_server) |
490 | 486 |
491 return exit_code | 487 return exit_code |
(...skipping 26 matching lines...) Expand all Loading... |
518 | 514 |
519 if command == 'gtest': | 515 if command == 'gtest': |
520 return _RunGTests(options, option_parser.error) | 516 return _RunGTests(options, option_parser.error) |
521 elif command == 'instrumentation': | 517 elif command == 'instrumentation': |
522 return _RunInstrumentationTests(options, option_parser.error) | 518 return _RunInstrumentationTests(options, option_parser.error) |
523 elif command == 'uiautomator': | 519 elif command == 'uiautomator': |
524 return _RunUIAutomatorTests(options, option_parser.error) | 520 return _RunUIAutomatorTests(options, option_parser.error) |
525 else: | 521 else: |
526 raise Exception('Unknown test type.') | 522 raise Exception('Unknown test type.') |
527 | 523 |
528 return exit_code | |
529 | |
530 | 524 |
531 def HelpCommand(command, options, args, option_parser): | 525 def HelpCommand(command, options, args, option_parser): |
532 """Display help for a certain command, or overall help. | 526 """Display help for a certain command, or overall help. |
533 | 527 |
534 Args: | 528 Args: |
535 command: String indicating the command that was received to trigger | 529 command: String indicating the command that was received to trigger |
536 this function. | 530 this function. |
537 options: optparse options dictionary. | 531 options: optparse options dictionary. |
538 args: List of extra args from optparse. | 532 args: List of extra args from optparse. |
539 option_parser: optparse.OptionParser object. | 533 option_parser: optparse.OptionParser object. |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
621 return 0 | 615 return 0 |
622 command = argv[1] | 616 command = argv[1] |
623 VALID_COMMANDS[command].add_options_func(option_parser) | 617 VALID_COMMANDS[command].add_options_func(option_parser) |
624 options, args = option_parser.parse_args(argv) | 618 options, args = option_parser.parse_args(argv) |
625 return VALID_COMMANDS[command].run_command_func( | 619 return VALID_COMMANDS[command].run_command_func( |
626 command, options, args, option_parser) | 620 command, options, args, option_parser) |
627 | 621 |
628 | 622 |
629 if __name__ == '__main__': | 623 if __name__ == '__main__': |
630 sys.exit(main(sys.argv)) | 624 sys.exit(main(sys.argv)) |
OLD | NEW |