| 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 |