| 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 """Generates test runner factory and tests for GTests.""" | 
|    6  |    6  | 
|    7 import copy |  | 
|    8 import fnmatch |    7 import fnmatch | 
|    9 import glob |    8 import glob | 
|   10 import logging |    9 import logging | 
|   11 import os |   10 import os | 
|   12 import shutil |   11 import shutil | 
|   13 import sys |   12 import sys | 
|   14  |   13  | 
|   15 from pylib import android_commands |   14 from pylib import android_commands | 
|   16 from pylib import cmd_helper |   15 from pylib import cmd_helper | 
|   17 from pylib import constants |   16 from pylib import constants | 
|   18 from pylib import ports |   17 from pylib import ports | 
|   19 from pylib.base import base_test_result |   18 from pylib.base import base_test_result | 
|   20 from pylib.base import shard |  | 
|   21 from pylib.utils import emulator |  | 
|   22 from pylib.utils import report_results |  | 
|   23 from pylib.utils import xvfb |  | 
|   24  |   19  | 
|   25 import gtest_config |   20 import gtest_config | 
|   26 import test_runner |   21 import test_runner | 
|   27  |   22  | 
|   28 sys.path.insert(0, |   23 sys.path.insert(0, | 
|   29                 os.path.join(constants.DIR_SOURCE_ROOT, 'build', 'util', 'lib')) |   24                 os.path.join(constants.DIR_SOURCE_ROOT, 'build', 'util', 'lib')) | 
|   30 from common import unittest_util |   25 from common import unittest_util | 
|   31  |   26  | 
|   32  |   27  | 
|   33 _ISOLATE_FILE_PATHS = { |   28 _ISOLATE_FILE_PATHS = { | 
| (...skipping 29 matching lines...) Expand all  Loading... | 
|   63     'third_party/hunspell_dictionaries/*.dic', |   58     'third_party/hunspell_dictionaries/*.dic', | 
|   64     # crbug.com/258690 |   59     # crbug.com/258690 | 
|   65     'webkit/data/bmp_decoder', |   60     'webkit/data/bmp_decoder', | 
|   66     'webkit/data/ico_decoder', |   61     'webkit/data/ico_decoder', | 
|   67 ] |   62 ] | 
|   68  |   63  | 
|   69 _ISOLATE_SCRIPT = os.path.join( |   64 _ISOLATE_SCRIPT = os.path.join( | 
|   70     constants.DIR_SOURCE_ROOT, 'tools', 'swarm_client', 'isolate.py') |   65     constants.DIR_SOURCE_ROOT, 'tools', 'swarm_client', 'isolate.py') | 
|   71  |   66  | 
|   72  |   67  | 
|   73 def _GenerateDepsDirUsingIsolate(test_suite, build_type): |   68 def _GenerateDepsDirUsingIsolate(suite_name, build_type): | 
|   74   """Generate the dependency dir for the test suite using isolate. |   69   """Generate the dependency dir for the test suite using isolate. | 
|   75  |   70  | 
|   76   Args: |   71   Args: | 
|   77     test_suite: Name of the test suite (e.g. base_unittests). |   72     suite_name: Name of the test suite (e.g. base_unittests). | 
|   78     build_type: Release/Debug |   73     build_type: Release/Debug | 
|   79   """ |   74   """ | 
|   80   product_dir = os.path.join(cmd_helper.OutDirectory.get(), build_type) |   75   product_dir = os.path.join(cmd_helper.OutDirectory.get(), build_type) | 
|   81   assert os.path.isabs(product_dir) |   76   assert os.path.isabs(product_dir) | 
|   82  |   77  | 
|   83   if os.path.isdir(constants.ISOLATE_DEPS_DIR): |   78   if os.path.isdir(constants.ISOLATE_DEPS_DIR): | 
|   84     shutil.rmtree(constants.ISOLATE_DEPS_DIR) |   79     shutil.rmtree(constants.ISOLATE_DEPS_DIR) | 
|   85  |   80  | 
|   86   isolate_rel_path = _ISOLATE_FILE_PATHS.get(test_suite) |   81   isolate_rel_path = _ISOLATE_FILE_PATHS.get(suite_name) | 
|   87   if not isolate_rel_path: |   82   if not isolate_rel_path: | 
|   88     logging.info('Did not find an isolate file for the test suite.') |   83     logging.info('Did not find an isolate file for the test suite.') | 
|   89     return |   84     return | 
|   90  |   85  | 
|   91   isolate_abs_path = os.path.join(constants.DIR_SOURCE_ROOT, isolate_rel_path) |   86   isolate_abs_path = os.path.join(constants.DIR_SOURCE_ROOT, isolate_rel_path) | 
|   92   isolated_abs_path = os.path.join( |   87   isolated_abs_path = os.path.join( | 
|   93       product_dir, '%s.isolated' % test_suite) |   88       product_dir, '%s.isolated' % suite_name) | 
|   94   assert os.path.exists(isolate_abs_path) |   89   assert os.path.exists(isolate_abs_path) | 
|   95   isolate_cmd = [ |   90   isolate_cmd = [ | 
|   96       'python', _ISOLATE_SCRIPT, |   91       'python', _ISOLATE_SCRIPT, | 
|   97       'remap', |   92       'remap', | 
|   98       '--isolate', isolate_abs_path, |   93       '--isolate', isolate_abs_path, | 
|   99       '--isolated', isolated_abs_path, |   94       '--isolated', isolated_abs_path, | 
|  100       '-V', 'PRODUCT_DIR=%s' % product_dir, |   95       '-V', 'PRODUCT_DIR=%s' % product_dir, | 
|  101       '-V', 'OS=android', |   96       '-V', 'OS=android', | 
|  102       '--outdir', constants.ISOLATE_DEPS_DIR, |   97       '--outdir', constants.ISOLATE_DEPS_DIR, | 
|  103   ] |   98   ] | 
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
|  144  |  139  | 
|  145   # Move everything in PRODUCT_DIR to top level. |  140   # Move everything in PRODUCT_DIR to top level. | 
|  146   deps_product_dir = os.path.join(constants.ISOLATE_DEPS_DIR, 'out', build_type) |  141   deps_product_dir = os.path.join(constants.ISOLATE_DEPS_DIR, 'out', build_type) | 
|  147   if os.path.isdir(deps_product_dir): |  142   if os.path.isdir(deps_product_dir): | 
|  148     for p in os.listdir(deps_product_dir): |  143     for p in os.listdir(deps_product_dir): | 
|  149       shutil.move(os.path.join(deps_product_dir, p), constants.ISOLATE_DEPS_DIR) |  144       shutil.move(os.path.join(deps_product_dir, p), constants.ISOLATE_DEPS_DIR) | 
|  150     os.rmdir(deps_product_dir) |  145     os.rmdir(deps_product_dir) | 
|  151     os.rmdir(os.path.join(constants.ISOLATE_DEPS_DIR, 'out')) |  146     os.rmdir(os.path.join(constants.ISOLATE_DEPS_DIR, 'out')) | 
|  152  |  147  | 
|  153  |  148  | 
|  154 def _FullyQualifiedTestSuites(exe, option_test_suite, build_type): |  149 def _GetSuitePath(use_exe_test_runner, suite_name, build_type): | 
|  155   """Get a list of absolute paths to test suite targets. |  150   """Get the absolute path to the test suite. | 
|  156  |  151  | 
|  157   Args: |  152   Args: | 
|  158     exe: if True, use the executable-based test runner. |  153     use_exe_test_runner: If True, use the executable-based test runner. | 
|  159     option_test_suite: the test_suite specified as an option. |  154     suite_name: The suite name specified on the command line. | 
|  160     build_type: 'Release' or 'Debug'. |  155     build_type: 'Release' or 'Debug'. | 
|  161  |  156  | 
|  162   Returns: |  157   Returns: | 
|  163     A list of tuples containing the suite and absolute path. |  158     The absolute path of the given suite. | 
|  164     Ex. ('content_unittests', |  159     Ex. '/tmp/chrome/src/out/Debug/content_unittests_apk/' | 
|  165           '/tmp/chrome/src/out/Debug/content_unittests_apk/' |  160         'content_unittests-debug.apk' | 
|  166           'content_unittests-debug.apk') |  | 
|  167  |  161  | 
|  168   Raises: |  162   Raises: | 
|  169     Exception: If test suite not found. |  163     Exception: If test suite not found. | 
|  170   """ |  164   """ | 
|  171   def GetQualifiedSuite(suite): |  165   if use_exe_test_runner: | 
|  172     if suite.is_suite_exe: |  166     relpath = suite_name | 
|  173       relpath = suite.name |  167   else: | 
|  174     else: |  168     relpath = os.path.join(suite_name + '_apk', suite_name + '-debug.apk') | 
|  175       # out/(Debug|Release)/$SUITE_apk/$SUITE-debug.apk |  169   suite_path = os.path.join(cmd_helper.OutDirectory.get(), build_type, relpath) | 
|  176       relpath = os.path.join(suite.name + '_apk', suite.name + '-debug.apk') |  | 
|  177     return suite.name, os.path.join(test_suite_dir, relpath) |  | 
|  178  |  170  | 
|  179   test_suite_dir = os.path.join(cmd_helper.OutDirectory.get(), build_type) |  171   if not os.path.exists(suite_path): | 
|  180   if option_test_suite: |  172     raise Exception('Test suite %s not found in %s.\n' | 
|  181     all_test_suites = [gtest_config.Suite(exe, option_test_suite)] |  173                     'Supported test suites:\n %s\n' | 
|  182   else: |  174                     'Ensure it has been built.\n' % | 
|  183     all_test_suites = gtest_config.STABLE_TEST_SUITES |  175                     (suite_name, suite_path, | 
 |  176                      [s.name for s in gtest_config.STABLE_TEST_SUITES])) | 
