| #!/usr/bin/env python |
| # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """A tool to run a chrome test executable directly, or in isolated mode. |
| |
| TODO(maruel): This script technically needs to die and be replaced by running |
| all the tests always isolated even when not run on Swarming. This will take a |
| while. |
| """ |
| |
| import logging |
| import optparse |
| import os |
| import subprocess |
| import sys |
| |
| |
| USAGE = ('%s [options] /full/path/to/test.exe -- [original test command]' % |
| os.path.basename(sys.argv[0])) |
| |
| LINUX_ISOLATE_ENABLED_TESTS = set(( |
| 'base_unittests', |
| 'browser_tests', |
| 'interactive_ui_tests', |
| 'net_unittests', |
| 'unit_tests', |
| )) |
| |
| # TODO(maruel): Not enabled because of lack of XCode support and missing |
| # dependencies for more complex tests. |
| MAC_ISOLATE_ENABLED_TESTS = set() |
| |
| WIN_ISOLATE_ENABLED_TESTS = set(( |
| 'base_unittests', |
| 'browser_tests', |
| 'interactive_ui_tests', |
| 'net_unittests', |
| 'unit_tests', |
| )) |
| |
| ISOLATE_ENABLED_BUILDERS = { |
| # CI linux |
| 'Linux Tests': LINUX_ISOLATE_ENABLED_TESTS, |
| # CI mac |
| 'Mac10.6 Tests (1)': MAC_ISOLATE_ENABLED_TESTS, |
| 'Mac10.7 Tests (1)': MAC_ISOLATE_ENABLED_TESTS, |
| # CI win |
| 'Win7 Tests (1)': WIN_ISOLATE_ENABLED_TESTS, |
| 'Win10 Tests x64': WIN_ISOLATE_ENABLED_TESTS, |
| |
| # Try Server |
| 'linux_rel': LINUX_ISOLATE_ENABLED_TESTS, |
| 'mac_rel': MAC_ISOLATE_ENABLED_TESTS, |
| 'win_rel': WIN_ISOLATE_ENABLED_TESTS, |
| } |
| |
| |
| def should_run_as_isolated(builder_name, test_name): |
| logging.info('should_run_as_isolated(%s, %s)' % (builder_name, test_name)) |
| return test_name in ISOLATE_ENABLED_BUILDERS.get(builder_name, []) |
| |
| |
| def run_command(command): |
| """Inspired from chromium_utils.py's RunCommand().""" |
| print '\n' + subprocess.list2cmdline(command) |
| sys.stdout.flush() |
| sys.stderr.flush() |
| return subprocess.call(command) |
| |
| |
| def run_test_isolated(isolate_script, test_exe, original_command): |
| """Runs the test under isolate.py run. |
| |
| It compensates for discrepancies between sharding_supervisor.py arguments and |
| run_test_cases.py arguments. |
| |
| The isolated file must be alongside the test executable, with the same |
| name and the .isolated extension. |
| """ |
| isolated_file = os.path.splitext(test_exe)[0] + '.isolated' |
| |
| if not os.path.exists(isolated_file): |
| logging.error('No isolate file %s', isolated_file) |
| return 1 |
| |
| isolate_command = [sys.executable, isolate_script, |
| 'run', '--isolated', isolated_file, |
| # Print info log lines, so isolate.py prints the path to |
| # the binary it's about to run, http://crbug.com/311625 |
| '-v'] |
| |
| # Start setting the test specific options. |
| isolate_command.append('--') |
| isolate_command.append('--no-cr') |
| original_command = original_command[:] |
| while original_command: |
| item = original_command.pop(0) |
| if item == '--total-slave': |
| isolate_command.extend(['--shards', original_command.pop(0)]) |
| elif item == '--slave-index': |
| isolate_command.extend(['--index', original_command.pop(0)]) |
| elif item.startswith(('--gtest_filter', |
| '--gtest_output', |
| '--test-launcher')): |
| isolate_command.append(item) |
| |
| return run_command(isolate_command) |
| |
| |
| def main(argv): |
| option_parser = optparse.OptionParser(USAGE) |
| option_parser.add_option('--test_name', default='', |
| help='The name of the test') |
| option_parser.add_option('--builder_name', default='', |
| help='The name of the builder that created this' |
| 'test') |
| option_parser.add_option('--checkout_dir', |
| help='Checkout directory, used to locate the ' |
| 'swarm_client scripts.') |
| option_parser.add_option('-f', '--force-isolated', action='store_true', |
| help='Force test to run isolated. By default only ' |
| 'white listed builders and tests are run isolated.') |
| option_parser.add_option('-v', '--verbose', action='count', default=0, |
| help='Use to increase log verbosity. Can be passed ' |
| 'in multiple times for more detailed logs.') |
| |
| options, args = option_parser.parse_args(argv) |
| |
| test_exe = args[0] |
| original_command = args[1:] |
| |
| # Initialize logging. |
| level = [logging.ERROR, logging.INFO, logging.DEBUG][min(2, options.verbose)] |
| logging.basicConfig(level=level, |
| format='%(asctime)s %(filename)s:%(lineno)-3d' |
| ' %(levelname)s %(message)s', |
| datefmt='%y%m%d %H:%M:%S') |
| |
| if (options.force_isolated or |
| should_run_as_isolated(options.builder_name, options.test_name)): |
| logging.info('Running test in isolate mode') |
| # Search first in swarming_client |
| isolate_script = os.path.join(options.checkout_dir, 'src', 'tools', |
| 'swarming_client', 'isolate.py') |
| |
| return run_test_isolated(isolate_script, test_exe, original_command) |
| else: |
| logging.info('Running test normally') |
| return run_command(original_command) |
| |
| |
| if '__main__' == __name__: |
| sys.exit(main(None)) |