OLD | NEW |
1 # Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2013 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """Dispatches GTests.""" | 5 """Dispatches GTests.""" |
6 | 6 |
7 import copy | 7 import copy |
8 import fnmatch | 8 import fnmatch |
| 9 import glob |
9 import logging | 10 import logging |
10 import os | 11 import os |
| 12 import shutil |
11 | 13 |
12 from pylib import android_commands | 14 from pylib import android_commands |
13 from pylib import cmd_helper | 15 from pylib import cmd_helper |
14 from pylib import constants | 16 from pylib import constants |
15 from pylib import ports | 17 from pylib import ports |
16 from pylib.base import base_test_result | 18 from pylib.base import base_test_result |
17 from pylib.base import shard | 19 from pylib.base import shard |
18 from pylib.utils import emulator | 20 from pylib.utils import emulator |
19 from pylib.utils import report_results | 21 from pylib.utils import report_results |
20 from pylib.utils import xvfb | 22 from pylib.utils import xvfb |
21 | 23 |
22 import gtest_config | 24 import gtest_config |
23 import test_runner | 25 import test_runner |
24 | 26 |
25 | 27 |
| 28 # TODO(frankf): Add more test targets here after making sure we don't |
| 29 # blow up the dependency size (and the world). |
| 30 _ISOLATE_FILE_PATHS = { |
| 31 'base_unittests': 'base/base_unittests.isolate', |
| 32 'unit_tests': 'chrome/unit_tests.isolate', |
| 33 } |
| 34 |
| 35 # Used for filtering large data deps at a finer grain than what's allowed in |
| 36 # isolate files since pushing deps to devices is expensive. |
| 37 # Wildcards are allowed. |
| 38 _DEPS_EXCLUSION_LIST = [ |
| 39 'chrome/test/data/extensions/api_test', |
| 40 'chrome/test/data/extensions/secure_shell', |
| 41 'chrome/test/data/firefox*', |
| 42 'chrome/test/data/gpu', |
| 43 'chrome/test/data/image_decoding', |
| 44 'chrome/test/data/import', |
| 45 'chrome/test/data/page_cycler', |
| 46 'chrome/test/data/perf', |
| 47 'chrome/test/data/pyauto_private', |
| 48 'chrome/test/data/safari_import', |
| 49 'chrome/test/data/scroll', |
| 50 'chrome/test/data/third_party', |
| 51 'third_party/hunspell_dictionaries/*.dic', |
| 52 ] |
| 53 |
| 54 _ISOLATE_SCRIPT = os.path.join( |
| 55 constants.DIR_SOURCE_ROOT, 'tools', 'swarm_client', 'isolate.py') |
| 56 |
| 57 |
| 58 def _GenerateDepsDirUsingIsolate(test_suite, build_type): |
| 59 """Generate the dependency dir for the test suite using isolate. |
| 60 |
| 61 Args: |
| 62 test_suite: The test suite basename (e.g. base_unittests). |
| 63 build_type: Release/Debug |
| 64 |
| 65 Returns: |
| 66 If an isolate file exists, returns path to dependency dir on the host. |
| 67 Otherwise, returns False. |
| 68 """ |
| 69 product_dir = os.path.join(cmd_helper.OutDirectory.get(), build_type) |
| 70 assert os.path.isabs(product_dir) |
| 71 isolate_rel_path = _ISOLATE_FILE_PATHS.get(test_suite) |
| 72 if not isolate_rel_path: |
| 73 return False |
| 74 |
| 75 isolate_abs_path = os.path.join(constants.DIR_SOURCE_ROOT, isolate_rel_path) |
| 76 isolated_abs_path = os.path.join( |
| 77 product_dir, '%s.isolated' % test_suite) |
| 78 assert os.path.exists(isolate_abs_path) |
| 79 deps_dir = os.path.join(product_dir, 'isolate_deps_dir') |
| 80 if os.path.isdir(deps_dir): |
| 81 shutil.rmtree(deps_dir) |
| 82 isolate_cmd = [ |
| 83 'python', _ISOLATE_SCRIPT, |
| 84 'remap', |
| 85 '--isolate', isolate_abs_path, |
| 86 '--isolated', isolated_abs_path, |
| 87 '-V', 'PRODUCT_DIR=%s' % product_dir, |
| 88 '-V', 'OS=android', |
| 89 '--outdir', deps_dir, |
| 90 ] |
| 91 assert not cmd_helper.RunCmd(isolate_cmd) |
| 92 |
| 93 # We're relying on the fact that timestamps are preserved |
| 94 # by the remap command (hardlinked). Otherwise, all the data |
| 95 # will be pushed to the device once we move to using time diff |
| 96 # instead of md5sum. Perform a sanity check here. |
| 97 for root, _, filenames in os.walk(deps_dir): |
| 98 if filenames: |
| 99 linked_file = os.path.join(root, filenames[0]) |
| 100 orig_file = os.path.join( |
| 101 constants.DIR_SOURCE_ROOT, |
| 102 os.path.relpath(linked_file, deps_dir)) |
| 103 if os.stat(linked_file).st_ino == os.stat(orig_file).st_ino: |
| 104 break |
| 105 else: |
| 106 raise Exception('isolate remap command did not use hardlinks.') |
| 107 |
| 108 # Delete excluded files as defined by _DEPS_EXCLUSION_LIST. |
| 109 old_cwd = os.getcwd() |
| 110 try: |
| 111 os.chdir(deps_dir) |
| 112 excluded_paths = [x for y in _DEPS_EXCLUSION_LIST for x in glob.glob(y)] |
| 113 if excluded_paths: |
| 114 logging.info('Excluding the following from dependency list: %s', |
| 115 excluded_paths) |
| 116 for p in excluded_paths: |
| 117 if os.path.isdir(p): |
| 118 shutil.rmtree(p) |
| 119 else: |
| 120 os.remove(p) |
| 121 finally: |
| 122 os.chdir(old_cwd) |
| 123 |
| 124 # On Android, all pak files need to be in the top-level 'paks' directory. |
| 125 paks_dir = os.path.join(deps_dir, 'paks') |
| 126 os.mkdir(paks_dir) |
| 127 for root, _, filenames in os.walk(os.path.join(deps_dir, 'out')): |
| 128 for filename in fnmatch.filter(filenames, '*.pak'): |
| 129 shutil.move(os.path.join(root, filename), paks_dir) |
| 130 |
| 131 # Move everything in PRODUCT_DIR to top level. |
| 132 deps_product_dir = os.path.join(deps_dir, 'out', build_type) |
| 133 if os.path.isdir(deps_product_dir): |
| 134 for p in os.listdir(deps_product_dir): |
| 135 shutil.move(os.path.join(deps_product_dir, p), deps_dir) |
| 136 os.rmdir(deps_product_dir) |
| 137 os.rmdir(os.path.join(deps_dir, 'out')) |
| 138 |
| 139 return deps_dir |
| 140 |
| 141 |
26 def _FullyQualifiedTestSuites(exe, option_test_suite, build_type): | 142 def _FullyQualifiedTestSuites(exe, option_test_suite, build_type): |
27 """Get a list of absolute paths to test suite targets. | 143 """Get a list of absolute paths to test suite targets. |
28 | 144 |
29 Args: | 145 Args: |
30 exe: if True, use the executable-based test runner. | 146 exe: if True, use the executable-based test runner. |
31 option_test_suite: the test_suite specified as an option. | 147 option_test_suite: the test_suite specified as an option. |
32 build_type: 'Release' or 'Debug'. | 148 build_type: 'Release' or 'Debug'. |
33 | 149 |
34 Returns: | 150 Returns: |
35 A list of tuples containing the suite and absolute path. | 151 A list of tuples containing the suite and absolute path. |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
143 attached_devices = android_commands.GetAttachedDevices() | 259 attached_devices = android_commands.GetAttachedDevices() |
144 | 260 |
145 if not attached_devices: | 261 if not attached_devices: |
146 raise Exception('A device must be attached and online.') | 262 raise Exception('A device must be attached and online.') |
147 | 263 |
148 # Reset the test port allocation. It's important to do it before starting | 264 # Reset the test port allocation. It's important to do it before starting |
149 # to dispatch any tests. | 265 # to dispatch any tests. |
150 if not ports.ResetTestServerPortAllocation(): | 266 if not ports.ResetTestServerPortAllocation(): |
151 raise Exception('Failed to reset test server port.') | 267 raise Exception('Failed to reset test server port.') |
152 | 268 |
| 269 deps_dir = _GenerateDepsDirUsingIsolate(suite_name, options.build_type) |
| 270 |
153 # Constructs a new TestRunner with the current options. | 271 # Constructs a new TestRunner with the current options. |
154 def RunnerFactory(device, shard_index): | 272 def RunnerFactory(device, shard_index): |
155 return test_runner.TestRunner( | 273 return test_runner.TestRunner( |
156 device, | 274 device, |
157 options.test_suite, | 275 options.test_suite, |
158 options.test_arguments, | 276 options.test_arguments, |
159 options.timeout, | 277 options.timeout, |
160 options.cleanup_test_files, | 278 options.cleanup_test_files, |
161 options.tool, | 279 options.tool, |
162 options.build_type, | 280 options.build_type, |
163 options.webkit, | 281 options.webkit, |
164 options.push_deps, | 282 options.push_deps, |
165 constants.GTEST_TEST_PACKAGE_NAME, | 283 constants.GTEST_TEST_PACKAGE_NAME, |
166 constants.GTEST_TEST_ACTIVITY_NAME, | 284 constants.GTEST_TEST_ACTIVITY_NAME, |
167 constants.GTEST_COMMAND_LINE_FILE) | 285 constants.GTEST_COMMAND_LINE_FILE, |
| 286 deps_dir=deps_dir) |
168 | 287 |
169 # Get tests and split them up based on the number of devices. | 288 # Get tests and split them up based on the number of devices. |
170 if options.test_filter: | 289 if options.test_filter: |
171 all_tests = [t for t in options.test_filter.split(':') if t] | 290 all_tests = [t for t in options.test_filter.split(':') if t] |
172 else: | 291 else: |
173 all_tests = GetAllEnabledTests(RunnerFactory, attached_devices) | 292 all_tests = GetAllEnabledTests(RunnerFactory, attached_devices) |
174 num_devices = len(attached_devices) | 293 num_devices = len(attached_devices) |
175 tests = [':'.join(all_tests[i::num_devices]) for i in xrange(num_devices)] | 294 tests = [':'.join(all_tests[i::num_devices]) for i in xrange(num_devices)] |
176 tests = [t for t in tests if t] | 295 tests = [t for t in tests if t] |
177 | 296 |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
231 test_options.test_suite = suite_path | 350 test_options.test_suite = suite_path |
232 test_results, test_exit_code = _RunATestSuite(test_options, suite_name) | 351 test_results, test_exit_code = _RunATestSuite(test_options, suite_name) |
233 results.AddTestRunResults(test_results) | 352 results.AddTestRunResults(test_results) |
234 if test_exit_code and exit_code != constants.ERROR_EXIT_CODE: | 353 if test_exit_code and exit_code != constants.ERROR_EXIT_CODE: |
235 exit_code = test_exit_code | 354 exit_code = test_exit_code |
236 | 355 |
237 if options.use_xvfb: | 356 if options.use_xvfb: |
238 framebuffer.Stop() | 357 framebuffer.Stop() |
239 | 358 |
240 return (results, exit_code) | 359 return (results, exit_code) |
OLD | NEW |