summaryrefslogtreecommitdiffstats
path: root/build
diff options
context:
space:
mode:
authorcraigdh@chromium.org <craigdh@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-30 19:39:18 +0000
committercraigdh@chromium.org <craigdh@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-30 19:39:18 +0000
commit32b722a8c97d3da059a7ab0f7eda266c4e40afe2 (patch)
tree95e6d61629a6bba0f65853ba286abc19dc95bbf4 /build
parent32c3e98e51fda2fb2da0a3e076d7754e7cccbb5f (diff)
downloadchromium_src-32b722a8c97d3da059a7ab0f7eda266c4e40afe2.zip
chromium_src-32b722a8c97d3da059a7ab0f7eda266c4e40afe2.tar.gz
chromium_src-32b722a8c97d3da059a7ab0f7eda266c4e40afe2.tar.bz2
Split out gtest dispatcher and sharder.
BUG=168889 TEST=build/android/run_tests.py Review URL: https://codereview.chromium.org/12094005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@179687 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'build')
-rw-r--r--build/android/pylib/gtest/dispatch.py148
-rw-r--r--build/android/pylib/gtest/test_runner.py (renamed from build/android/pylib/gtest/single_test_runner.py)0
-rw-r--r--build/android/pylib/gtest/test_sharder.py129
-rwxr-xr-xbuild/android/run_tests.py268
4 files changed, 280 insertions, 265 deletions
diff --git a/build/android/pylib/gtest/dispatch.py b/build/android/pylib/gtest/dispatch.py
new file mode 100644
index 0000000..7f25583
--- /dev/null
+++ b/build/android/pylib/gtest/dispatch.py
@@ -0,0 +1,148 @@
+# 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.
+
+import copy
+import logging
+import os
+
+from pylib import android_commands
+from pylib import cmd_helper
+from pylib import ports
+from pylib.utils import emulator
+from pylib.utils import xvfb
+
+import gtest_config
+import test_sharder
+
+
+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'.
+ """
+ test_suite_dir = os.path.join(cmd_helper.OutDirectory.get(), build_type)
+ if option_test_suite:
+ all_test_suites = [option_test_suite]
+ else:
+ all_test_suites = gtest_config.STABLE_TEST_SUITES
+
+ if exe:
+ qualified_test_suites = [os.path.join(test_suite_dir, t)
+ for t in all_test_suites]
+ else:
+ # out/(Debug|Release)/$SUITE_apk/$SUITE-debug.apk
+ qualified_test_suites = [os.path.join(test_suite_dir,
+ t + '_apk',
+ t + '-debug.apk')
+ for t in all_test_suites]
+ for t, q in zip(all_test_suites, 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, gtest_config.STABLE_TEST_SUITES))
+ return qualified_test_suites
+
+
+def _RunATestSuite(options):
+ """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.
+
+ Returns:
+ 0 if successful, number of failing tests otherwise.
+ """
+ step_name = os.path.basename(options.test_suite).replace('-debug.apk', '')
+ attached_devices = []
+ buildbot_emulators = []
+
+ if options.use_emulator:
+ buildbot_emulators = emulator.LaunchEmulators(options.emulator_count,
+ 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:
+ logging.critical('A device must be attached and online.')
+ return 1
+
+ # 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.')
+
+ if options.gtest_filter:
+ logging.warning('Sharding is not possible with these configurations.')
+ attached_devices = [attached_devices[0]]
+
+ sharder = test_sharder.TestSharder(
+ attached_devices,
+ options.test_suite,
+ options.gtest_filter,
+ options.test_arguments,
+ options.timeout,
+ options.cleanup_test_files,
+ options.tool,
+ options.log_dump,
+ options.build_type,
+ options.webkit,
+ options.flakiness_dashboard_server)
+ test_results = sharder.RunShardedTests()
+
+ for buildbot_emulator in buildbot_emulators:
+ buildbot_emulator.Shutdown()
+
+ return len(test_results.GetAllBroken())
+
+
+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:
+ 0 if successful, number of failing tests otherwise.
+ """
+ if options.test_suite == 'help':
+ _ListTestSuites()
+ return 0
+
+ if options.use_xvfb:
+ framebuffer = xvfb.Xvfb()
+ framebuffer.Start()
+
+ all_test_suites = _FullyQualifiedTestSuites(options.exe, options.test_suite,
+ options.build_type)
+ failures = 0
+ for suite in all_test_suites:
+ # Give each test suite its own copy of options.
+ test_options = copy.deepcopy(options)
+ test_options.test_suite = suite
+ failures += _RunATestSuite(test_options)
+
+ if options.use_xvfb:
+ framebuffer.Stop()
+ return failures
diff --git a/build/android/pylib/gtest/single_test_runner.py b/build/android/pylib/gtest/test_runner.py
index 9947ab4..9947ab4 100644
--- a/build/android/pylib/gtest/single_test_runner.py
+++ b/build/android/pylib/gtest/test_runner.py
diff --git a/build/android/pylib/gtest/test_sharder.py b/build/android/pylib/gtest/test_sharder.py
new file mode 100644
index 0000000..4de8467
--- /dev/null
+++ b/build/android/pylib/gtest/test_sharder.py
@@ -0,0 +1,129 @@
+# 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.
+
+import fnmatch
+import logging
+import os
+
+from pylib import cmd_helper
+from pylib.base import base_test_sharder
+from pylib.gtest import debug_info
+from pylib.gtest import test_runner
+
+class TestSharder(base_test_sharder.BaseTestSharder):
+ """Responsible for sharding the tests on the connected devices."""
+
+ def __init__(self, attached_devices, test_suite, gtest_filter,
+ test_arguments, timeout, cleanup_test_files, tool,
+ log_dump_name, build_type, in_webkit_checkout,
+ flakiness_server=None):
+ super(TestSharder, self).__init__(attached_devices, build_type)
+ self.test_suite = test_suite
+ self.gtest_filter = gtest_filter or ''
+ self.test_arguments = test_arguments
+ self.timeout = timeout
+ self.cleanup_test_files = cleanup_test_files
+ self.tool = tool
+ self.log_dump_name = log_dump_name
+ self.in_webkit_checkout = in_webkit_checkout
+ self.flakiness_server = flakiness_server
+ self.all_tests = []
+ if not self.gtest_filter:
+ # No filter has been specified, let's add all tests then.
+ self.all_tests, self.attached_devices = self._GetAllEnabledTests()
+ self.tests = self.all_tests
+
+ def _GetAllEnabledTests(self):
+ """Get all enabled tests and available devices.
+
+ Obtains a list of enabled tests from the test package on the device,
+ then filters it again using the diabled list on the host.
+
+ Returns:
+ Tuple of (all enabled tests, available devices).
+
+ Raises Exception if all devices failed.
+ """
+ # TODO(frankf): This method is doing too much in a non-systematic way.
+ # If the intention is to drop flaky devices, why not go through all devices
+ # instead of breaking on the first succesfull run?
+ available_devices = list(self.attached_devices)
+ while available_devices:
+ try:
+ return (self._GetTestsFromDevice(available_devices[-1]),
+ available_devices)
+ except Exception as e:
+ logging.warning('Failed obtaining tests from %s %s',
+ available_devices[-1], e)
+ available_devices.pop()
+
+ raise Exception('No device available to get the list of tests.')
+
+ def _GetTestsFromDevice(self, device):
+ logging.info('Obtaining tests from %s', device)
+ runner = test_runner.SingleTestRunner(
+ device,
+ self.test_suite,
+ self.gtest_filter,
+ self.test_arguments,
+ self.timeout,
+ self.cleanup_test_files,
+ self.tool,
+ 0,
+ not not self.log_dump_name,
+ self.build_type,
+ self.in_webkit_checkout)
+ # The executable/apk needs to be copied before we can call GetAllTests.
+ runner.test_package.StripAndCopyExecutable()
+ all_tests = runner.test_package.GetAllTests()
+ disabled_list = runner.GetDisabledTests()
+ # Only includes tests that do not have any match in the disabled list.
+ all_tests = filter(lambda t:
+ not any([fnmatch.fnmatch(t, disabled_pattern)
+ for disabled_pattern in disabled_list]),
+ all_tests)
+ return all_tests
+
+ def CreateShardedTestRunner(self, device, index):
+ """Creates a suite-specific test runner.
+
+ Args:
+ device: Device serial where this shard will run.
+ index: Index of this device in the pool.
+
+ Returns:
+ A SingleTestRunner object.
+ """
+ device_num = len(self.attached_devices)
+ shard_test_list = self.tests[index::device_num]
+ test_filter = ':'.join(shard_test_list) + self.gtest_filter
+ return test_runner.SingleTestRunner(
+ device,
+ self.test_suite,
+ test_filter,
+ self.test_arguments,
+ self.timeout,
+ self.cleanup_test_files, self.tool, index,
+ not not self.log_dump_name,
+ self.build_type,
+ self.in_webkit_checkout)
+
+ def OnTestsCompleted(self, test_runners, test_results):
+ """Notifies that we completed the tests."""
+ test_results.LogFull(
+ test_type='Unit test',
+ test_package=test_runners[0].test_package.test_suite_basename,
+ build_type=self.build_type,
+ flakiness_server=self.flakiness_server)
+ test_results.PrintAnnotation()
+
+ if self.log_dump_name:
+ # Zip all debug info outputs into a file named by log_dump_name.
+ debug_info.GTestDebugInfo.ZipAndCleanResults(
+ os.path.join(
+ cmd_helper.OutDirectory.get(), self.build_type,
+ 'debug_info_dumps'),
+ self.log_dump_name)
+
+
diff --git a/build/android/run_tests.py b/build/android/run_tests.py
index 020814c..119535da 100755
--- a/build/android/run_tests.py
+++ b/build/android/run_tests.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
#
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# 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.
@@ -35,276 +35,14 @@ loaded. We don't care about the rare testcases which succeeded on emuatlor, but
failed on device.
"""
-import copy
-import fnmatch
-import logging
import optparse
-import os
-import signal
-import subprocess
import sys
-import time
-from pylib import android_commands
from pylib import cmd_helper
-from pylib import ports
-from pylib.base.base_test_sharder import BaseTestSharder
-from pylib.gtest import debug_info
-from pylib.gtest import gtest_config
-from pylib.gtest.single_test_runner import SingleTestRunner
+from pylib.gtest import dispatch
from pylib.utils import emulator
from pylib.utils import run_tests_helper
from pylib.utils import test_options_parser
-from pylib.utils import time_profile
-from pylib.utils import xvfb
-
-
-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'.
- """
- test_suite_dir = os.path.join(cmd_helper.OutDirectory.get(), build_type)
- if option_test_suite:
- all_test_suites = [option_test_suite]
- else:
- all_test_suites = gtest_config.STABLE_TEST_SUITES
-
- if exe:
- qualified_test_suites = [os.path.join(test_suite_dir, t)
- for t in all_test_suites]
- else:
- # out/(Debug|Release)/$SUITE_apk/$SUITE-debug.apk
- qualified_test_suites = [os.path.join(test_suite_dir,
- t + '_apk',
- t + '-debug.apk')
- for t in all_test_suites]
- for t, q in zip(all_test_suites, 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, gtest_config.STABLE_TEST_SUITES))
- return qualified_test_suites
-
-
-class TestSharder(BaseTestSharder):
- """Responsible for sharding the tests on the connected devices."""
-
- def __init__(self, attached_devices, test_suite, gtest_filter,
- test_arguments, timeout, cleanup_test_files, tool,
- log_dump_name, build_type, in_webkit_checkout,
- flakiness_server=None):
- BaseTestSharder.__init__(self, attached_devices, build_type)
- self.test_suite = test_suite
- self.gtest_filter = gtest_filter or ''
- self.test_arguments = test_arguments
- self.timeout = timeout
- self.cleanup_test_files = cleanup_test_files
- self.tool = tool
- self.log_dump_name = log_dump_name
- self.in_webkit_checkout = in_webkit_checkout
- self.flakiness_server = flakiness_server
- self.all_tests = []
- if not self.gtest_filter:
- # No filter has been specified, let's add all tests then.
- self.all_tests, self.attached_devices = self._GetAllEnabledTests()
- self.tests = self.all_tests
-
- def _GetAllEnabledTests(self):
- """Get all enabled tests and available devices.
-
- Obtains a list of enabled tests from the test package on the device,
- then filters it again using the diabled list on the host.
-
- Returns:
- Tuple of (all enabled tests, available devices).
-
- Raises Exception if all devices failed.
- """
- # TODO(frankf): This method is doing too much in a non-systematic way.
- # If the intention is to drop flaky devices, why not go through all devices
- # instead of breaking on the first succesfull run?
- available_devices = list(self.attached_devices)
- while available_devices:
- try:
- return (self._GetTestsFromDevice(available_devices[-1]),
- available_devices)
- except Exception as e:
- logging.warning('Failed obtaining tests from %s %s',
- available_devices[-1], e)
- available_devices.pop()
-
- raise Exception('No device available to get the list of tests.')
-
- def _GetTestsFromDevice(self, device):
- logging.info('Obtaining tests from %s', device)
- test_runner = SingleTestRunner(
- device,
- self.test_suite,
- self.gtest_filter,
- self.test_arguments,
- self.timeout,
- self.cleanup_test_files,
- self.tool,
- 0,
- not not self.log_dump_name,
- self.build_type,
- self.in_webkit_checkout)
- # The executable/apk needs to be copied before we can call GetAllTests.
- test_runner.test_package.StripAndCopyExecutable()
- all_tests = test_runner.test_package.GetAllTests()
- disabled_list = test_runner.GetDisabledTests()
- # Only includes tests that do not have any match in the disabled list.
- all_tests = filter(lambda t:
- not any([fnmatch.fnmatch(t, disabled_pattern)
- for disabled_pattern in disabled_list]),
- all_tests)
- return all_tests
-
- def CreateShardedTestRunner(self, device, index):
- """Creates a suite-specific test runner.
-
- Args:
- device: Device serial where this shard will run.
- index: Index of this device in the pool.
-
- Returns:
- A SingleTestRunner object.
- """
- device_num = len(self.attached_devices)
- shard_test_list = self.tests[index::device_num]
- test_filter = ':'.join(shard_test_list) + self.gtest_filter
- return SingleTestRunner(
- device,
- self.test_suite,
- test_filter,
- self.test_arguments,
- self.timeout,
- self.cleanup_test_files, self.tool, index,
- not not self.log_dump_name,
- self.build_type,
- self.in_webkit_checkout)
-
- def OnTestsCompleted(self, test_runners, test_results):
- """Notifies that we completed the tests."""
- test_results.LogFull(
- test_type='Unit test',
- test_package=test_runners[0].test_package.test_suite_basename,
- build_type=self.build_type,
- flakiness_server=self.flakiness_server)
- test_results.PrintAnnotation()
-
- if self.log_dump_name:
- # Zip all debug info outputs into a file named by log_dump_name.
- debug_info.GTestDebugInfo.ZipAndCleanResults(
- os.path.join(
- cmd_helper.OutDirectory.get(), self.build_type,
- 'debug_info_dumps'),
- self.log_dump_name)
-
-
-def _RunATestSuite(options):
- """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.
-
- Returns:
- 0 if successful, number of failing tests otherwise.
- """
- step_name = os.path.basename(options.test_suite).replace('-debug.apk', '')
- attached_devices = []
- buildbot_emulators = []
-
- if options.use_emulator:
- buildbot_emulators = emulator.LaunchEmulators(options.emulator_count,
- 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:
- logging.critical('A device must be attached and online.')
- return 1
-
- # 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.')
-
- if options.gtest_filter:
- logging.warning('Sharding is not possible with these configurations.')
- attached_devices = [attached_devices[0]]
-
- sharder = TestSharder(
- attached_devices,
- options.test_suite,
- options.gtest_filter,
- options.test_arguments,
- options.timeout,
- options.cleanup_test_files,
- options.tool,
- options.log_dump,
- options.build_type,
- options.webkit,
- options.flakiness_dashboard_server)
- test_results = sharder.RunShardedTests()
-
- for buildbot_emulator in buildbot_emulators:
- buildbot_emulator.Shutdown()
-
- return len(test_results.GetAllBroken())
-
-
-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:
- 0 if successful, number of failing tests otherwise.
- """
- if options.test_suite == 'help':
- ListTestSuites()
- return 0
-
- if options.use_xvfb:
- framebuffer = xvfb.Xvfb()
- framebuffer.Start()
-
- all_test_suites = FullyQualifiedTestSuites(options.exe, options.test_suite,
- options.build_type)
- failures = 0
- for suite in all_test_suites:
- # Give each test suite its own copy of options.
- test_options = copy.deepcopy(options)
- test_options.test_suite = suite
- failures += _RunATestSuite(test_options)
-
- if options.use_xvfb:
- framebuffer.Stop()
- return failures
-
-
-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 main(argv):
@@ -323,7 +61,7 @@ def main(argv):
if options.use_emulator:
emulator.DeleteAllTempAVDs()
- failed_tests_count = Dispatch(options)
+ failed_tests_count = dispatch.Dispatch(options)
# Failures of individual test suites are communicated by printing a
# STEP_FAILURE message.