Commit 7addf674 authored by ojan@chromium.org's avatar ojan@chromium.org

Optimize baselines in parallel.

This is by far the slowest part of doing rebaselines.
We can do everything but the SCM commands in parallel.
Like we do for the other commands, save off the SCM commands
and do them all at the end.

Review URL: https://codereview.chromium.org/308793004

git-svn-id: svn://svn.chromium.org/blink/trunk@175191 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent 3710d057
...@@ -48,9 +48,12 @@ def _invert_dictionary(dictionary): ...@@ -48,9 +48,12 @@ def _invert_dictionary(dictionary):
class BaselineOptimizer(object): class BaselineOptimizer(object):
ROOT_LAYOUT_TESTS_DIRECTORY = 'LayoutTests' ROOT_LAYOUT_TESTS_DIRECTORY = 'LayoutTests'
def __init__(self, host, port_names): def __init__(self, host, port_names, skip_scm_commands):
self._filesystem = host.filesystem self._filesystem = host.filesystem
self._port_factory = host.port_factory self._port_factory = host.port_factory
self._skip_scm_commands = skip_scm_commands
self._files_to_delete = []
self._files_to_add = []
self._scm = host.scm() self._scm = host.scm()
self._port_names = port_names self._port_names = port_names
# Only used by unittests. # Only used by unittests.
...@@ -224,7 +227,10 @@ class BaselineOptimizer(object): ...@@ -224,7 +227,10 @@ class BaselineOptimizer(object):
_log.debug(" Deleting (SCM):") _log.debug(" Deleting (SCM):")
for platform_dir in sorted(self._platform(filename) for filename in scm_files): for platform_dir in sorted(self._platform(filename) for filename in scm_files):
_log.debug(" " + platform_dir) _log.debug(" " + platform_dir)
self._scm.delete_list(scm_files) if self._skip_scm_commands:
self._files_to_delete.extend(scm_files)
else:
self._scm.delete_list(scm_files)
if fs_files: if fs_files:
_log.debug(" Deleting (file system):") _log.debug(" Deleting (file system):")
for platform_dir in sorted(self._platform(filename) for filename in fs_files): for platform_dir in sorted(self._platform(filename) for filename in fs_files):
...@@ -246,7 +252,12 @@ class BaselineOptimizer(object): ...@@ -246,7 +252,12 @@ class BaselineOptimizer(object):
_log.debug(" Adding:") _log.debug(" Adding:")
for platform_dir in sorted(self._platform(filename) for filename in file_names): for platform_dir in sorted(self._platform(filename) for filename in file_names):
_log.debug(" " + platform_dir) _log.debug(" " + platform_dir)
self._scm.add_list(file_names) if self._skip_scm_commands:
# Have adds win over deletes.
self._files_to_delete = list(set(self._files_to_delete) - set(file_names))
self._files_to_add.extend(file_names)
else:
self._scm.add_list(file_names)
else: else:
_log.debug(" (Nothing to add)") _log.debug(" (Nothing to add)")
...@@ -303,7 +314,10 @@ class BaselineOptimizer(object): ...@@ -303,7 +314,10 @@ class BaselineOptimizer(object):
break break
_log.debug("Deleting redundant virtual root expected result.") _log.debug("Deleting redundant virtual root expected result.")
self._scm.delete(virtual_root_expected_baseline_path) if self._skip_scm_commands:
self._files_to_delete.append(virtual_root_expected_baseline_path)
else:
self._scm.delete(virtual_root_expected_baseline_path)
def optimize(self, baseline_name): def optimize(self, baseline_name):
# The virtual fallback path is the same as the non-virtual one tacked on to the bottom of the non-virtual path. # The virtual fallback path is the same as the non-virtual one tacked on to the bottom of the non-virtual path.
...@@ -316,10 +330,10 @@ class BaselineOptimizer(object): ...@@ -316,10 +330,10 @@ class BaselineOptimizer(object):
result = self._optimize_subtree(baseline_name) result = self._optimize_subtree(baseline_name)
non_virtual_baseline_name = self._port_factory.get().lookup_virtual_test_base(baseline_name) non_virtual_baseline_name = self._port_factory.get().lookup_virtual_test_base(baseline_name)
if not non_virtual_baseline_name: if not non_virtual_baseline_name:
return result return result, self._files_to_delete, self._files_to_add
self._optimize_virtual_root(baseline_name, non_virtual_baseline_name) self._optimize_virtual_root(baseline_name, non_virtual_baseline_name)
_log.debug("Optimizing non-virtual fallback path.") _log.debug("Optimizing non-virtual fallback path.")
result |= self._optimize_subtree(non_virtual_baseline_name) result |= self._optimize_subtree(non_virtual_baseline_name)
return result return result, self._files_to_delete, self._files_to_add
...@@ -65,7 +65,7 @@ class BaselineOptimizerTest(unittest.TestCase): ...@@ -65,7 +65,7 @@ class BaselineOptimizerTest(unittest.TestCase):
host.filesystem.write_binary_file('/mock-checkout/third_party/WebKit/LayoutTests/platform/win/another/test-expected.txt', 'result A') host.filesystem.write_binary_file('/mock-checkout/third_party/WebKit/LayoutTests/platform/win/another/test-expected.txt', 'result A')
host.filesystem.write_binary_file('/mock-checkout/third_party/WebKit/LayoutTests/platform/mac/another/test-expected.txt', 'result A') host.filesystem.write_binary_file('/mock-checkout/third_party/WebKit/LayoutTests/platform/mac/another/test-expected.txt', 'result A')
host.filesystem.write_binary_file('/mock-checkout/third_party/WebKit/LayoutTests/another/test-expected.txt', 'result B') host.filesystem.write_binary_file('/mock-checkout/third_party/WebKit/LayoutTests/another/test-expected.txt', 'result B')
baseline_optimizer = BaselineOptimizer(host, host.port_factory.all_port_names()) baseline_optimizer = BaselineOptimizer(host, host.port_factory.all_port_names(), skip_scm_commands=False)
baseline_optimizer._move_baselines('another/test-expected.txt', { baseline_optimizer._move_baselines('another/test-expected.txt', {
'/mock-checkout/third_party/WebKit/LayoutTests/platform/win': 'aaa', '/mock-checkout/third_party/WebKit/LayoutTests/platform/win': 'aaa',
'/mock-checkout/third_party/WebKit/LayoutTests/platform/mac': 'aaa', '/mock-checkout/third_party/WebKit/LayoutTests/platform/mac': 'aaa',
...@@ -75,7 +75,32 @@ class BaselineOptimizerTest(unittest.TestCase): ...@@ -75,7 +75,32 @@ class BaselineOptimizerTest(unittest.TestCase):
}) })
self.assertEqual(host.filesystem.read_binary_file('/mock-checkout/third_party/WebKit/LayoutTests/another/test-expected.txt'), 'result A') self.assertEqual(host.filesystem.read_binary_file('/mock-checkout/third_party/WebKit/LayoutTests/another/test-expected.txt'), 'result A')
def _assertOptimization(self, results_by_directory, expected_new_results_by_directory, baseline_dirname=''): def test_move_baselines_skip_scm_commands(self):
host = MockHost(scm=ExcludingMockSCM(['/mock-checkout/third_party/WebKit/LayoutTests/platform/mac/another/test-expected.txt']))
host.filesystem.write_binary_file('/mock-checkout/third_party/WebKit/LayoutTests/platform/win/another/test-expected.txt', 'result A')
host.filesystem.write_binary_file('/mock-checkout/third_party/WebKit/LayoutTests/platform/mac/another/test-expected.txt', 'result A')
host.filesystem.write_binary_file('/mock-checkout/third_party/WebKit/LayoutTests/another/test-expected.txt', 'result B')
baseline_optimizer = BaselineOptimizer(host, host.port_factory.all_port_names(), skip_scm_commands=True)
baseline_optimizer._move_baselines('another/test-expected.txt', {
'/mock-checkout/third_party/WebKit/LayoutTests/platform/win': 'aaa',
'/mock-checkout/third_party/WebKit/LayoutTests/platform/mac': 'aaa',
'/mock-checkout/third_party/WebKit/LayoutTests': 'bbb',
}, {
'/mock-checkout/third_party/WebKit/LayoutTests/platform/linux': 'bbb',
'/mock-checkout/third_party/WebKit/LayoutTests': 'aaa',
})
self.assertEqual(host.filesystem.read_binary_file('/mock-checkout/third_party/WebKit/LayoutTests/another/test-expected.txt'), 'result A')
self.assertEqual(baseline_optimizer._files_to_delete, [
'/mock-checkout/third_party/WebKit/LayoutTests/platform/win/another/test-expected.txt',
])
self.assertEqual(baseline_optimizer._files_to_add, [
'/mock-checkout/third_party/WebKit/LayoutTests/another/test-expected.txt',
'/mock-checkout/third_party/WebKit/LayoutTests/platform/linux/another/test-expected.txt',
])
def _assertOptimization(self, results_by_directory, expected_new_results_by_directory, baseline_dirname='', expected_files_to_delete=None):
host = MockHost() host = MockHost()
fs = host.filesystem fs = host.filesystem
webkit_base = WebKitFinder(fs).webkit_base() webkit_base = WebKitFinder(fs).webkit_base()
...@@ -85,13 +110,13 @@ class BaselineOptimizerTest(unittest.TestCase): ...@@ -85,13 +110,13 @@ class BaselineOptimizerTest(unittest.TestCase):
path = fs.join(webkit_base, 'LayoutTests', dirname, baseline_name) path = fs.join(webkit_base, 'LayoutTests', dirname, baseline_name)
fs.write_binary_file(path, contents) fs.write_binary_file(path, contents)
baseline_optimizer = BaselineOptimizer(host, host.port_factory.all_port_names()) baseline_optimizer = BaselineOptimizer(host, host.port_factory.all_port_names(), skip_scm_commands=expected_files_to_delete is not None)
self.assertTrue(baseline_optimizer.optimize(fs.join(baseline_dirname, baseline_name))) self.assertTrue(baseline_optimizer.optimize(fs.join(baseline_dirname, baseline_name)))
for dirname, contents in expected_new_results_by_directory.items(): for dirname, contents in expected_new_results_by_directory.items():
path = fs.join(webkit_base, 'LayoutTests', dirname, baseline_name) path = fs.join(webkit_base, 'LayoutTests', dirname, baseline_name)
if contents is None: if contents is None:
self.assertFalse(fs.exists(path)) self.assertTrue(not fs.exists(path) or path in baseline_optimizer._files_to_delete)
else: else:
self.assertEqual(fs.read_binary_file(path), contents) self.assertEqual(fs.read_binary_file(path), contents)
...@@ -99,7 +124,10 @@ class BaselineOptimizerTest(unittest.TestCase): ...@@ -99,7 +124,10 @@ class BaselineOptimizerTest(unittest.TestCase):
for dirname in results_by_directory: for dirname in results_by_directory:
path = fs.join(webkit_base, 'LayoutTests', dirname, baseline_name) path = fs.join(webkit_base, 'LayoutTests', dirname, baseline_name)
if not dirname in expected_new_results_by_directory: if not dirname in expected_new_results_by_directory:
self.assertFalse(fs.exists(path)) self.assertTrue(not fs.exists(path) or path in baseline_optimizer._files_to_delete)
if expected_files_to_delete:
self.assertEqual(baseline_optimizer._files_to_delete, expected_files_to_delete)
def test_linux_redundant_with_win(self): def test_linux_redundant_with_win(self):
self._assertOptimization({ self._assertOptimization({
...@@ -209,6 +237,22 @@ class BaselineOptimizerTest(unittest.TestCase): ...@@ -209,6 +237,22 @@ class BaselineOptimizerTest(unittest.TestCase):
'compositing': '2', 'compositing': '2',
}, baseline_dirname='virtual/softwarecompositing') }, baseline_dirname='virtual/softwarecompositing')
def test_virtual_root_redundant_with_ancestors_skip_scm_commands(self):
self._assertOptimization({
'virtual/softwarecompositing': '2',
'platform/mac/compositing': '2',
'platform/win/compositing': '2',
}, {
'virtual/softwarecompositing': None,
'compositing': '2',
},
baseline_dirname='virtual/softwarecompositing',
expected_files_to_delete=[
'/mock-checkout/third_party/WebKit/LayoutTests/virtual/softwarecompositing/mock-baseline-expected.txt',
'/mock-checkout/third_party/WebKit/LayoutTests/platform/mac/compositing/mock-baseline-expected.txt',
'/mock-checkout/third_party/WebKit/LayoutTests/platform/win/compositing/mock-baseline-expected.txt',
])
def test_virtual_root_not_redundant_with_ancestors(self): def test_virtual_root_not_redundant_with_ancestors(self):
self._assertOptimization({ self._assertOptimization({
'virtual/softwarecompositing': '2', 'virtual/softwarecompositing': '2',
......
...@@ -72,6 +72,13 @@ class AbstractRebaseliningCommand(AbstractDeclarativeCommand): ...@@ -72,6 +72,13 @@ class AbstractRebaseliningCommand(AbstractDeclarativeCommand):
def __init__(self, options=None): def __init__(self, options=None):
super(AbstractRebaseliningCommand, self).__init__(options=options) super(AbstractRebaseliningCommand, self).__init__(options=options)
self._baseline_suffix_list = BASELINE_SUFFIX_LIST self._baseline_suffix_list = BASELINE_SUFFIX_LIST
self._scm_changes = {'add': [], 'delete': [], 'remove-lines': []}
def _add_to_scm_later(self, path):
self._scm_changes['add'].append(path)
def _delete_from_scm_later(self, path):
self._scm_changes['delete'].append(path)
class BaseInternalRebaselineCommand(AbstractRebaseliningCommand): class BaseInternalRebaselineCommand(AbstractRebaseliningCommand):
...@@ -82,10 +89,6 @@ class BaseInternalRebaselineCommand(AbstractRebaseliningCommand): ...@@ -82,10 +89,6 @@ class BaseInternalRebaselineCommand(AbstractRebaseliningCommand):
optparse.make_option("--builder", help="Builder to pull new baselines from"), optparse.make_option("--builder", help="Builder to pull new baselines from"),
optparse.make_option("--test", help="Test to rebaseline"), optparse.make_option("--test", help="Test to rebaseline"),
]) ])
self._scm_changes = {'add': [], 'remove-lines': []}
def _add_to_scm(self, path):
self._scm_changes['add'].append(path)
def _baseline_directory(self, builder_name): def _baseline_directory(self, builder_name):
port = self._tool.port_factory.get_from_builder_name(builder_name) port = self._tool.port_factory.get_from_builder_name(builder_name)
...@@ -168,7 +171,7 @@ class CopyExistingBaselinesInternal(BaseInternalRebaselineCommand): ...@@ -168,7 +171,7 @@ class CopyExistingBaselinesInternal(BaseInternalRebaselineCommand):
self._tool.filesystem.maybe_make_directory(self._tool.filesystem.dirname(new_baseline)) self._tool.filesystem.maybe_make_directory(self._tool.filesystem.dirname(new_baseline))
self._tool.filesystem.copyfile(old_baseline, new_baseline) self._tool.filesystem.copyfile(old_baseline, new_baseline)
if not self._tool.scm().exists(new_baseline): if not self._tool.scm().exists(new_baseline):
self._add_to_scm(new_baseline) self._add_to_scm_later(new_baseline)
def execute(self, options, args, tool): def execute(self, options, args, tool):
for suffix in options.suffixes.split(','): for suffix in options.suffixes.split(','):
...@@ -192,7 +195,7 @@ class RebaselineTest(BaseInternalRebaselineCommand): ...@@ -192,7 +195,7 @@ class RebaselineTest(BaseInternalRebaselineCommand):
filesystem.maybe_make_directory(filesystem.dirname(target_baseline)) filesystem.maybe_make_directory(filesystem.dirname(target_baseline))
filesystem.write_binary_file(target_baseline, data) filesystem.write_binary_file(target_baseline, data)
if not self._tool.scm().exists(target_baseline): if not self._tool.scm().exists(target_baseline):
self._add_to_scm(target_baseline) self._add_to_scm_later(target_baseline)
def _rebaseline_test(self, builder_name, test_name, suffix, results_url): def _rebaseline_test(self, builder_name, test_name, suffix, results_url):
baseline_directory = self._baseline_directory(builder_name) baseline_directory = self._baseline_directory(builder_name)
...@@ -231,13 +234,18 @@ class OptimizeBaselines(AbstractRebaseliningCommand): ...@@ -231,13 +234,18 @@ class OptimizeBaselines(AbstractRebaseliningCommand):
argument_names = "TEST_NAMES" argument_names = "TEST_NAMES"
def __init__(self): def __init__(self):
super(OptimizeBaselines, self).__init__(options=[self.suffixes_option] + self.platform_options) super(OptimizeBaselines, self).__init__(options=[
self.suffixes_option,
optparse.make_option('--no-modify-scm', action='store_true', default=False, help='Dump SCM commands as JSON instead of '),
] + self.platform_options)
def _optimize_baseline(self, optimizer, test_name): def _optimize_baseline(self, optimizer, test_name):
for suffix in self._baseline_suffix_list: for suffix in self._baseline_suffix_list:
baseline_name = _baseline_name(self._tool.filesystem, test_name, suffix) baseline_name = _baseline_name(self._tool.filesystem, test_name, suffix)
if not optimizer.optimize(baseline_name): succeeded, files_to_delete, files_to_add = optimizer.optimize(baseline_name)
if not succeeded:
print "Heuristics failed to optimize %s" % baseline_name print "Heuristics failed to optimize %s" % baseline_name
return files_to_delete, files_to_add
def execute(self, options, args, tool): def execute(self, options, args, tool):
self._baseline_suffix_list = options.suffixes.split(',') self._baseline_suffix_list = options.suffixes.split(',')
...@@ -246,11 +254,17 @@ class OptimizeBaselines(AbstractRebaseliningCommand): ...@@ -246,11 +254,17 @@ class OptimizeBaselines(AbstractRebaseliningCommand):
print "No port names match '%s'" % options.platform print "No port names match '%s'" % options.platform
return return
optimizer = BaselineOptimizer(tool, port_names) optimizer = BaselineOptimizer(tool, port_names, skip_scm_commands=options.no_modify_scm)
port = tool.port_factory.get(port_names[0]) port = tool.port_factory.get(port_names[0])
for test_name in port.tests(args): for test_name in port.tests(args):
_log.info("Optimizing %s" % test_name) _log.info("Optimizing %s" % test_name)
self._optimize_baseline(optimizer, test_name) files_to_delete, files_to_add = self._optimize_baseline(optimizer, test_name)
for path in files_to_delete:
self._delete_from_scm_later(path)
for path in files_to_add:
self._add_to_scm_later(path)
print json.dumps(self._scm_changes)
class AnalyzeBaselines(AbstractRebaseliningCommand): class AnalyzeBaselines(AbstractRebaseliningCommand):
...@@ -288,7 +302,7 @@ class AnalyzeBaselines(AbstractRebaseliningCommand): ...@@ -288,7 +302,7 @@ class AnalyzeBaselines(AbstractRebaseliningCommand):
print "No port names match '%s'" % options.platform print "No port names match '%s'" % options.platform
return return
self._baseline_optimizer = self._optimizer_class(tool, port_names) self._baseline_optimizer = self._optimizer_class(tool, port_names, skip_scm_commands=False)
self._port = tool.port_factory.get(port_names[0]) self._port = tool.port_factory.get(port_names[0])
for test_name in self._port.tests(args): for test_name in self._port.tests(args):
self._analyze_baseline(options, test_name) self._analyze_baseline(options, test_name)
...@@ -373,8 +387,9 @@ class AbstractParallelRebaselineCommand(AbstractRebaseliningCommand): ...@@ -373,8 +387,9 @@ class AbstractParallelRebaselineCommand(AbstractRebaseliningCommand):
rebaseline_commands.append(tuple([[path_to_webkit_patch, 'rebaseline-test-internal'] + cmd_line, cwd])) rebaseline_commands.append(tuple([[path_to_webkit_patch, 'rebaseline-test-internal'] + cmd_line, cwd]))
return copy_baseline_commands, rebaseline_commands return copy_baseline_commands, rebaseline_commands
def _files_to_add(self, command_results): def _serial_commands(self, command_results):
files_to_add = set() files_to_add = set()
files_to_delete = set()
lines_to_remove = {} lines_to_remove = {}
for output in [result[1].split('\n') for result in command_results]: for output in [result[1].split('\n') for result in command_results]:
file_added = False file_added = False
...@@ -384,6 +399,8 @@ class AbstractParallelRebaselineCommand(AbstractRebaseliningCommand): ...@@ -384,6 +399,8 @@ class AbstractParallelRebaselineCommand(AbstractRebaseliningCommand):
parsed_line = json.loads(line) parsed_line = json.loads(line)
if 'add' in parsed_line: if 'add' in parsed_line:
files_to_add.update(parsed_line['add']) files_to_add.update(parsed_line['add'])
if 'delete' in parsed_line:
files_to_delete.update(parsed_line['delete'])
if 'remove-lines' in parsed_line: if 'remove-lines' in parsed_line:
for line_to_remove in parsed_line['remove-lines']: for line_to_remove in parsed_line['remove-lines']:
test = line_to_remove['test'] test = line_to_remove['test']
...@@ -398,16 +415,24 @@ class AbstractParallelRebaselineCommand(AbstractRebaseliningCommand): ...@@ -398,16 +415,24 @@ class AbstractParallelRebaselineCommand(AbstractRebaseliningCommand):
if not file_added: if not file_added:
_log.debug('Could not add file based off output "%s"' % output) _log.debug('Could not add file based off output "%s"' % output)
return list(files_to_add), lines_to_remove return list(files_to_add), list(files_to_delete), lines_to_remove
def _optimize_baselines(self, test_prefix_list, verbose=False): def _optimize_baselines(self, test_prefix_list, verbose=False):
# We don't run this in parallel because modifying the SCM in parallel is unreliable. optimize_commands = []
for test in test_prefix_list: for test in test_prefix_list:
all_suffixes = set() all_suffixes = set()
for builder in self._builders_to_fetch_from(test_prefix_list[test]): for builder in self._builders_to_fetch_from(test_prefix_list[test]):
all_suffixes.update(self._suffixes_for_actual_failures(test, builder, test_prefix_list[test][builder])) all_suffixes.update(self._suffixes_for_actual_failures(test, builder, test_prefix_list[test][builder]))
# FIXME: We should propagate the platform options as well. # FIXME: We should propagate the platform options as well.
self._run_webkit_patch(['optimize-baselines', '--suffixes', ','.join(all_suffixes), test], verbose) cmd_line = ['--no-modify-scm', '--suffixes', ','.join(all_suffixes), test]
if verbose:
cmd_line.append('--verbose')
path_to_webkit_patch = self._tool.path()
cwd = self._tool.scm().checkout_root
optimize_commands.append(tuple([[path_to_webkit_patch, 'optimize-baselines'] + cmd_line, cwd]))
return optimize_commands
def _update_expectations_files(self, lines_to_remove): def _update_expectations_files(self, lines_to_remove):
# FIXME: This routine is way too expensive. We're creating N ports and N TestExpectations # FIXME: This routine is way too expensive. We're creating N ports and N TestExpectations
...@@ -451,9 +476,11 @@ class AbstractParallelRebaselineCommand(AbstractRebaseliningCommand): ...@@ -451,9 +476,11 @@ class AbstractParallelRebaselineCommand(AbstractRebaseliningCommand):
if line: if line:
print >> sys.stderr, line # FIXME: Figure out how to log properly. print >> sys.stderr, line # FIXME: Figure out how to log properly.
files_to_add, lines_to_remove = self._files_to_add(command_results) files_to_add, files_to_delete, lines_to_remove = self._serial_commands(command_results)
if files_to_delete:
self._tool.scm().delete_list(files_to_delete)
if files_to_add: if files_to_add:
self._tool.scm().add_list(list(files_to_add)) self._tool.scm().add_list(files_to_add)
if lines_to_remove: if lines_to_remove:
self._update_expectations_files(lines_to_remove) self._update_expectations_files(lines_to_remove)
...@@ -468,9 +495,8 @@ class AbstractParallelRebaselineCommand(AbstractRebaseliningCommand): ...@@ -468,9 +495,8 @@ class AbstractParallelRebaselineCommand(AbstractRebaseliningCommand):
self._run_in_parallel_and_update_scm(copy_baseline_commands) self._run_in_parallel_and_update_scm(copy_baseline_commands)
if rebaseline_commands: if rebaseline_commands:
self._run_in_parallel_and_update_scm(rebaseline_commands) self._run_in_parallel_and_update_scm(rebaseline_commands)
if options.optimize: if options.optimize:
self._optimize_baselines(test_prefix_list, options.verbose) self._run_in_parallel_and_update_scm(self._optimize_baselines(test_prefix_list, options.verbose))
def _suffixes_for_actual_failures(self, test, builder_name, existing_suffixes): def _suffixes_for_actual_failures(self, test, builder_name, existing_suffixes):
actual_results = self.builder_data()[builder_name].actual_results(test) actual_results = self.builder_data()[builder_name].actual_results(test)
......
...@@ -28,11 +28,13 @@ ...@@ -28,11 +28,13 @@
import webkitpy.thirdparty.unittest2 as unittest import webkitpy.thirdparty.unittest2 as unittest
from webkitpy.common.system.outputcapture import OutputCapture
from webkitpy.common.checkout.baselineoptimizer import BaselineOptimizer from webkitpy.common.checkout.baselineoptimizer import BaselineOptimizer
from webkitpy.common.checkout.scm.scm_mock import MockSCM
from webkitpy.common.host_mock import MockHost
from webkitpy.common.net.buildbot.buildbot_mock import MockBuilder from webkitpy.common.net.buildbot.buildbot_mock import MockBuilder
from webkitpy.common.net.layouttestresults import LayoutTestResults from webkitpy.common.net.layouttestresults import LayoutTestResults
from webkitpy.common.system.executive_mock import MockExecutive2 from webkitpy.common.system.executive_mock import MockExecutive2
from webkitpy.common.system.outputcapture import OutputCapture
from webkitpy.thirdparty.mock import Mock from webkitpy.thirdparty.mock import Mock
from webkitpy.tool.commands.rebaseline import * from webkitpy.tool.commands.rebaseline import *
from webkitpy.tool.mocktool import MockTool, MockOptions from webkitpy.tool.mocktool import MockTool, MockOptions
...@@ -124,7 +126,7 @@ class TestCopyExistingBaselinesInternal(_BaseTestCase): ...@@ -124,7 +126,7 @@ class TestCopyExistingBaselinesInternal(_BaseTestCase):
builders._exact_matches = old_exact_matches builders._exact_matches = old_exact_matches
self.assertMultiLineEqual(self._read(self.tool.filesystem.join(port.layout_tests_dir(), 'platform/test-mac-leopard/failures/expected/image-expected.txt')), 'original snowleopard result') self.assertMultiLineEqual(self._read(self.tool.filesystem.join(port.layout_tests_dir(), 'platform/test-mac-leopard/failures/expected/image-expected.txt')), 'original snowleopard result')
self.assertMultiLineEqual(out, '{"add": [], "remove-lines": []}\n') self.assertMultiLineEqual(out, '{"add": [], "remove-lines": [], "delete": []}\n')
def test_copying_overwritten_baseline_to_multiple_locations(self): def test_copying_overwritten_baseline_to_multiple_locations(self):
self.tool.executive = MockExecutive2() self.tool.executive = MockExecutive2()
...@@ -152,7 +154,7 @@ class TestCopyExistingBaselinesInternal(_BaseTestCase): ...@@ -152,7 +154,7 @@ class TestCopyExistingBaselinesInternal(_BaseTestCase):
self.assertMultiLineEqual(self._read(self.tool.filesystem.join(port.layout_tests_dir(), 'platform/test-linux-x86_64/failures/expected/image-expected.txt')), 'original win7 result') self.assertMultiLineEqual(self._read(self.tool.filesystem.join(port.layout_tests_dir(), 'platform/test-linux-x86_64/failures/expected/image-expected.txt')), 'original win7 result')
self.assertFalse(self.tool.filesystem.exists(self.tool.filesystem.join(port.layout_tests_dir(), 'platform/mac-leopard/userscripts/another-test-expected.txt'))) self.assertFalse(self.tool.filesystem.exists(self.tool.filesystem.join(port.layout_tests_dir(), 'platform/mac-leopard/userscripts/another-test-expected.txt')))
self.assertMultiLineEqual(out, '{"add": [], "remove-lines": []}\n') self.assertMultiLineEqual(out, '{"add": [], "remove-lines": [], "delete": []}\n')
def test_no_copy_existing_baseline(self): def test_no_copy_existing_baseline(self):
self.tool.executive = MockExecutive2() self.tool.executive = MockExecutive2()
...@@ -181,7 +183,7 @@ class TestCopyExistingBaselinesInternal(_BaseTestCase): ...@@ -181,7 +183,7 @@ class TestCopyExistingBaselinesInternal(_BaseTestCase):
self.assertMultiLineEqual(self._read(self.tool.filesystem.join(port.layout_tests_dir(), 'platform/test-linux-x86_64/failures/expected/image-expected.txt')), 'original win7 result') self.assertMultiLineEqual(self._read(self.tool.filesystem.join(port.layout_tests_dir(), 'platform/test-linux-x86_64/failures/expected/image-expected.txt')), 'original win7 result')
self.assertMultiLineEqual(self._read(self.tool.filesystem.join(port.layout_tests_dir(), 'platform/test-win-win7/failures/expected/image-expected.txt')), 'original win7 result') self.assertMultiLineEqual(self._read(self.tool.filesystem.join(port.layout_tests_dir(), 'platform/test-win-win7/failures/expected/image-expected.txt')), 'original win7 result')
self.assertFalse(self.tool.filesystem.exists(self.tool.filesystem.join(port.layout_tests_dir(), 'platform/mac-leopard/userscripts/another-test-expected.txt'))) self.assertFalse(self.tool.filesystem.exists(self.tool.filesystem.join(port.layout_tests_dir(), 'platform/mac-leopard/userscripts/another-test-expected.txt')))
self.assertMultiLineEqual(out, '{"add": [], "remove-lines": []}\n') self.assertMultiLineEqual(out, '{"add": [], "remove-lines": [], "delete": []}\n')
def test_no_copy_skipped_test(self): def test_no_copy_skipped_test(self):
self.tool.executive = MockExecutive2() self.tool.executive = MockExecutive2()
...@@ -264,7 +266,7 @@ Bug(A) [ Debug ] : fast/css/large-list-of-rules-crash.html [ Failure ] ...@@ -264,7 +266,7 @@ Bug(A) [ Debug ] : fast/css/large-list-of-rules-crash.html [ Failure ]
self._write("userscripts/another-test-expected.html", "generic result") self._write("userscripts/another-test-expected.html", "generic result")
OutputCapture().assert_outputs(self, self.command._rebaseline_test_and_update_expectations, args=[self.options], OutputCapture().assert_outputs(self, self.command._rebaseline_test_and_update_expectations, args=[self.options],
expected_logs="Cannot rebaseline reftest: userscripts/another-test.html\n") expected_logs="Cannot rebaseline reftest: userscripts/another-test.html\n")
self.assertDictEqual(self.command._scm_changes, {'add': [], 'remove-lines': []}) self.assertDictEqual(self.command._scm_changes, {'add': [], 'remove-lines': [], "delete": []})
def test_rebaseline_test_and_print_scm_changes(self): def test_rebaseline_test_and_print_scm_changes(self):
self.command._print_scm_changes = True self.command._print_scm_changes = True
...@@ -301,7 +303,7 @@ Bug(A) [ Debug ] : fast/css/large-list-of-rules-crash.html [ Failure ] ...@@ -301,7 +303,7 @@ Bug(A) [ Debug ] : fast/css/large-list-of-rules-crash.html [ Failure ]
self.assertMultiLineEqual(self._read(self.tool.filesystem.join(port.layout_tests_dir(), 'platform/test-win-win7/failures/expected/image-expected.txt')), 'MOCK Web result, convert 404 to None=True') self.assertMultiLineEqual(self._read(self.tool.filesystem.join(port.layout_tests_dir(), 'platform/test-win-win7/failures/expected/image-expected.txt')), 'MOCK Web result, convert 404 to None=True')
self.assertFalse(self.tool.filesystem.exists(self.tool.filesystem.join(port.layout_tests_dir(), 'platform/test-win-xp/failures/expected/image-expected.txt'))) self.assertFalse(self.tool.filesystem.exists(self.tool.filesystem.join(port.layout_tests_dir(), 'platform/test-win-xp/failures/expected/image-expected.txt')))
self.assertMultiLineEqual(out, '{"add": [], "remove-lines": [{"test": "failures/expected/image.html", "builder": "MOCK Win7"}]}\n') self.assertMultiLineEqual(out, '{"add": [], "remove-lines": [{"test": "failures/expected/image.html", "builder": "MOCK Win7"}], "delete": []}\n')
class TestAbstractParallelRebaselineCommand(_BaseTestCase): class TestAbstractParallelRebaselineCommand(_BaseTestCase):
...@@ -363,7 +365,7 @@ class TestRebaselineJson(_BaseTestCase): ...@@ -363,7 +365,7 @@ class TestRebaselineJson(_BaseTestCase):
# Note that we have one run_in_parallel() call followed by a run_command() # Note that we have one run_in_parallel() call followed by a run_command()
self.assertEqual(self.tool.executive.calls, self.assertEqual(self.tool.executive.calls,
[['echo', '--verbose', 'optimize-baselines', '--suffixes', '', 'userscripts/first-test.html']]) [[['echo', 'optimize-baselines', '--no-modify-scm', '--suffixes', '', 'userscripts/first-test.html', '--verbose']]])
def test_rebaseline_all(self): def test_rebaseline_all(self):
self._setup_mock_builder_data() self._setup_mock_builder_data()
...@@ -376,7 +378,7 @@ class TestRebaselineJson(_BaseTestCase): ...@@ -376,7 +378,7 @@ class TestRebaselineJson(_BaseTestCase):
self.assertEqual(self.tool.executive.calls, self.assertEqual(self.tool.executive.calls,
[[['echo', 'copy-existing-baselines-internal', '--suffixes', 'txt,png', '--builder', 'MOCK builder', '--test', 'userscripts/first-test.html', '--verbose']], [[['echo', 'copy-existing-baselines-internal', '--suffixes', 'txt,png', '--builder', 'MOCK builder', '--test', 'userscripts/first-test.html', '--verbose']],
[['echo', 'rebaseline-test-internal', '--suffixes', 'txt,png', '--builder', 'MOCK builder', '--test', 'userscripts/first-test.html', '--verbose']], [['echo', 'rebaseline-test-internal', '--suffixes', 'txt,png', '--builder', 'MOCK builder', '--test', 'userscripts/first-test.html', '--verbose']],
['echo', '--verbose', 'optimize-baselines', '--suffixes', 'txt,png', 'userscripts/first-test.html']]) [['echo', 'optimize-baselines', '--no-modify-scm', '--suffixes', 'txt,png', 'userscripts/first-test.html', '--verbose']]])
def test_rebaseline_debug(self): def test_rebaseline_debug(self):
self._setup_mock_builder_data() self._setup_mock_builder_data()
...@@ -389,7 +391,7 @@ class TestRebaselineJson(_BaseTestCase): ...@@ -389,7 +391,7 @@ class TestRebaselineJson(_BaseTestCase):
self.assertEqual(self.tool.executive.calls, self.assertEqual(self.tool.executive.calls,
[[['echo', 'copy-existing-baselines-internal', '--suffixes', 'txt,png', '--builder', 'MOCK builder (Debug)', '--test', 'userscripts/first-test.html', '--verbose']], [[['echo', 'copy-existing-baselines-internal', '--suffixes', 'txt,png', '--builder', 'MOCK builder (Debug)', '--test', 'userscripts/first-test.html', '--verbose']],
[['echo', 'rebaseline-test-internal', '--suffixes', 'txt,png', '--builder', 'MOCK builder (Debug)', '--test', 'userscripts/first-test.html', '--verbose']], [['echo', 'rebaseline-test-internal', '--suffixes', 'txt,png', '--builder', 'MOCK builder (Debug)', '--test', 'userscripts/first-test.html', '--verbose']],
['echo', '--verbose', 'optimize-baselines', '--suffixes', 'txt,png', 'userscripts/first-test.html']]) [['echo', 'optimize-baselines', '--no-modify-scm', '--suffixes', 'txt,png', 'userscripts/first-test.html', '--verbose']]])
def test_no_optimize(self): def test_no_optimize(self):
self._setup_mock_builder_data() self._setup_mock_builder_data()
...@@ -671,6 +673,71 @@ class _FakeOptimizer(BaselineOptimizer): ...@@ -671,6 +673,71 @@ class _FakeOptimizer(BaselineOptimizer):
return {} return {}
class TestOptimizeBaselines(_BaseTestCase):
command_constructor = OptimizeBaselines
def _write_test_file(self, port, path, contents):
abs_path = self.tool.filesystem.join(port.layout_tests_dir(), path)
self.tool.filesystem.write_text_file(abs_path, contents)
def setUp(self):
super(TestOptimizeBaselines, self).setUp()
def test_modify_scm(self):
# FIXME: This is a hack to get the unittest and the BaselineOptimize to both use /mock-checkout
# instead of one using /mock-checkout and one using /test-checkout.
default_port = self.tool.port_factory.get()
self.tool.port_factory.get = lambda port_name=None: default_port
test_port = self.tool.port_factory.get('test')
self._write_test_file(test_port, 'another/test.html', "Dummy test contents")
self._write_test_file(test_port, 'platform/mac-snowleopard/another/test-expected.txt', "result A")
self._write_test_file(test_port, 'another/test-expected.txt', "result A")
old_exact_matches = builders._exact_matches
try:
builders._exact_matches = {
"MOCK Leopard Debug": {"port_name": "test-mac-snowleopard", "specifiers": set(["mock-specifier"])},
}
OutputCapture().assert_outputs(self, self.command.execute, args=[
MockOptions(suffixes='txt', no_modify_scm=False, platform='test-mac-snowleopard'),
['another/test.html'],
self.tool,
], expected_stdout='{"add": [], "remove-lines": [], "delete": []}\n')
finally:
builders._exact_matches = old_exact_matches
self.assertFalse(self.tool.filesystem.exists(self.tool.filesystem.join(test_port.layout_tests_dir(), 'platform/mac/another/test-expected.txt')))
self.assertTrue(self.tool.filesystem.exists(self.tool.filesystem.join(test_port.layout_tests_dir(), 'another/test-expected.txt')))
def test_no_modify_scm(self):
# FIXME: This is a hack to get the unittest and the BaselineOptimize to both use /mock-checkout
# instead of one using /mock-checkout and one using /test-checkout.
default_port = self.tool.port_factory.get()
self.tool.port_factory.get = lambda port_name=None: default_port
test_port = self.tool.port_factory.get('test')
self._write_test_file(test_port, 'another/test.html', "Dummy test contents")
self._write_test_file(test_port, 'platform/mac-snowleopard/another/test-expected.txt', "result A")
self._write_test_file(test_port, 'another/test-expected.txt', "result A")
old_exact_matches = builders._exact_matches
try:
builders._exact_matches = {
"MOCK Leopard Debug": {"port_name": "test-mac-snowleopard", "specifiers": set(["mock-specifier"])},
}
OutputCapture().assert_outputs(self, self.command.execute, args=[
MockOptions(suffixes='txt', no_modify_scm=True, platform='test-mac-snowleopard'),
['another/test.html'],
self.tool,
], expected_stdout='{"add": [], "remove-lines": [], "delete": ["/mock-checkout/third_party/WebKit/LayoutTests/platform/mac-snowleopard/another/test-expected.txt"]}\n')
finally:
builders._exact_matches = old_exact_matches
self.assertFalse(self.tool.filesystem.exists(self.tool.filesystem.join(test_port.layout_tests_dir(), 'platform/mac/another/test-expected.txt')))
self.assertTrue(self.tool.filesystem.exists(self.tool.filesystem.join(test_port.layout_tests_dir(), 'another/test-expected.txt')))
class TestAnalyzeBaselines(_BaseTestCase): class TestAnalyzeBaselines(_BaseTestCase):
command_constructor = AnalyzeBaselines command_constructor = AnalyzeBaselines
...@@ -894,9 +961,11 @@ crbug.com/24182 path/to/locally-changed-lined.html [ NeedsRebaseline ] ...@@ -894,9 +961,11 @@ crbug.com/24182 path/to/locally-changed-lined.html [ NeedsRebaseline ]
['echo', 'rebaseline-test-internal', '--suffixes', 'txt', '--builder', 'MOCK Leopard', '--test', 'fast/dom/prototype-taco.html'], ['echo', 'rebaseline-test-internal', '--suffixes', 'txt', '--builder', 'MOCK Leopard', '--test', 'fast/dom/prototype-taco.html'],
['echo', 'rebaseline-test-internal', '--suffixes', 'txt', '--builder', 'MOCK SnowLeopard', '--test', 'fast/dom/prototype-taco.html'], ['echo', 'rebaseline-test-internal', '--suffixes', 'txt', '--builder', 'MOCK SnowLeopard', '--test', 'fast/dom/prototype-taco.html'],
], ],
['echo', 'optimize-baselines', '--suffixes', 'txt,png', 'fast/dom/prototype-chocolate.html'], [
['echo', 'optimize-baselines', '--suffixes', 'png', 'fast/dom/prototype-strawberry.html'], ['echo', 'optimize-baselines', '--no-modify-scm', '--suffixes', 'txt,png', 'fast/dom/prototype-chocolate.html'],
['echo', 'optimize-baselines', '--suffixes', 'txt', 'fast/dom/prototype-taco.html'], ['echo', 'optimize-baselines', '--no-modify-scm', '--suffixes', 'png', 'fast/dom/prototype-strawberry.html'],
['echo', 'optimize-baselines', '--no-modify-scm', '--suffixes', 'txt', 'fast/dom/prototype-taco.html'],
],
['git', 'cl', 'upload', '-f'], ['git', 'cl', 'upload', '-f'],
['git', 'pull'], ['git', 'pull'],
['git', 'cl', 'dcommit', '-f'], ['git', 'cl', 'dcommit', '-f'],
...@@ -969,7 +1038,7 @@ Bug(foo) fast/dom/prototype-taco.html [ NeedsRebaseline ] ...@@ -969,7 +1038,7 @@ Bug(foo) fast/dom/prototype-taco.html [ NeedsRebaseline ]
[ [
['echo', 'rebaseline-test-internal', '--suffixes', 'txt', '--builder', 'MOCK SnowLeopard', '--test', 'fast/dom/prototype-taco.html'], ['echo', 'rebaseline-test-internal', '--suffixes', 'txt', '--builder', 'MOCK SnowLeopard', '--test', 'fast/dom/prototype-taco.html'],
], ],
['echo', 'optimize-baselines', '--suffixes', 'txt', 'fast/dom/prototype-taco.html'], [['echo', 'optimize-baselines', '--no-modify-scm', '--suffixes', 'txt', 'fast/dom/prototype-taco.html']],
['git', 'cl', 'upload', '-f'], ['git', 'cl', 'upload', '-f'],
['git', 'cl', 'set_close'], ['git', 'cl', 'set_close'],
]) ])
...@@ -1029,7 +1098,7 @@ Bug(foo) fast/dom/prototype-taco.html [ NeedsRebaseline ] ...@@ -1029,7 +1098,7 @@ Bug(foo) fast/dom/prototype-taco.html [ NeedsRebaseline ]
['echo', 'rebaseline-test-internal', '--suffixes', 'txt', '--builder', 'MOCK Leopard', '--test', 'fast/dom/prototype-taco.html'], ['echo', 'rebaseline-test-internal', '--suffixes', 'txt', '--builder', 'MOCK Leopard', '--test', 'fast/dom/prototype-taco.html'],
['echo', 'rebaseline-test-internal', '--suffixes', 'txt', '--builder', 'MOCK SnowLeopard', '--test', 'fast/dom/prototype-taco.html'], ['echo', 'rebaseline-test-internal', '--suffixes', 'txt', '--builder', 'MOCK SnowLeopard', '--test', 'fast/dom/prototype-taco.html'],
], ],
['echo', 'optimize-baselines', '--suffixes', 'txt', 'fast/dom/prototype-taco.html'], [['echo', 'optimize-baselines', '--no-modify-scm', '--suffixes', 'txt', 'fast/dom/prototype-taco.html']],
['git', 'cl', 'upload', '-f'], ['git', 'cl', 'upload', '-f'],
['git', 'pull'], ['git', 'pull'],
['git', 'cl', 'dcommit', '-f'], ['git', 'cl', 'dcommit', '-f'],
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment