Index: build/android/pylib/gtest/dispatch.py |
diff --git a/build/android/pylib/gtest/dispatch.py b/build/android/pylib/gtest/dispatch.py |
deleted file mode 100644 |
index 3dfb2620c3f74c4af6bfd7813194933031ac0af5..0000000000000000000000000000000000000000 |
--- a/build/android/pylib/gtest/dispatch.py |
+++ /dev/null |
@@ -1,359 +0,0 @@ |
-# Copyright (c) 2013 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. |
- |
-"""Dispatches GTests.""" |
- |
-import copy |
-import fnmatch |
-import glob |
-import logging |
-import os |
-import shutil |
- |
-from pylib import android_commands |
-from pylib import cmd_helper |
-from pylib import constants |
-from pylib import ports |
-from pylib.base import base_test_result |
-from pylib.base import shard |
-from pylib.utils import emulator |
-from pylib.utils import report_results |
-from pylib.utils import xvfb |
- |
-import gtest_config |
-import test_runner |
- |
- |
-# TODO(frankf): Add more test targets here after making sure we don't |
-# blow up the dependency size (and the world). |
-_ISOLATE_FILE_PATHS = { |
- 'base_unittests': 'base/base_unittests.isolate', |
- 'unit_tests': 'chrome/unit_tests.isolate', |
-} |
- |
-# Used for filtering large data deps at a finer grain than what's allowed in |
-# isolate files since pushing deps to devices is expensive. |
-# Wildcards are allowed. |
-_DEPS_EXCLUSION_LIST = [ |
- 'chrome/test/data/extensions/api_test', |
- 'chrome/test/data/extensions/secure_shell', |
- 'chrome/test/data/firefox*', |
- 'chrome/test/data/gpu', |
- 'chrome/test/data/image_decoding', |
- 'chrome/test/data/import', |
- 'chrome/test/data/page_cycler', |
- 'chrome/test/data/perf', |
- 'chrome/test/data/pyauto_private', |
- 'chrome/test/data/safari_import', |
- 'chrome/test/data/scroll', |
- 'chrome/test/data/third_party', |
- 'third_party/hunspell_dictionaries/*.dic', |
-] |
- |
-_ISOLATE_SCRIPT = os.path.join( |
- constants.DIR_SOURCE_ROOT, 'tools', 'swarm_client', 'isolate.py') |
- |
- |
-def _GenerateDepsDirUsingIsolate(test_suite, build_type): |
- """Generate the dependency dir for the test suite using isolate. |
- |
- Args: |
- test_suite: The test suite basename (e.g. base_unittests). |
- build_type: Release/Debug |
- |
- Returns: |
- If an isolate file exists, returns path to dependency dir on the host. |
- Otherwise, returns False. |
- """ |
- product_dir = os.path.join(cmd_helper.OutDirectory.get(), build_type) |
- assert os.path.isabs(product_dir) |
- isolate_rel_path = _ISOLATE_FILE_PATHS.get(test_suite) |
- if not isolate_rel_path: |
- return False |
- |
- isolate_abs_path = os.path.join(constants.DIR_SOURCE_ROOT, isolate_rel_path) |
- isolated_abs_path = os.path.join( |
- product_dir, '%s.isolated' % test_suite) |
- assert os.path.exists(isolate_abs_path) |
- deps_dir = os.path.join(product_dir, 'isolate_deps_dir') |
- if os.path.isdir(deps_dir): |
- shutil.rmtree(deps_dir) |
- isolate_cmd = [ |
- 'python', _ISOLATE_SCRIPT, |
- 'remap', |
- '--isolate', isolate_abs_path, |
- '--isolated', isolated_abs_path, |
- '-V', 'PRODUCT_DIR=%s' % product_dir, |
- '-V', 'OS=android', |
- '--outdir', deps_dir, |
- ] |
- assert not cmd_helper.RunCmd(isolate_cmd) |
- |
- # We're relying on the fact that timestamps are preserved |
- # by the remap command (hardlinked). Otherwise, all the data |
- # will be pushed to the device once we move to using time diff |
- # instead of md5sum. Perform a sanity check here. |
- for root, _, filenames in os.walk(deps_dir): |
- if filenames: |
- linked_file = os.path.join(root, filenames[0]) |
- orig_file = os.path.join( |
- constants.DIR_SOURCE_ROOT, |
- os.path.relpath(linked_file, deps_dir)) |
- if os.stat(linked_file).st_ino == os.stat(orig_file).st_ino: |
- break |
- else: |
- raise Exception('isolate remap command did not use hardlinks.') |
- |
- # Delete excluded files as defined by _DEPS_EXCLUSION_LIST. |
- old_cwd = os.getcwd() |
- try: |
- os.chdir(deps_dir) |
- excluded_paths = [x for y in _DEPS_EXCLUSION_LIST for x in glob.glob(y)] |
- if excluded_paths: |
- logging.info('Excluding the following from dependency list: %s', |
- excluded_paths) |
- for p in excluded_paths: |
- if os.path.isdir(p): |
- shutil.rmtree(p) |
- else: |
- os.remove(p) |
- finally: |
- os.chdir(old_cwd) |
- |
- # On Android, all pak files need to be in the top-level 'paks' directory. |
- paks_dir = os.path.join(deps_dir, 'paks') |
- os.mkdir(paks_dir) |
- for root, _, filenames in os.walk(os.path.join(deps_dir, 'out')): |
- for filename in fnmatch.filter(filenames, '*.pak'): |
- shutil.move(os.path.join(root, filename), paks_dir) |
- |
- # Move everything in PRODUCT_DIR to top level. |
- deps_product_dir = os.path.join(deps_dir, 'out', build_type) |
- if os.path.isdir(deps_product_dir): |
- for p in os.listdir(deps_product_dir): |
- shutil.move(os.path.join(deps_product_dir, p), deps_dir) |
- os.rmdir(deps_product_dir) |
- os.rmdir(os.path.join(deps_dir, 'out')) |
- |
- return deps_dir |
- |
- |
-def _FullyQualifiedTestSuites(exe, option_test_suite, build_type): |
- """Get a list of absolute paths to test suite targets. |
- |
- Args: |
- exe: if True, use the executable-based test runner. |
- option_test_suite: the test_suite specified as an option. |
- build_type: 'Release' or 'Debug'. |
- |
- Returns: |
- A list of tuples containing the suite and absolute path. |
- Ex. ('content_unittests', |
- '/tmp/chrome/src/out/Debug/content_unittests_apk/' |
- 'content_unittests-debug.apk') |
- |
- Raises: |
- Exception: If test suite not found. |
- """ |
- def GetQualifiedSuite(suite): |
- if suite.is_suite_exe: |
- relpath = suite.name |
- else: |
- # out/(Debug|Release)/$SUITE_apk/$SUITE-debug.apk |
- relpath = os.path.join(suite.name + '_apk', suite.name + '-debug.apk') |
- return suite.name, os.path.join(test_suite_dir, relpath) |
- |
- test_suite_dir = os.path.join(cmd_helper.OutDirectory.get(), build_type) |
- if option_test_suite: |
- all_test_suites = [gtest_config.Suite(exe, option_test_suite)] |
- else: |
- all_test_suites = gtest_config.STABLE_TEST_SUITES |
- |
- # List of tuples (suite_name, suite_path) |
- qualified_test_suites = map(GetQualifiedSuite, all_test_suites) |
- |
- for t, q in qualified_test_suites: |
- if not os.path.exists(q): |
- raise Exception('Test suite %s not found in %s.\n' |
- 'Supported test suites:\n %s\n' |
- 'Ensure it has been built.\n' % |
- (t, q, [s.name for s in gtest_config.STABLE_TEST_SUITES])) |
- return qualified_test_suites |
- |
- |
-def GetTestsFromDevice(runner): |
- """Get a list of tests from a device, excluding disabled tests. |
- |
- Args: |
- runner: a TestRunner. |
- Returns: |
- All non-disabled tests on the device. |
- """ |
- # The executable/apk needs to be copied before we can call GetAllTests. |
- runner.test_package.StripAndCopyExecutable() |
- all_tests = runner.test_package.GetAllTests() |
- # Only includes tests that do not have any match in the disabled list. |
- disabled_list = runner.GetDisabledTests() |
- return filter(lambda t: not any([fnmatch.fnmatch(t, disabled_pattern) |
- for disabled_pattern in disabled_list]), |
- all_tests) |
- |
- |
-def GetAllEnabledTests(runner_factory, devices): |
- """Get all enabled tests. |
- |
- Obtains a list of enabled tests from the test package on the device, |
- then filters it again using the disabled list on the host. |
- |
- Args: |
- runner_factory: callable that takes a devices and returns a TestRunner. |
- devices: list of devices. |
- |
- Returns: |
- List of all enabled tests. |
- |
- Raises: |
- Exception: If no devices available. |
- """ |
- for device in devices: |
- try: |
- logging.info('Obtaining tests from %s', device) |
- runner = runner_factory(device, 0) |
- return GetTestsFromDevice(runner) |
- except Exception as e: |
- logging.warning('Failed obtaining tests from %s with exception: %s', |
- device, e) |
- raise Exception('No device available to get the list of tests.') |
- |
- |
-def _RunATestSuite(options, suite_name): |
- """Run a single test suite. |
- |
- Helper for Dispatch() to allow stop/restart of the emulator across |
- test bundles. If using the emulator, we start it on entry and stop |
- it on exit. |
- |
- Args: |
- options: options for running the tests. |
- suite_name: name of the test suite being run. |
- |
- Returns: |
- A tuple of (base_test_result.TestRunResult object, exit code). |
- |
- Raises: |
- Exception: For various reasons including device failure or failing to reset |
- the test server port. |
- """ |
- attached_devices = [] |
- buildbot_emulators = [] |
- |
- if options.use_emulator: |
- buildbot_emulators = emulator.LaunchEmulators(options.emulator_count, |
- options.abi, |
- wait_for_boot=True) |
- attached_devices = [e.device for e in buildbot_emulators] |
- elif options.test_device: |
- attached_devices = [options.test_device] |
- else: |
- attached_devices = android_commands.GetAttachedDevices() |
- |
- if not attached_devices: |
- raise Exception('A device must be attached and online.') |
- |
- # Reset the test port allocation. It's important to do it before starting |
- # to dispatch any tests. |
- if not ports.ResetTestServerPortAllocation(): |
- raise Exception('Failed to reset test server port.') |
- |
- deps_dir = _GenerateDepsDirUsingIsolate(suite_name, options.build_type) |
- |
- # Constructs a new TestRunner with the current options. |
- def RunnerFactory(device, shard_index): |
- return test_runner.TestRunner( |
- device, |
- options.test_suite, |
- options.test_arguments, |
- options.timeout, |
- options.cleanup_test_files, |
- options.tool, |
- options.build_type, |
- options.webkit, |
- options.push_deps, |
- constants.GTEST_TEST_PACKAGE_NAME, |
- constants.GTEST_TEST_ACTIVITY_NAME, |
- constants.GTEST_COMMAND_LINE_FILE, |
- deps_dir=deps_dir) |
- |
- # Get tests and split them up based on the number of devices. |
- if options.test_filter: |
- all_tests = [t for t in options.test_filter.split(':') if t] |
- else: |
- all_tests = GetAllEnabledTests(RunnerFactory, attached_devices) |
- num_devices = len(attached_devices) |
- tests = [':'.join(all_tests[i::num_devices]) for i in xrange(num_devices)] |
- tests = [t for t in tests if t] |
- |
- # Run tests. |
- test_results, exit_code = shard.ShardAndRunTests( |
- RunnerFactory, attached_devices, tests, options.build_type, |
- test_timeout=None, num_retries=options.num_retries) |
- |
- report_results.LogFull( |
- results=test_results, |
- test_type='Unit test', |
- test_package=suite_name, |
- build_type=options.build_type, |
- flakiness_server=options.flakiness_dashboard_server) |
- |
- for buildbot_emulator in buildbot_emulators: |
- buildbot_emulator.Shutdown() |
- |
- return (test_results, exit_code) |
- |
- |
-def _ListTestSuites(): |
- """Display a list of available test suites.""" |
- print 'Available test suites are:' |
- for test_suite in gtest_config.STABLE_TEST_SUITES: |
- print test_suite |
- |
- |
-def Dispatch(options): |
- """Dispatches the tests, sharding if possible. |
- |
- If options.use_emulator is True, all tests will be run in new emulator |
- instance. |
- |
- Args: |
- options: options for running the tests. |
- |
- Returns: |
- base_test_result.TestRunResults object with the results of running the tests |
- """ |
- results = base_test_result.TestRunResults() |
- |
- if options.test_suite == 'help': |
- _ListTestSuites() |
- return (results, 0) |
- |
- if options.use_xvfb: |
- framebuffer = xvfb.Xvfb() |
- framebuffer.Start() |
- |
- all_test_suites = _FullyQualifiedTestSuites(options.exe, options.test_suite, |
- options.build_type) |
- exit_code = 0 |
- for suite_name, suite_path in all_test_suites: |
- # Give each test suite its own copy of options. |
- test_options = copy.deepcopy(options) |
- test_options.test_suite = suite_path |
- test_results, test_exit_code = _RunATestSuite(test_options, suite_name) |
- results.AddTestRunResults(test_results) |
- if test_exit_code and exit_code != constants.ERROR_EXIT_CODE: |
- exit_code = test_exit_code |
- |
- if options.use_xvfb: |
- framebuffer.Stop() |
- |
- return (results, exit_code) |