Diff
Modified: trunk/Tools/ChangeLog (277060 => 277061)
--- trunk/Tools/ChangeLog 2021-05-06 00:19:10 UTC (rev 277060)
+++ trunk/Tools/ChangeLog 2021-05-06 00:22:16 UTC (rev 277061)
@@ -1,3 +1,39 @@
+2021-05-05 Jonathan Bedard <jbed...@apple.com>
+
+ [run-api-tests] Use webkitcorepy's TaskPool
+ https://bugs.webkit.org/show_bug.cgi?id=225221
+ <rdar://problem/77352465>
+
+ Reviewed by Dewei Zhu.
+
+ * Scripts/libraries/webkitcorepy/setup.py: Bump version.
+ * Scripts/libraries/webkitcorepy/webkitcorepy/__init__.py: Ditto.
+ * Scripts/libraries/webkitcorepy/webkitcorepy/task_pool.py:
+ (TaskPool.__enter__): Ensure that mock has been imported before spawning child processes.
+ * Scripts/run-api-tests: Moved from webkitpy/api_tests/run_api_tests.py.
+ * Scripts/webkitpy/api_tests/run_api_tests.py: Moved to run-api-tests.
+ * Scripts/webkitpy/api_tests/runner.py:
+ (_Worker): Representation of Worker process.
+ (_Worker.setup): Pass port object to worker process.
+ (_Worker.teardown): Un-set port object on worker process.
+ (_Worker.__init__): Construct object to hold a Worker process's variables.
+ (_Worker._run_single_test): Log test results, post results to parent.
+ (_Worker.run): Run shard with and post results to parent process.
+ (setup_shard): Run setup in Worker process.
+ (run_shard): Run test shard in Worker process.
+ (report_result): Receive result in the parent process.
+ ((teardown_shard): Tear down Worker process.
+ (Runner.__init__):
+ (Runner.command_for_port):
+ (Runner._shard_tests): Split tests into shards to be efficiently run.
+ (Runner.run): Use TaskPool to run tests in sub processes.
+ (Runner.callback): Save results in parent process.
+ (Runner.result_map_by_status):
+ (Runner.handle): Deleted.
+ (_Worker._run_shard_with_binary): Deleted.
+ (_Worker.post): Deleted.
+ (_Worker.handle): Deleted.
+
2021-05-05 Devin Rousso <drou...@apple.com>
Sampled Page Top Color: don't snapshot if the hit test location is an image or has an animation
Modified: trunk/Tools/Scripts/libraries/webkitcorepy/setup.py (277060 => 277061)
--- trunk/Tools/Scripts/libraries/webkitcorepy/setup.py 2021-05-06 00:19:10 UTC (rev 277060)
+++ trunk/Tools/Scripts/libraries/webkitcorepy/setup.py 2021-05-06 00:22:16 UTC (rev 277061)
@@ -30,7 +30,7 @@
setup(
name='webkitcorepy',
- version='0.5.13',
+ version='0.5.14',
description='Library containing various Python support classes and functions.',
long_description=readme(),
classifiers=[
Modified: trunk/Tools/Scripts/libraries/webkitcorepy/webkitcorepy/__init__.py (277060 => 277061)
--- trunk/Tools/Scripts/libraries/webkitcorepy/webkitcorepy/__init__.py 2021-05-06 00:19:10 UTC (rev 277060)
+++ trunk/Tools/Scripts/libraries/webkitcorepy/webkitcorepy/__init__.py 2021-05-06 00:22:16 UTC (rev 277061)
@@ -37,7 +37,7 @@
from webkitcorepy.task_pool import TaskPool
from webkitcorepy.credentials import credentials
-version = Version(0, 5, 13)
+version = Version(0, 5, 14)
from webkitcorepy.autoinstall import Package, AutoInstall
if sys.version_info > (3, 0):
Modified: trunk/Tools/Scripts/libraries/webkitcorepy/webkitcorepy/task_pool.py (277060 => 277061)
--- trunk/Tools/Scripts/libraries/webkitcorepy/webkitcorepy/task_pool.py 2021-05-06 00:19:10 UTC (rev 277060)
+++ trunk/Tools/Scripts/libraries/webkitcorepy/webkitcorepy/task_pool.py 2021-05-06 00:22:16 UTC (rev 277061)
@@ -332,6 +332,8 @@
self.block_size = block_size
def __enter__(self):
+ from mock import patch
+
with Timeout(seconds=10, patch=False, handler=self.Exception('Failed to start all workers')):
for worker in self.workers:
worker.start()
Modified: trunk/Tools/Scripts/run-api-tests (277060 => 277061)
--- trunk/Tools/Scripts/run-api-tests 2021-05-06 00:19:10 UTC (rev 277060)
+++ trunk/Tools/Scripts/run-api-tests 2021-05-06 00:22:16 UTC (rev 277061)
@@ -1,6 +1,6 @@
#!/usr/bin/env python
-# Copyright (C) 2018 Apple Inc. All rights reserved.
+# Copyright (C) 2018-2021 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
@@ -22,7 +22,145 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-"""Wrapper around webkitpy/api_tests/run_api_tests.py"""
-from webkitpy.common import multiprocessing_bootstrap
+from __future__ import print_function
+import logging
+import optparse
+import sys
+import traceback
-multiprocessing_bootstrap.run('webkitpy', 'api_tests', 'run_api_tests.py')
+from webkitpy.api_tests.manager import Manager
+from webkitpy.common.host import Host
+from webkitpy.layout_tests.views.metered_stream import MeteredStream
+from webkitpy.port import configuration_options, platform_options, base, win
+from webkitpy.results.options import upload_options
+
+EXCEPTIONAL_EXIT_STATUS = -1
+INTERRUPT_EXIT_STATUS = -2
+
+_log = logging.getLogger(__name__)
+
+
+def main(argv, stdout, stderr):
+ options, args = parse_args(argv)
+ host = Host()
+
+ try:
+ options.webkit_test_runner = True
+ port = host.port_factory.get(options.platform, options)
+ except NotImplementedError as e:
+ print(str(e), file=stderr)
+ return EXCEPTIONAL_EXIT_STATUS
+
+ # Some platforms do not support API tests
+ does_not_support_api_tests = ['ios-device']
+ if port.operating_system() in does_not_support_api_tests:
+ print('{} cannot run API tests'.format(port.operating_system()), file=stderr)
+ return EXCEPTIONAL_EXIT_STATUS
+
+ try:
+ return run(port, options, args, stderr)
+ except KeyboardInterrupt:
+ return INTERRUPT_EXIT_STATUS
+ except BaseException as e:
+ if isinstance(e, Exception):
+ print('\n%s raised: %s' % (e.__class__.__name__, str(e)), file=stderr)
+ traceback.print_exc(file=stderr)
+ return EXCEPTIONAL_EXIT_STATUS
+
+
+def run(port, options, args, logging_stream):
+ logger = logging.getLogger()
+ logger.setLevel(logging.DEBUG if options.verbose else logging.ERROR if options.quiet else logging.INFO)
+
+ try:
+ stream = MeteredStream(logging_stream, options.verbose, logger=logger, number_of_columns=port.host.platform.terminal_width(), print_timestamps=options.timestamps)
+ manager = Manager(port, options, stream)
+
+ result = manager.run(args, json_output=options.json_output)
+ _log.debug("Testing completed, Exit status: %d" % result)
+ return result
+ finally:
+ stream.cleanup()
+
+
+def parse_args(args):
+ option_group_definitions = []
+
+ option_group_definitions.append(('Platform options', platform_options()))
+ option_group_definitions.append(('Configuration options', configuration_options()))
+ option_group_definitions.append(('Printing Options', [
+ optparse.make_option('-q', '--quiet', action='', default=False,
+ help='Run quietly (errors, warnings, and progress only)'),
+ optparse.make_option('-v', '--verbose', action='', default=False,
+ help='Enable verbose printing'),
+ optparse.make_option('--timestamps', action='', default=False,
+ help='Print timestamps for each logged line'),
+ optparse.make_option('--json-output', action='', default=None,
+ help='Save test results as JSON to file'),
+ ]))
+
+ option_group_definitions.append(('WebKit Options', [
+ optparse.make_option('-g', '--guard-malloc', action='', default=False,
+ help='Enable Guard Malloc (OS X only)'),
+ optparse.make_option('--root', action='',
+ help='Path to a directory containing the executables needed to run tests.'),
+ ]))
+
+ option_group_definitions.append(('Testing Options', [
+ optparse.make_option('--wtf-only', action='', const='TestWTF', dest='api_binary',
+ help='Only build, check and run TestWTF'),
+ optparse.make_option('--webkit-only', action='', const='TestWebKitAPI', dest='api_binary',
+ help='Only check and run TestWebKitAPI'),
+ optparse.make_option('--web-core-only', action='', const='TestWebCore', dest='api_binary',
+ help='Only check and run TestWebCore.exe (Windows only)'),
+ optparse.make_option('--webkit-legacy-only', action='', const='TestWebKitLegacy', dest='api_binary',
+ help='Only check and run TestWebKitLegacy.exe (Windows only)'),
+ optparse.make_option('-d', '--dump', action='', default=False,
+ help='Dump all test names without running them'),
+ optparse.make_option('--build', dest='build', action='', default=True,
+ help='Check to ensure the build is up-to-date (default).'),
+ optparse.make_option('--no-build', dest='build', action='',
+ help="Don't check to see if the build is up-to-date."),
+ optparse.make_option('--timeout', default=30,
+ help='Number of seconds to wait before a test times out'),
+ optparse.make_option('--no-timeout', dest='timeout', action='',
+ help='Disable timeouts for all tests'),
+ optparse.make_option('--iterations', type='int', default=1, help='Number of times to run the set of tests (e.g. ABCABCABC)'),
+ optparse.make_option('--repeat-each', type='int', default=1, help='Number of times to run each test (e.g. AAABBBCCC)'),
+
+ # FIXME: Remove the default, API tests should be multiprocess
+ optparse.make_option('--child-processes', default=1,
+ help='Number of processes to run in parallel.'),
+
+ # FIXME: Default should be false, API tests should not be forced to run singly
+ optparse.make_option('--run-singly', action='', default=True,
+ help='Run a separate process for each test'),
+
+ optparse.make_option('--force', action='', default=False,
+ help='Run all tests, even DISABLED tests'),
+ optparse.make_option('--additional-env-var', type='string', action='', default=[],
+ help='Passes that environment variable to the tests (--additional-env-var=NAME=VALUE)'),
+ ]))
+ option_group_definitions.append(('Upload Options', upload_options()))
+
+ option_parser = optparse.OptionParser(
+ usage='run-api-tests [options] [<test names>...]',
+ description="""By default, run-api-tests will run all API tests. It also allows the user to specify tests of the \
+format <suite>.<test> or <canonicalized binary name>.<suite>.<test>. Note that in the case where a binary is not \
+specified, one will be inferred by listing all available tests. Specifying just a binary or just a suite will cause every \
+test contained within to be run. The canonicalized binary name is the binary name with any filename extension \
+stripped. For Unix ports, these binaries are {} and {}. For Windows ports, they are {} and {}.""".format(
+ ', '.join(base.Port.API_TEST_BINARY_NAMES[:-1]), base.Port.API_TEST_BINARY_NAMES[-1],
+ ', '.join(win.WinPort.API_TEST_BINARY_NAMES[:-1]), win.WinPort.API_TEST_BINARY_NAMES[-1],
+ ))
+
+ for group_name, group_options in option_group_definitions:
+ option_group = optparse.OptionGroup(option_parser, group_name)
+ option_group.add_options(group_options)
+ option_parser.add_option_group(option_group)
+
+ return option_parser.parse_args(args)
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:], sys.stdout, sys.stderr))
Deleted: trunk/Tools/Scripts/webkitpy/api_tests/run_api_tests.py (277060 => 277061)
--- trunk/Tools/Scripts/webkitpy/api_tests/run_api_tests.py 2021-05-06 00:19:10 UTC (rev 277060)
+++ trunk/Tools/Scripts/webkitpy/api_tests/run_api_tests.py 2021-05-06 00:22:16 UTC (rev 277061)
@@ -1,164 +0,0 @@
-# Copyright (C) 2018 Apple Inc. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-# 1. Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
-# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
-# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
-# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-from __future__ import print_function
-import logging
-import optparse
-import sys
-import traceback
-
-from webkitpy.api_tests.manager import Manager
-from webkitpy.common.host import Host
-from webkitpy.layout_tests.views.metered_stream import MeteredStream
-from webkitpy.port import configuration_options, platform_options, base, win
-from webkitpy.results.options import upload_options
-
-EXCEPTIONAL_EXIT_STATUS = -1
-INTERRUPT_EXIT_STATUS = -2
-
-_log = logging.getLogger(__name__)
-
-
-def main(argv, stdout, stderr):
- options, args = parse_args(argv)
- host = Host()
-
- try:
- options.webkit_test_runner = True
- port = host.port_factory.get(options.platform, options)
- except NotImplementedError as e:
- print(str(e), file=stderr)
- return EXCEPTIONAL_EXIT_STATUS
-
- # Some platforms do not support API tests
- does_not_support_api_tests = ['ios-device']
- if port.operating_system() in does_not_support_api_tests:
- print('{} cannot run API tests'.format(port.operating_system()), file=stderr)
- return EXCEPTIONAL_EXIT_STATUS
-
- try:
- return run(port, options, args, stderr)
- except KeyboardInterrupt:
- return INTERRUPT_EXIT_STATUS
- except BaseException as e:
- if isinstance(e, Exception):
- print('\n%s raised: %s' % (e.__class__.__name__, str(e)), file=stderr)
- traceback.print_exc(file=stderr)
- return EXCEPTIONAL_EXIT_STATUS
-
-
-def run(port, options, args, logging_stream):
- logger = logging.getLogger()
- logger.setLevel(logging.DEBUG if options.verbose else logging.ERROR if options.quiet else logging.INFO)
-
- try:
- stream = MeteredStream(logging_stream, options.verbose, logger=logger, number_of_columns=port.host.platform.terminal_width(), print_timestamps=options.timestamps)
- manager = Manager(port, options, stream)
-
- result = manager.run(args, json_output=options.json_output)
- _log.debug("Testing completed, Exit status: %d" % result)
- return result
- finally:
- stream.cleanup()
-
-
-def parse_args(args):
- option_group_definitions = []
-
- option_group_definitions.append(('Platform options', platform_options()))
- option_group_definitions.append(('Configuration options', configuration_options()))
- option_group_definitions.append(('Printing Options', [
- optparse.make_option('-q', '--quiet', action='', default=False,
- help='Run quietly (errors, warnings, and progress only)'),
- optparse.make_option('-v', '--verbose', action='', default=False,
- help='Enable verbose printing'),
- optparse.make_option('--timestamps', action='', default=False,
- help='Print timestamps for each logged line'),
- optparse.make_option('--json-output', action='', default=None,
- help='Save test results as JSON to file'),
- ]))
-
- option_group_definitions.append(('WebKit Options', [
- optparse.make_option('-g', '--guard-malloc', action='', default=False,
- help='Enable Guard Malloc (OS X only)'),
- optparse.make_option('--root', action='',
- help='Path to a directory containing the executables needed to run tests.'),
- ]))
-
- option_group_definitions.append(('Testing Options', [
- optparse.make_option('--wtf-only', action='', const='TestWTF', dest='api_binary',
- help='Only build, check and run TestWTF'),
- optparse.make_option('--webkit-only', action='', const='TestWebKitAPI', dest='api_binary',
- help='Only check and run TestWebKitAPI'),
- optparse.make_option('--web-core-only', action='', const='TestWebCore', dest='api_binary',
- help='Only check and run TestWebCore.exe (Windows only)'),
- optparse.make_option('--webkit-legacy-only', action='', const='TestWebKitLegacy', dest='api_binary',
- help='Only check and run TestWebKitLegacy.exe (Windows only)'),
- optparse.make_option('-d', '--dump', action='', default=False,
- help='Dump all test names without running them'),
- optparse.make_option('--build', dest='build', action='', default=True,
- help='Check to ensure the build is up-to-date (default).'),
- optparse.make_option('--no-build', dest='build', action='',
- help="Don't check to see if the build is up-to-date."),
- optparse.make_option('--timeout', default=30,
- help='Number of seconds to wait before a test times out'),
- optparse.make_option('--no-timeout', dest='timeout', action='',
- help='Disable timeouts for all tests'),
- optparse.make_option('--iterations', type='int', default=1, help='Number of times to run the set of tests (e.g. ABCABCABC)'),
- optparse.make_option('--repeat-each', type='int', default=1, help='Number of times to run each test (e.g. AAABBBCCC)'),
-
- # FIXME: Remove the default, API tests should be multiprocess
- optparse.make_option('--child-processes', default=1,
- help='Number of processes to run in parallel.'),
-
- # FIXME: Default should be false, API tests should not be forced to run singly
- optparse.make_option('--run-singly', action='', default=True,
- help='Run a separate process for each test'),
-
- optparse.make_option('--force', action='', default=False,
- help='Run all tests, even DISABLED tests'),
- optparse.make_option('--additional-env-var', type='string', action='', default=[],
- help='Passes that environment variable to the tests (--additional-env-var=NAME=VALUE)'),
- ]))
- option_group_definitions.append(('Upload Options', upload_options()))
-
- option_parser = optparse.OptionParser(
- usage='run-api-tests [options] [<test names>...]',
- description="""By default, run-api-tests will run all API tests. It also allows the user to specify tests of the \
-format <suite>.<test> or <canonicalized binary name>.<suite>.<test>. Note that in the case where a binary is not \
-specified, one will be inferred by listing all available tests. Specifying just a binary or just a suite will cause every \
-test contained within to be run. The canonicalized binary name is the binary name with any filename extension \
-stripped. For Unix ports, these binaries are {} and {}. For Windows ports, they are {} and {}.""".format(
- ', '.join(base.Port.API_TEST_BINARY_NAMES[:-1]), base.Port.API_TEST_BINARY_NAMES[-1],
- ', '.join(win.WinPort.API_TEST_BINARY_NAMES[:-1]), win.WinPort.API_TEST_BINARY_NAMES[-1],
- ))
-
- for group_name, group_options in option_group_definitions:
- option_group = optparse.OptionGroup(option_parser, group_name)
- option_group.add_options(group_options)
- option_parser.add_option_group(option_group)
-
- return option_parser.parse_args(args)
-
-
-if __name__ == '__main__':
- sys.exit(main(sys.argv[1:], sys.stdout, sys.stderr))
Modified: trunk/Tools/Scripts/webkitpy/api_tests/runner.py (277060 => 277061)
--- trunk/Tools/Scripts/webkitpy/api_tests/runner.py 2021-05-06 00:19:10 UTC (rev 277060)
+++ trunk/Tools/Scripts/webkitpy/api_tests/runner.py 2021-05-06 00:22:16 UTC (rev 277061)
@@ -1,4 +1,4 @@
-# Copyright (C) 2018 Apple Inc. All rights reserved.
+# Copyright (C) 2018-2021 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
@@ -25,13 +25,35 @@
import time
from webkitcorepy import string_utils
+from webkitcorepy import TaskPool
-from webkitpy.common import message_pool
from webkitpy.common.iteration_compatibility import iteritems
from webkitpy.port.server_process import ServerProcess, _log as server_process_logger
from webkitpy.xcode.simulated_device import SimulatedDeviceManager
+_log = logging.getLogger(__name__)
+
+def setup_shard(port=None):
+ return _Worker.setup(port=port)
+
+
+def run_shard(name, *tests):
+ return _Worker.instance.run(name, *tests)
+
+
+def report_result(worker, test, status, output):
+ if status == Runner.STATUS_PASSED and (not output or Runner.instance.port.get_option('quiet')):
+ Runner.instance.printer.write_update('{} {} {}'.format(worker, test, Runner.NAME_FOR_STATUS[status]))
+ else:
+ Runner.instance.printer.writeln('{} {} {}'.format(worker, test, Runner.NAME_FOR_STATUS[status]))
+ Runner.instance.results[test] = status, output
+
+
+def teardown_shard():
+ return _Worker.teardown()
+
+
class Runner(object):
STATUS_PASSED = 0
STATUS_FAILED = 1
@@ -48,6 +70,8 @@
'Disabled',
]
+ instance = None
+
def __init__(self, port, printer):
self.port = port
self.printer = printer
@@ -56,16 +80,6 @@
self._has_logged_for_test = True # Suppress an empty line between "Running tests" and the first test's output.
self.results = {}
- @staticmethod
- def _shard_tests(tests):
- shards = {}
- for test in tests:
- shard_prefix = '.'.join(test.split('.')[:-1])
- if shard_prefix not in shards:
- shards[shard_prefix] = []
- shards[shard_prefix].append(test)
- return shards
-
# FIXME API tests should run as an app, we won't need this function <https://bugs.webkit.org/show_bug.cgi?id=175204>
@staticmethod
def command_for_port(port, args):
@@ -80,6 +94,16 @@
args[0] = os.path.splitext(args[0])[0] + '.exe'
return args
+ @staticmethod
+ def _shard_tests(tests):
+ shards = {}
+ for test in tests:
+ shard_prefix = '.'.join(test.split('.')[:-1])
+ if shard_prefix not in shards:
+ shards[shard_prefix] = []
+ shards[shard_prefix].append(test)
+ return shards
+
def run(self, tests, num_workers):
if not tests:
return
@@ -91,45 +115,24 @@
server_process_logger.setLevel(logging.CRITICAL)
try:
+ if Runner.instance:
+ raise RuntimeError('Cannot nest API test runners')
+ Runner.instance = self
self._num_workers = min(num_workers, len(shards))
- with message_pool.get(self, lambda caller: _Worker(caller, self.port, shards), self._num_workers) as pool:
- pool.run(('test', shard) for shard, _ in iteritems(shards))
+
+ with TaskPool(
+ workers=self._num_workers,
+ setup=setup_shard, setupkwargs=dict(port=self.port),
+ teardown=teardown_shard,
+ ) as pool:
+ for name, tests in iteritems(shards):
+ pool.do(run_shard, name, *tests)
+ pool.wait()
+
finally:
server_process_logger.setLevel(original_level)
+ Runner.instance = None
-
- def handle(self, message_name, source, test_name=None, status=0, output=''):
- if message_name == 'did_spawn_worker':
- return
-
- source = '' if self._num_workers == 1 else source + ' '
- will_stream_logs = self._num_workers == 1 and self.port.get_option('verbose')
- if message_name == 'ended_test':
- update = '{}{} {}'.format(source, test_name, Runner.NAME_FOR_STATUS[status])
-
- # Don't print test output if --quiet.
- if status != Runner.STATUS_PASSED or (output and not self.port.get_option('quiet')):
- if not will_stream_logs:
- for line in output.splitlines():
- if not self._has_logged_for_test:
- self._has_logged_for_test = True
- self.printer.writeln(source)
- self.printer.writeln('{} {}'.format(source, line))
- self.printer.writeln(update)
- else:
- self.printer.write_update(update)
- self.tests_run += 1
- self.results[test_name] = (status, output)
- self._has_logged_for_test = False
-
- if message_name == 'log' and will_stream_logs:
- for line in output.splitlines():
- if not self._has_logged_for_test:
- self._has_logged_for_test = True
- self.printer.writeln(source)
- self.printer.writeln('{} {}'.format(source, line))
-
-
def result_map_by_status(self, status=None):
map = {}
for test_name, result in iteritems(self.results):
@@ -139,17 +142,25 @@
class _Worker(object):
- def __init__(self, caller, port, shard_map):
- self._caller = caller
+ instance = None
+
+ @classmethod
+ def setup(cls, port=None):
+ cls.instance = cls(port)
+
+ @classmethod
+ def teardown(cls):
+ cls.instance = None
+
+ def __init__(self, port):
self._port = port
self.host = port.host
- self._shard_map = shard_map
# ServerProcess doesn't allow for a timeout of 'None,' this uses a week instead of None.
self._timeout = int(self._port.get_option('timeout')) if self._port.get_option('timeout') else 60 * 24 * 7
- @staticmethod
- def _filter_noisy_output(output):
+ @classmethod
+ def _filter_noisy_output(cls, output):
result = ''
for line in output.splitlines():
if line.lstrip().startswith('objc['):
@@ -167,13 +178,14 @@
if test.split('.')[1].startswith('DISABLED_') and not self._port.get_option('force'):
status = Runner.STATUS_DISABLED
+ stdout_buffer = ''
+ stderr_buffer = ''
+
try:
deadline = time.time() + self._timeout
if status != Runner.STATUS_DISABLED:
server_process.start()
- stdout_buffer = ''
- stderr_buffer = ''
while status == Runner.STATUS_RUNNING:
stdout_line, stderr_line = server_process.read_either_stdout_or_stderr_line(deadline)
if not stderr_line and not stdout_line:
@@ -182,7 +194,7 @@
if stderr_line:
stderr_line = string_utils.decode(stderr_line, target_type=str)
stderr_buffer += stderr_line
- self.post('log', output=stderr_line[:-1])
+ _log.error(stderr_line[:-1])
if stdout_line:
stdout_line = string_utils.decode(stdout_line, target_type=str)
if '**PASS**' in stdout_line:
@@ -191,7 +203,7 @@
status = Runner.STATUS_FAILED
else:
stdout_buffer += stdout_line
- self.post('log', output=stdout_line[:-1])
+ _log.error(stdout_line[:-1])
if status == Runner.STATUS_DISABLED:
pass
@@ -205,14 +217,21 @@
finally:
remaining_stderr = string_utils.decode(server_process.pop_all_buffered_stderr(), target_type=str)
remaining_stdout = string_utils.decode(server_process.pop_all_buffered_stdout(), target_type=str)
- self.post('log', output=remaining_stderr + remaining_stdout)
+ for line in (remaining_stdout + remaining_stderr).splitlines(False):
+ _log.error(line)
output_buffer = stderr_buffer + stdout_buffer + remaining_stderr + remaining_stdout
server_process.stop()
- self.post('ended_test', '{}.{}'.format(binary_name, test), status, self._filter_noisy_output(output_buffer))
+ TaskPool.Process.queue.send(TaskPool.Task(
+ report_result, None, TaskPool.Process.name,
+ '{}.{}'.format(binary_name, test),
+ status,
+ self._filter_noisy_output(output_buffer),
+ ))
- def _run_shard_with_binary(self, binary_name, tests):
- remaining_tests = list(tests)
+ def run(self, name, *tests):
+ binary_name = name.split('.')[0]
+ remaining_tests = ['.'.join(test.split('.')[1:]) for test in tests]
# Try to run the shard in a single process.
while remaining_tests and not self._port.get_option('run_singly'):
@@ -219,8 +238,9 @@
starting_length = len(remaining_tests)
server_process = ServerProcess(
self._port, binary_name,
- Runner.command_for_port(self._port, [self._port._build_path(binary_name), '--gtest_filter={}'.format(':'.join(remaining_tests))]),
- env=self._port.environment_for_api_tests())
+ Runner.command_for_port(self._port, [
+ self._port._build_path(binary_name), '--gtest_filter={}'.format(':'.join(remaining_tests))
+ ]), env=self._port.environment_for_api_tests())
try:
deadline = time.time() + self._timeout
@@ -230,7 +250,7 @@
server_process.start()
while remaining_tests:
- stdout = server_process.read_stdout_line(deadline)
+ stdout = string_utils.decode(server_process.read_stdout_line(deadline), target_type=str)
# If we've triggered a timeout, we don't know which test caused it. Break out and run singly.
if stdout is None and server_process.timed_out:
@@ -251,7 +271,14 @@
continue
if last_test is not None:
remaining_tests.remove(last_test)
- self.post('ended_test', '{}.{}'.format(binary_name, last_test), last_status, stdout_buffer)
+
+ for line in stdout_buffer.splitlines(False):
+ _log.error(line)
+ TaskPool.Process.queue.send(TaskPool.Task(
+ report_result, None, TaskPool.Process.name,
+ '{}.{}'.format(binary_name, last_test),
+ last_status, stdout_buffer,
+ ))
deadline = time.time() + self._timeout
stdout_buffer = ''
@@ -264,11 +291,18 @@
# We assume that stderr is only relevant if there is a crash (meaning we triggered an assert)
if last_test:
remaining_tests.remove(last_test)
- stdout_buffer += server_process.pop_all_buffered_stdout()
- stderr_buffer = server_process.pop_all_buffered_stderr() if last_status == Runner.STATUS_CRASHED else ''
- self.post('log', output=stdout_buffer + stderr_buffer)
- self.post('ended_test', '{}.{}'.format(binary_name, last_test), last_status, self._filter_noisy_output(stdout_buffer + stderr_buffer))
+ stdout_buffer += string_utils.decode(server_process.pop_all_buffered_stdout(), target_type=str)
+ stderr_buffer = string_utils.decode(server_process.pop_all_buffered_stderr(), target_type=str) if last_status == Runner.STATUS_CRASHED else ''
+ for line in (stdout_buffer + stderr_buffer).splitlines(keepends=False):
+ _log.error(line)
+ TaskPool.Process.queue.send(TaskPool.Task(
+ report_result, None, TaskPool.Process.name,
+ '{}.{}'.format(binary_name, last_test),
+ last_status,
+ self._filter_noisy_output(stdout_buffer + stderr_buffer),
+ ))
+
if server_process.timed_out:
break
@@ -281,19 +315,3 @@
# Now, just try and run the rest of the tests singly.
for test in remaining_tests:
self._run_single_test(binary_name, test)
-
- def post(self, message_name, test_name=None, status=0, output=''):
- self._caller.post(message_name, test_name, status, output)
-
- def handle(self, message_name, source, shard_name):
- assert message_name == 'test'
- self.post('started_shard', shard_name)
-
- binary_map = {}
- for test in self._shard_map[shard_name]:
- split_test_name = test.split('.')
- if split_test_name[0] not in binary_map:
- binary_map[split_test_name[0]] = []
- binary_map[split_test_name[0]].append('.'.join(split_test_name[1:]))
- for binary_name, test_list in binary_map.items():
- self._run_shard_with_binary(binary_name, test_list)