15c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Copyright (C) 2011, Google Inc. All rights reserved. 25c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# 35c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Redistribution and use in source and binary forms, with or without 45c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# modification, are permitted provided that the following conditions are 55c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# met: 65c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# 75c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# * Redistributions of source code must retain the above copyright 85c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# notice, this list of conditions and the following disclaimer. 95c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# * Redistributions in binary form must reproduce the above 105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# copyright notice, this list of conditions and the following disclaimer 115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# in the documentation and/or other materials provided with the 125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# distribution. 135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# * Neither the name of Google Inc. nor the names of its 145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# contributors may be used to endorse or promote products derived from 155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# this software without specific prior written permission. 165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# 175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)import copy 305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)import logging 315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 3253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)from webkitpy.common.memoized import memoized 335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)_log = logging.getLogger(__name__) 355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# FIXME: Should this function be somewhere more general? 385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)def _invert_dictionary(dictionary): 395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) inverted_dictionary = {} 405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) for key, value in dictionary.items(): 415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if inverted_dictionary.get(value): 425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) inverted_dictionary[value].append(key) 435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) else: 445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) inverted_dictionary[value] = [key] 455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return inverted_dictionary 465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)class BaselineOptimizer(object): 4953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) ROOT_LAYOUT_TESTS_DIRECTORY = 'LayoutTests' 5053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) 517242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci def __init__(self, host, port, port_names, skip_scm_commands): 5253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) self._filesystem = host.filesystem 53f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles) self._skip_scm_commands = skip_scm_commands 54f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles) self._files_to_delete = [] 55f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles) self._files_to_add = [] 5653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) self._scm = host.scm() 577242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci self._default_port = port 587242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci self._ports = {} 597242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci for port_name in port_names: 607242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci self._ports[port_name] = host.port_factory.get(port_name) 617242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci 627242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci self._webkit_base = port.webkit_base() 637242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci self._layout_tests_dir = port.layout_tests_dir() 647242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci 65e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch # Only used by unittests. 66e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch self.new_results_by_directory = [] 67e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch 687242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci def _baseline_root(self, baseline_name): 697242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci virtual_suite = self._virtual_suite(baseline_name) 70e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch if virtual_suite: 71e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch return self._filesystem.join(self.ROOT_LAYOUT_TESTS_DIRECTORY, virtual_suite.name) 72e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch return self.ROOT_LAYOUT_TESTS_DIRECTORY 73e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch 74e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch def _baseline_search_path(self, port, baseline_name): 757242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci virtual_suite = self._virtual_suite(baseline_name) 76e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch if virtual_suite: 77e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch return port.virtual_baseline_search_path(baseline_name) 78e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch return port.baseline_search_path() 7953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) 807242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci def _virtual_suite(self, baseline_name): 817242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci return self._default_port.lookup_virtual_suite(baseline_name) 827242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci 837242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci def _virtual_base(self, baseline_name): 847242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci return self._default_port.lookup_virtual_test_base(baseline_name) 857242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci 867242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci def _relative_baseline_search_paths(self, port, baseline_name): 877242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci baseline_search_path = self._baseline_search_path(port, baseline_name) 887242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci baseline_root = self._baseline_root(baseline_name) 897242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci relative_paths = [self._filesystem.relpath(path, self._webkit_base) for path in baseline_search_path] 907242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci return relative_paths + [baseline_root] 915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 92f5e4ad553afbc08dd2e729bb77e937a9a94d5827Torne (Richard Coles) def _join_directory(self, directory, baseline_name): 93e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch # This code is complicated because both the directory name and the baseline_name have the virtual 94e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch # test suite in the name and the virtual baseline name is not a strict superset of the non-virtual name. 959e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles) # For example, virtual/gpu/fast/canvas/foo-expected.png corresponds to fast/canvas/foo-expected.png and 969e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles) # the baseline directories are like platform/mac/virtual/gpu/fast/canvas. So, to get the path 97e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch # to the baseline in the platform directory, we need to append jsut foo-expected.png to the directory. 987242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci virtual_suite = self._virtual_suite(baseline_name) 99e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch if virtual_suite: 100e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch baseline_name_without_virtual = baseline_name[len(virtual_suite.name) + 1:] 101e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch else: 102e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch baseline_name_without_virtual = baseline_name 103f5e4ad553afbc08dd2e729bb77e937a9a94d5827Torne (Richard Coles) return self._filesystem.join(self._scm.checkout_root, directory, baseline_name_without_virtual) 104f5e4ad553afbc08dd2e729bb77e937a9a94d5827Torne (Richard Coles) 105f5e4ad553afbc08dd2e729bb77e937a9a94d5827Torne (Richard Coles) def read_results_by_directory(self, baseline_name): 106f5e4ad553afbc08dd2e729bb77e937a9a94d5827Torne (Richard Coles) results_by_directory = {} 1077242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci directories = reduce(set.union, map(set, [self._relative_baseline_search_paths(port, baseline_name) for port in self._ports.values()])) 108e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch 10953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) for directory in directories: 110f5e4ad553afbc08dd2e729bb77e937a9a94d5827Torne (Richard Coles) path = self._join_directory(directory, baseline_name) 1115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if self._filesystem.exists(path): 1125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) results_by_directory[directory] = self._filesystem.sha1(path) 1135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return results_by_directory 1145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 115e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch def _results_by_port_name(self, results_by_directory, baseline_name): 1165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) results_by_port_name = {} 1177242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci for port_name, port in self._ports.items(): 1187242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci for directory in self._relative_baseline_search_paths(port, baseline_name): 1195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if directory in results_by_directory: 1205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) results_by_port_name[port_name] = results_by_directory[directory] 1215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) break 1225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return results_by_port_name 1235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 12453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) @memoized 125e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch def _directories_immediately_preceding_root(self, baseline_name): 12653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) directories = set() 1277242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci for port in self._ports.values(): 1287242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci directory = self._filesystem.relpath(self._baseline_search_path(port, baseline_name)[-1], self._webkit_base) 12953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) directories.add(directory) 13053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) return directories 13153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) 132e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch def _optimize_result_for_root(self, new_results_by_directory, baseline_name): 13353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) # The root directory (i.e. LayoutTests) is the only one that doesn't correspond 13453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) # to a specific platform. As such, it's the only one where the baseline in fallback directories 135e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch # immediately before it can be promoted up, i.e. if win and mac 13653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) # have the same baseline, then it can be promoted up to be the LayoutTests baseline. 13753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) # All other baselines can only be removed if they're redundant with a baseline earlier 13853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) # in the fallback order. They can never promoted up. 139e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch directories_immediately_preceding_root = self._directories_immediately_preceding_root(baseline_name) 14053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) 14153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) shared_result = None 14253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) root_baseline_unused = False 14353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) for directory in directories_immediately_preceding_root: 14453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) this_result = new_results_by_directory.get(directory) 14553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) 14653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) # If any of these directories don't have a baseline, there's no optimization we can do. 14753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) if not this_result: 14853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) return 14953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) 15053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) if not shared_result: 15153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) shared_result = this_result 15253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) elif shared_result != this_result: 15353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) root_baseline_unused = True 15453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) 1557242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci baseline_root = self._baseline_root(baseline_name) 156e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch 15753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) # The root baseline is unused if all the directories immediately preceding the root 15853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) # have a baseline, but have different baselines, so the baselines can't be promoted up. 15953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) if root_baseline_unused: 160e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch if baseline_root in new_results_by_directory: 161e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch del new_results_by_directory[baseline_root] 16253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) return 16353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) 164e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch new_results_by_directory[baseline_root] = shared_result 16553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) for directory in directories_immediately_preceding_root: 16653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) del new_results_by_directory[directory] 1675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) def _find_optimal_result_placement(self, baseline_name): 1695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) results_by_directory = self.read_results_by_directory(baseline_name) 170e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch results_by_port_name = self._results_by_port_name(results_by_directory, baseline_name) 1715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) port_names_by_result = _invert_dictionary(results_by_port_name) 1725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 173e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch new_results_by_directory = self._remove_redundant_results(results_by_directory, results_by_port_name, port_names_by_result, baseline_name) 174e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch self._optimize_result_for_root(new_results_by_directory, baseline_name) 1755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return results_by_directory, new_results_by_directory 1775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 178e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch def _remove_redundant_results(self, results_by_directory, results_by_port_name, port_names_by_result, baseline_name): 17953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) new_results_by_directory = copy.copy(results_by_directory) 1807242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci for port_name, port in self._ports.items(): 18153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) current_result = results_by_port_name.get(port_name) 18253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) 18353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) # This happens if we're missing baselines for a port. 18453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) if not current_result: 18553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) continue; 18653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) 1877242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci fallback_path = self._relative_baseline_search_paths(port, baseline_name) 18853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) current_index, current_directory = self._find_in_fallbackpath(fallback_path, current_result, new_results_by_directory) 18953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) for index in range(current_index + 1, len(fallback_path)): 19053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) new_directory = fallback_path[index] 19153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) if not new_directory in new_results_by_directory: 19253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) # No result for this baseline in this directory. 19353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) continue 19453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) elif new_results_by_directory[new_directory] == current_result: 19553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) # Result for new_directory are redundant with the result earlier in the fallback order. 19653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) if current_directory in new_results_by_directory: 19753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) del new_results_by_directory[current_directory] 19853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) else: 19953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) # The new_directory contains a different result, so stop trying to push results up. 2005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) break 2015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 20253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) return new_results_by_directory 2035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) def _find_in_fallbackpath(self, fallback_path, current_result, results_by_directory): 2055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) for index, directory in enumerate(fallback_path): 2065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if directory in results_by_directory and (results_by_directory[directory] == current_result): 2075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return index, directory 2085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) assert False, "result %s not found in fallback_path %s, %s" % (current_result, fallback_path, results_by_directory) 2095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) def _platform(self, filename): 21153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) platform_dir = self.ROOT_LAYOUT_TESTS_DIRECTORY + self._filesystem.sep + 'platform' + self._filesystem.sep 2125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if filename.startswith(platform_dir): 2135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return filename.replace(platform_dir, '').split(self._filesystem.sep)[0] 2145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) platform_dir = self._filesystem.join(self._scm.checkout_root, platform_dir) 2155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if filename.startswith(platform_dir): 2165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return filename.replace(platform_dir, '').split(self._filesystem.sep)[0] 2175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return '(generic)' 2185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) def _move_baselines(self, baseline_name, results_by_directory, new_results_by_directory): 2205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) data_for_result = {} 2215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) for directory, result in results_by_directory.items(): 2225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if not result in data_for_result: 223f5e4ad553afbc08dd2e729bb77e937a9a94d5827Torne (Richard Coles) source = self._join_directory(directory, baseline_name) 2245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) data_for_result[result] = self._filesystem.read_binary_file(source) 2255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 22609380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) scm_files = [] 22709380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) fs_files = [] 2285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) for directory, result in results_by_directory.items(): 2295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if new_results_by_directory.get(directory) != result: 23009380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) file_name = self._join_directory(directory, baseline_name) 23109380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) if self._scm.exists(file_name): 23209380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) scm_files.append(file_name) 23309380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) else: 23409380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) fs_files.append(file_name) 23509380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) 23609380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) if scm_files or fs_files: 23709380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) if scm_files: 23809380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) _log.debug(" Deleting (SCM):") 23909380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) for platform_dir in sorted(self._platform(filename) for filename in scm_files): 24009380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) _log.debug(" " + platform_dir) 241f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles) if self._skip_scm_commands: 242f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles) self._files_to_delete.extend(scm_files) 243f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles) else: 244f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles) self._scm.delete_list(scm_files) 24509380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) if fs_files: 24609380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) _log.debug(" Deleting (file system):") 24709380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) for platform_dir in sorted(self._platform(filename) for filename in fs_files): 24809380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) _log.debug(" " + platform_dir) 24909380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) for filename in fs_files: 25009380295ba73501a205346becac22c6978e4671dTorne (Richard Coles) self._filesystem.remove(filename) 2515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) else: 2525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) _log.debug(" (Nothing to delete)") 2535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) file_names = [] 2555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) for directory, result in new_results_by_directory.items(): 2565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if results_by_directory.get(directory) != result: 257f5e4ad553afbc08dd2e729bb77e937a9a94d5827Torne (Richard Coles) destination = self._join_directory(directory, baseline_name) 2585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) self._filesystem.maybe_make_directory(self._filesystem.split(destination)[0]) 2595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) self._filesystem.write_binary_file(destination, data_for_result[result]) 2605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) file_names.append(destination) 261f5e4ad553afbc08dd2e729bb77e937a9a94d5827Torne (Richard Coles) 2625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if file_names: 2635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) _log.debug(" Adding:") 2645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) for platform_dir in sorted(self._platform(filename) for filename in file_names): 2655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) _log.debug(" " + platform_dir) 266f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles) if self._skip_scm_commands: 267f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles) # Have adds win over deletes. 268f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles) self._files_to_delete = list(set(self._files_to_delete) - set(file_names)) 269f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles) self._files_to_add.extend(file_names) 270f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles) else: 271f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles) self._scm.add_list(file_names) 2725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) else: 2735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) _log.debug(" (Nothing to add)") 2745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) def write_by_directory(self, results_by_directory, writer, indent): 2765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) for path in sorted(results_by_directory): 2775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) writer("%s%s: %s" % (indent, self._platform(path), results_by_directory[path][0:6])) 2785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 279e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch def _optimize_subtree(self, baseline_name): 2805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) basename = self._filesystem.basename(baseline_name) 2815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) results_by_directory, new_results_by_directory = self._find_optimal_result_placement(baseline_name) 28253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) 2835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if new_results_by_directory == results_by_directory: 2845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if new_results_by_directory: 2855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) _log.debug(" %s: (already optimal)" % basename) 2865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) self.write_by_directory(results_by_directory, _log.debug, " ") 2875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) else: 2885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) _log.debug(" %s: (no baselines found)" % basename) 28953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) # This is just used for unittests. Intentionally set it to the old data if we don't modify anything. 290e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch self.new_results_by_directory.append(results_by_directory) 2915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return True 29253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) 293e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch if self._results_by_port_name(results_by_directory, baseline_name) != self._results_by_port_name(new_results_by_directory, baseline_name): 29453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) # This really should never happen. Just a sanity check to make sure the script fails in the case of bugs 29553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) # instead of committing incorrect baselines. 29653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles) _log.error(" %s: optimization failed" % basename) 2975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) self.write_by_directory(results_by_directory, _log.warning, " ") 2985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return False 2995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 3005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) _log.debug(" %s:" % basename) 3015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) _log.debug(" Before: ") 3025c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) self.write_by_directory(results_by_directory, _log.debug, " ") 3035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) _log.debug(" After: ") 3045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) self.write_by_directory(new_results_by_directory, _log.debug, " ") 3055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 3065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) self._move_baselines(baseline_name, results_by_directory, new_results_by_directory) 3075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return True 308e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch 309e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch def _optimize_virtual_root(self, baseline_name, non_virtual_baseline_name): 3107242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci virtual_root_expected_baseline_path = self._filesystem.join(self._layout_tests_dir, baseline_name) 311e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch if not self._filesystem.exists(virtual_root_expected_baseline_path): 312e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch return 313e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch root_sha1 = self._filesystem.sha1(virtual_root_expected_baseline_path) 314e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch 315e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch results_by_directory = self.read_results_by_directory(non_virtual_baseline_name) 316e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch # See if all the immediate predecessors of the virtual root have the same expected result. 3177242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci for port in self._ports.values(): 3187242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci directories = self._relative_baseline_search_paths(port, non_virtual_baseline_name) 319e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch for directory in directories: 320e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch if directory not in results_by_directory: 321e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch continue 322e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch if results_by_directory[directory] != root_sha1: 323e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch return 324e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch break 325e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch 326f5e4ad553afbc08dd2e729bb77e937a9a94d5827Torne (Richard Coles) _log.debug("Deleting redundant virtual root expected result.") 327c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles) if self._skip_scm_commands and virtual_root_expected_baseline_path in self._files_to_add: 328c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles) self._files_to_add.remove(virtual_root_expected_baseline_path) 329c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles) if self._scm.exists(virtual_root_expected_baseline_path): 330c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles) _log.debug(" Deleting (SCM): " + virtual_root_expected_baseline_path) 331c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles) if self._skip_scm_commands: 332c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles) self._files_to_delete.append(virtual_root_expected_baseline_path) 333c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles) else: 334c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles) self._scm.delete(virtual_root_expected_baseline_path) 335f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles) else: 336c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles) _log.debug(" Deleting (file system): " + virtual_root_expected_baseline_path) 337c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles) self._filesystem.remove(virtual_root_expected_baseline_path) 338e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch 339e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch def optimize(self, baseline_name): 340e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch # The virtual fallback path is the same as the non-virtual one tacked on to the bottom of the non-virtual path. 341e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch # See https://docs.google.com/a/chromium.org/drawings/d/1eGdsIKzJ2dxDDBbUaIABrN4aMLD1bqJTfyxNGZsTdmg/edit for 342e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch # a visual representation of this. 343e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch # 344e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch # So, we can optimize the virtual path, then the virtual root and then the regular path. 345e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch 3467242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci self._files_to_delete = [] 3477242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci self._files_to_add = [] 348e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch _log.debug("Optimizing regular fallback path.") 349e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch result = self._optimize_subtree(baseline_name) 3507242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci non_virtual_baseline_name = self._virtual_base(baseline_name) 351e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch if not non_virtual_baseline_name: 352f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles) return result, self._files_to_delete, self._files_to_add 353e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch 354e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch self._optimize_virtual_root(baseline_name, non_virtual_baseline_name) 355e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch 356e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch _log.debug("Optimizing non-virtual fallback path.") 357e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch result |= self._optimize_subtree(non_virtual_baseline_name) 358f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles) return result, self._files_to_delete, self._files_to_add 359