|  184  |  177  | 
|  185   # List of tuples (suite_name, suite_path) |  178   return suite_path | 
|  186   qualified_test_suites = map(GetQualifiedSuite, all_test_suites) |  | 
|  187  |  | 
|  188   for t, q in qualified_test_suites: |  | 
|  189     if not os.path.exists(q): |  | 
|  190       raise Exception('Test suite %s not found in %s.\n' |  | 
|  191                       'Supported test suites:\n %s\n' |  | 
|  192                       'Ensure it has been built.\n' % |  | 
|  193                       (t, q, [s.name for s in gtest_config.STABLE_TEST_SUITES])) |  | 
|  194   return qualified_test_suites |  | 
|  195  |  179  | 
|  196  |  180  | 
|  197 def _GetDisabledTestsFilterFromFile(test_suite): |  181 def _GetDisabledTestsFilterFromFile(suite_name): | 
|  198   """Returns a gtest filter based on the *_disabled file. |  182   """Returns a gtest filter based on the *_disabled file. | 
|  199  |  183  | 
|  200   Args: |  184   Args: | 
|  201     test_suite: Name of the test suite (e.g. base_unittests). |  185     suite_name: Name of the test suite (e.g. base_unittests). | 
|  202  |  186  | 
|  203   Returns: |  187   Returns: | 
|  204     A gtest filter which excludes disabled tests. |  188     A gtest filter which excludes disabled tests. | 
|  205     Example: '*-StackTrace.*:StringPrintfTest.StringPrintfMisc' |  189     Example: '*-StackTrace.*:StringPrintfTest.StringPrintfMisc' | 
|  206   """ |  190   """ | 
|  207   filter_file_path = os.path.join( |  191   filter_file_path = os.path.join( | 
|  208       os.path.abspath(os.path.dirname(__file__)), |  192       os.path.abspath(os.path.dirname(__file__)), | 
|  209       'filter', '%s_disabled' % test_suite) |  193       'filter', '%s_disabled' % suite_name) | 
|  210  |  194  | 
|  211   if not filter_file_path or not os.path.exists(filter_file_path): |  195   if not filter_file_path or not os.path.exists(filter_file_path): | 
|  212     logging.info('No filter file found at %s', filter_file_path) |  196     logging.info('No filter file found at %s', filter_file_path) | 
|  213     return '*' |  197     return '*' | 
|  214  |  198  | 
|  215   filters = [x for x in [x.strip() for x in file(filter_file_path).readlines()] |  199   filters = [x for x in [x.strip() for x in file(filter_file_path).readlines()] | 
|  216              if x and x[0] != '#'] |  200              if x and x[0] != '#'] | 
|  217   disabled_filter = '*-%s' % ':'.join(filters) |  201   disabled_filter = '*-%s' % ':'.join(filters) | 
|  218   logging.info('Applying filter "%s" obtained from %s', |  202   logging.info('Applying filter "%s" obtained from %s', | 
|  219                disabled_filter, filter_file_path) |  203                disabled_filter, filter_file_path) | 
|  220   return disabled_filter |  204   return disabled_filter | 
|  221  |  205  | 
|  222  |  206  | 
|  223 def _GetTestsFromDevice(runner_factory, devices): |  207 def _GetTestsFromDevice(runner_factory, devices): | 
|  224   """Get a list of tests from a device. |  208   """Get a list of tests from a device. | 
|  225  |  209  | 
|  226   Args: |  210   Args: | 
|  227     runner_factory: callable that takes a device and index and returns a |  211     runner_factory: Callable that takes device and shard_index and returns | 
|  228       TestRunner object. |  212         a TestRunner. | 
|  229     devices: List of devices. |  213     devices: A list of device ids. | 
|  230  |  214  | 
|  231   Returns: |  215   Returns: | 
|  232     All the tests in the test suite. |  216     All the tests in the test suite. | 
|  233   """ |  217   """ | 
|  234   for device in devices: |  218   for device in devices: | 
|  235     try: |  219     try: | 
|  236       logging.info('Obtaining tests from %s', device) |  220       logging.info('Obtaining tests from %s', device) | 
|  237       runner = runner_factory(device, 0) |  221       runner = runner_factory(device, 0) | 
|  238       runner.test_package.Install() |  222       runner.test_package.Install() | 
|  239       return runner.test_package.GetAllTests() |  223       return runner.test_package.GetAllTests() | 
| (...skipping 25 matching lines...) Expand all  Loading... | 
|  265     filter_prefixes.append('MANUAL_') |  249     filter_prefixes.append('MANUAL_') | 
|  266  |  250  | 
|  267   for t in all_tests: |  251   for t in all_tests: | 
|  268     test_case, test = t.split('.', 1) |  252     test_case, test = t.split('.', 1) | 
|  269     if not any([test_case.startswith(prefix) or test.startswith(prefix) for |  253     if not any([test_case.startswith(prefix) or test.startswith(prefix) for | 
|  270                 prefix in filter_prefixes]): |  254                 prefix in filter_prefixes]): | 
|  271       filtered_tests.append(t) |  255       filtered_tests.append(t) | 
|  272   return filtered_tests |  256   return filtered_tests | 
|  273  |  257  | 
|  274  |  258  | 
|  275 def GetTestsFiltered(test_suite, gtest_filter, runner_factory, devices): |  259 def GetTestsFiltered(suite_name, gtest_filter, runner_factory, devices): | 
|  276   """Get all tests in the suite and filter them. |  260   """Get all tests in the suite and filter them. | 
|  277  |  261  | 
|  278   Obtains a list of tests from the test package on the device, and |  262   Obtains a list of tests from the test package on the device, and | 
|  279   applies the following filters in order: |  263   applies the following filters in order: | 
|  280     1. Remove tests with disabled prefixes. |  264     1. Remove tests with disabled prefixes. | 
|  281     2. Remove tests specified in the *_disabled files in the 'filter' dir |  265     2. Remove tests specified in the *_disabled files in the 'filter' dir | 
|  282     3. Applies |gtest_filter|. |  266     3. Applies |gtest_filter|. | 
|  283  |  267  | 
|  284   Args: |  268   Args: | 
|  285     test_suite: Name of the test suite (e.g. base_unittests). |  269     suite_name: Name of the test suite (e.g. base_unittests). | 
|  286     gtest_filter: A filter including negative and/or positive patterns. |  270     gtest_filter: A filter including negative and/or positive patterns. | 
|  287     runner_factory: callable that takes a device and index and returns a |  271     runner_factory: callable that takes a device and index and returns a | 
|  288       TestRunner object. |  272       TestRunner object. | 
|  289     devices: List of devices. |  273     devices: List of devices. | 
|  290  |  274  | 
|  291   Returns: |  275   Returns: | 
|  292     List of tests remaining. |  276     List of tests remaining. | 
|  293   """ |  277   """ | 
|  294   tests = _GetTestsFromDevice(runner_factory, devices) |  278   tests = _GetTestsFromDevice(runner_factory, devices) | 
|  295   tests = _FilterTestsUsingPrefixes( |  279   tests = _FilterTestsUsingPrefixes( | 
|  296       tests, bool(gtest_filter), bool(gtest_filter)) |  280       tests, bool(gtest_filter), bool(gtest_filter)) | 
|  297   tests = unittest_util.FilterTestNames( |  281   tests = unittest_util.FilterTestNames( | 
|  298       tests, _GetDisabledTestsFilterFromFile(test_suite)) |  282       tests, _GetDisabledTestsFilterFromFile(suite_name)) | 
|  299  |  283  | 
|  300   if gtest_filter: |  284   if gtest_filter: | 
|  301     tests = unittest_util.FilterTestNames(tests, gtest_filter) |  285     tests = unittest_util.FilterTestNames(tests, gtest_filter) | 
|  302  |  286  | 
|  303   return tests |  287   return tests | 
|  304  |  288  | 
|  305  |  289  | 
|  306 def _RunATestSuite(options, suite_name): |  290 def Setup(use_exe_test_runner, suite_name, test_arguments, timeout, | 
|  307   """Run a single test suite. |  291           cleanup_test_files, tool, build_type, webkit, push_deps, | 
|  308  |  292           gtest_filter): | 
|  309   Helper for Dispatch() to allow stop/restart of the emulator across |  293   """Create the test runner factory and tests. | 
|  310   test bundles.  If using the emulator, we start it on entry and stop |  | 
|  311   it on exit. |  | 
|  312  |  294  | 
|  313   Args: |  295   Args: | 
|  314     options: options for running the tests. |  296     use_exe_test_runner: If True, use the executable-based test runner. | 
|  315     suite_name: name of the test suite being run. |  297     suite_name: The suite name specified on the command line. | 
 |  298     test_arguments: Additional arguments to pass to the test binary. | 
 |  299     timeout: Timeout for each test. | 
 |  300     cleanup_test_files: Whether or not to cleanup test files on device. | 
 |  301     tool: Name of the Valgrind tool. | 
 |  302     build_type: 'Release' or 'Debug'. | 
 |  303     webkit: Whether the suite is being run from a WebKit checkout. | 
 |  304     push_deps: If True, push all dependencies to the device. | 
 |  305     gtest_filter: Filter for tests. | 
|  316  |  306  | 
|  317   Returns: |  307   Returns: | 
|  318     A tuple of (base_test_result.TestRunResult object, exit code). |  308     A tuple of (TestRunnerFactory, tests). | 
 |  309   """ | 
|  319  |  310  | 
|  320   Raises: |  | 
|  321     Exception: For various reasons including device failure or failing to reset |  | 
|  322         the test server port. |  | 
|  323   """ |  | 
|  324   attached_devices = [] |  | 
|  325   buildbot_emulators = [] |  | 
|  326  |  | 
|  327   if options.use_emulator: |  | 
|  328     buildbot_emulators = emulator.LaunchEmulators(options.emulator_count, |  | 
|  329                                                   options.abi, |  | 
|  330                                                   wait_for_boot=True) |  | 
|  331     attached_devices = [e.device for e in buildbot_emulators] |  | 
|  332   elif options.test_device: |  | 
|  333     attached_devices = [options.test_device] |  | 
|  334   else: |  | 
|  335     attached_devices = android_commands.GetAttachedDevices() |  | 
|  336  |  | 
|  337   if not attached_devices: |  | 
|  338     raise Exception('A device must be attached and online.') |  | 
|  339  |  | 
|  340   # Reset the test port allocation. It's important to do it before starting |  | 
|  341   # to dispatch any tests. |  | 
|  342   if not ports.ResetTestServerPortAllocation(): |  311   if not ports.ResetTestServerPortAllocation(): | 
|  343     raise Exception('Failed to reset test server port.') |  312     raise Exception('Failed to reset test server port.') | 
|  344  |  313  | 
|  345   _GenerateDepsDirUsingIsolate(suite_name, options.build_type) |  314   suite_path = _GetSuitePath(use_exe_test_runner, suite_name, build_type) | 
|  346  |  315  | 
 |  316   # TODO(gkanwar): This breaks the abstraction of having test_dispatcher.py deal | 
 |  317   # entirely with the devices. Can we do this another way? | 
 |  318   attached_devices = android_commands.GetAttachedDevices() | 
 |  319  | 
 |  320   deps_dir = _GenerateDepsDirUsingIsolate(suite_name, build_type) | 
|  347   # Constructs a new TestRunner with the current options. |  321   # Constructs a new TestRunner with the current options. | 
|  348   def RunnerFactory(device, shard_index): |  322   def TestRunnerFactory(device, shard_index): | 
|  349     return test_runner.TestRunner( |  323     return test_runner.TestRunner( | 
|  350         device, |  324         device, | 
|  351         options.test_suite, |  325         suite_path, | 
|  352         options.test_arguments, |  326         test_arguments, | 
|  353         options.timeout, |  327         timeout, | 
|  354         options.cleanup_test_files, |  328         cleanup_test_files, | 
|  355         options.tool, |  329         tool, | 
|  356         options.build_type, |  330         build_type, | 
|  357         options.webkit, |  331         webkit, | 
|  358         options.push_deps, |  332         push_deps, | 
|  359         constants.GTEST_TEST_PACKAGE_NAME, |  333         constants.GTEST_TEST_PACKAGE_NAME, | 
|  360         constants.GTEST_TEST_ACTIVITY_NAME, |  334         constants.GTEST_TEST_ACTIVITY_NAME, | 
|  361         constants.GTEST_COMMAND_LINE_FILE) |  335         constants.GTEST_COMMAND_LINE_FILE) | 
|  362  |  336  | 
|  363   # Get tests and split them up based on the number of devices. |  337   # Get tests and split them up based on the number of devices. | 
|  364   tests = GetTestsFiltered(suite_name, options.test_filter, |  338   tests = GetTestsFiltered(suite_name, gtest_filter, | 
|  365                            RunnerFactory, attached_devices) |  339                            TestRunnerFactory, attached_devices) | 
|  366   num_devices = len(attached_devices) |  340   num_devices = len(attached_devices) | 
|  367   tests = [':'.join(tests[i::num_devices]) for i in xrange(num_devices)] |  341   tests = [':'.join(tests[i::num_devices]) for i in xrange(num_devices)] | 
|  368   tests = [t for t in tests if t] |  342   tests = [t for t in tests if t] | 
|  369  |  343  | 
|  370   # Run tests. |  344   return (TestRunnerFactory, tests) | 
|  371   test_results, exit_code = shard.ShardAndRunTests( |  | 
|  372       RunnerFactory, attached_devices, tests, options.build_type, |  | 
|  373       test_timeout=None, num_retries=options.num_retries) |  | 
|  374  |  | 
|  375   report_results.LogFull( |  | 
|  376       results=test_results, |  | 
|  377       test_type='Unit test', |  | 
|  378       test_package=suite_name, |  | 
|  379       build_type=options.build_type, |  | 
|  380       flakiness_server=options.flakiness_dashboard_server) |  | 
|  381  |  | 
|  382   if os.path.isdir(constants.ISOLATE_DEPS_DIR): |  | 
|  383     shutil.rmtree(constants.ISOLATE_DEPS_DIR) |  | 
|  384  |  | 
|  385   for buildbot_emulator in buildbot_emulators: |  | 
|  386     buildbot_emulator.Shutdown() |  | 
|  387  |  | 
|  388   return (test_results, exit_code) |  | 
|  389  |  | 
|  390  |  | 
|  391 def _ListTestSuites(): |  | 
|  392   """Display a list of available test suites.""" |  | 
|  393   print 'Available test suites are:' |  | 
|  394   for test_suite in gtest_config.STABLE_TEST_SUITES: |  | 
|  395     print test_suite |  | 
|  396  |  | 
|  397  |  | 
|  398 def Dispatch(options): |  | 
|  399   """Dispatches the tests, sharding if possible. |  | 
|  400  |  | 
|  401   If options.use_emulator is True, all tests will be run in new emulator |  | 
|  402   instance. |  | 
|  403  |  | 
|  404   Args: |  | 
|  405     options: options for running the tests. |  | 
|  406  |  | 
|  407   Returns: |  | 
|  408     base_test_result.TestRunResults object with the results of running the tests |  | 
|  409   """ |  | 
|  410   results = base_test_result.TestRunResults() |  | 
|  411  |  | 
|  412   if options.test_suite == 'help': |  | 
|  413     _ListTestSuites() |  | 
|  414     return (results, 0) |  | 
|  415  |  | 
|  416   if options.use_xvfb: |  | 
|  417     framebuffer = xvfb.Xvfb() |  | 
|  418     framebuffer.Start() |  | 
|  419  |  | 
|  420   all_test_suites = _FullyQualifiedTestSuites(options.exe, options.test_suite, |  | 
|  421                                               options.build_type) |  | 
|  422   exit_code = 0 |  | 
|  423   for suite_name, suite_path in all_test_suites: |  | 
|  424     # Give each test suite its own copy of options. |  | 
|  425     test_options = copy.deepcopy(options) |  | 
|  426     test_options.test_suite = suite_path |  | 
|  427     test_results, test_exit_code = _RunATestSuite(test_options, suite_name) |  | 
|  428     results.AddTestRunResults(test_results) |  | 
|  429     if test_exit_code and exit_code != constants.ERROR_EXIT_CODE: |  | 
|  430       exit_code = test_exit_code |  | 
|  431  |  | 
|  432   if options.use_xvfb: |  | 
|  433     framebuffer.Stop() |  | 
|  434  |  | 
|  435   return (results, exit_code) |  | 
| OLD | NEW |