15c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Copyright (c) 2010 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
26d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (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)
29c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)import Queue
305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)import json
315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)import logging
325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)import optparse
3383750176c3ee2cea66c8a9751271026a5901be3aBen Murdochimport re
345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)import sys
35c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)import threading
3683750176c3ee2cea66c8a9751271026a5901be3aBen Murdochimport time
37e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)import traceback
38e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)import urllib
3983750176c3ee2cea66c8a9751271026a5901be3aBen Murdochimport urllib2
405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)from webkitpy.common.checkout.baselineoptimizer import BaselineOptimizer
4253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)from webkitpy.common.memoized import memoized
435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)from webkitpy.common.system.executive import ScriptError
445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)from webkitpy.layout_tests.controllers.test_result_writer import TestResultWriter
455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)from webkitpy.layout_tests.models import test_failures
46e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)from webkitpy.layout_tests.models.test_expectations import TestExpectations, BASELINE_SUFFIX_LIST, SKIP
475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)from webkitpy.layout_tests.port import builders
485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)from webkitpy.layout_tests.port import factory
495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)from webkitpy.tool.multicommandtool import AbstractDeclarativeCommand
505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)_log = logging.getLogger(__name__)
535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# FIXME: Should TestResultWriter know how to compute this string?
565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)def _baseline_name(fs, test_name, suffix):
575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return fs.splitext(test_name)[0] + TestResultWriter.FILENAME_SUFFIX_EXPECTED + "." + suffix
585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)class AbstractRebaseliningCommand(AbstractDeclarativeCommand):
61926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)    # not overriding execute() - pylint: disable=W0223
625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    no_optimize_option = optparse.make_option('--no-optimize', dest='optimize', action='store_false', default=True,
645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        help=('Do not optimize/de-dup the expectations after rebaselining (default is to de-dup automatically). '
655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)              'You can use "webkit-patch optimize-baselines" to optimize separately.'))
665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    platform_options = factory.platform_options(use_globs=True)
685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    results_directory_option = optparse.make_option("--results-directory", help="Local results directory to use")
705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    suffixes_option = optparse.make_option("--suffixes", default=','.join(BASELINE_SUFFIX_LIST), action="store",
725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        help="Comma-separated-list of file types to rebaseline")
735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    def __init__(self, options=None):
755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        super(AbstractRebaseliningCommand, self).__init__(options=options)
765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        self._baseline_suffix_list = BASELINE_SUFFIX_LIST
77f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        self._scm_changes = {'add': [], 'delete': [], 'remove-lines': []}
78f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)
79f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)    def _add_to_scm_later(self, path):
80f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        self._scm_changes['add'].append(path)
81f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)
82f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)    def _delete_from_scm_later(self, path):
83f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        self._scm_changes['delete'].append(path)
845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
86e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdochclass BaseInternalRebaselineCommand(AbstractRebaseliningCommand):
875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    def __init__(self):
88e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch        super(BaseInternalRebaselineCommand, self).__init__(options=[
895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            self.results_directory_option,
905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            self.suffixes_option,
915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            optparse.make_option("--builder", help="Builder to pull new baselines from"),
925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            optparse.make_option("--test", help="Test to rebaseline"),
935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            ])
945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    def _baseline_directory(self, builder_name):
965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        port = self._tool.port_factory.get_from_builder_name(builder_name)
975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        override_dir = builders.rebaseline_override_dir(builder_name)
985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if override_dir:
995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            return self._tool.filesystem.join(port.layout_tests_dir(), 'platform', override_dir)
1005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return port.baseline_version_dir()
1015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
102e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch    def _test_root(self, test_name):
103e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch        return self._tool.filesystem.splitext(test_name)[0]
104e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch
105e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch    def _file_name_for_actual_result(self, test_name, suffix):
106e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch        return "%s-actual.%s" % (self._test_root(test_name), suffix)
107e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch
108e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch    def _file_name_for_expected_result(self, test_name, suffix):
109e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch        return "%s-expected.%s" % (self._test_root(test_name), suffix)
110e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch
111e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch
112e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdochclass CopyExistingBaselinesInternal(BaseInternalRebaselineCommand):
113e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch    name = "copy-existing-baselines-internal"
114e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch    help_text = "Copy existing baselines down one level in the baseline order to ensure new baselines don't break existing passing platforms."
115e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch
11653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    @memoized
11753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    def _immediate_predecessors_in_fallback(self, path_to_rebaseline):
11853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        port_names = self._tool.port_factory.all_port_names()
11953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        immediate_predecessors_in_fallback = []
12053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        for port_name in port_names:
12153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)            port = self._tool.port_factory.get(port_name)
12253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)            if not port.buildbot_archives_baselines():
12353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                continue
12453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)            baseline_search_path = port.baseline_search_path()
12553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)            try:
12653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                index = baseline_search_path.index(path_to_rebaseline)
12753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                if index:
12853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                    immediate_predecessors_in_fallback.append(self._tool.filesystem.basename(baseline_search_path[index - 1]))
12953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)            except ValueError:
13053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                # index throw's a ValueError if the item isn't in the list.
13153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                pass
13253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        return immediate_predecessors_in_fallback
13353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
134521d96ec04ace82590870fb04353ec4f82bb150fTorne (Richard Coles)    def _port_for_primary_baseline(self, baseline):
135521d96ec04ace82590870fb04353ec4f82bb150fTorne (Richard Coles)        for port in [self._tool.port_factory.get(port_name) for port_name in self._tool.port_factory.all_port_names()]:
136521d96ec04ace82590870fb04353ec4f82bb150fTorne (Richard Coles)            if self._tool.filesystem.basename(port.baseline_version_dir()) == baseline:
137521d96ec04ace82590870fb04353ec4f82bb150fTorne (Richard Coles)                return port
138521d96ec04ace82590870fb04353ec4f82bb150fTorne (Richard Coles)        raise Exception("Failed to find port for primary baseline %s." % baseline)
139521d96ec04ace82590870fb04353ec4f82bb150fTorne (Richard Coles)
140e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch    def _copy_existing_baseline(self, builder_name, test_name, suffix):
141e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch        baseline_directory = self._baseline_directory(builder_name)
142e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch        ports = [self._port_for_primary_baseline(baseline) for baseline in self._immediate_predecessors_in_fallback(baseline_directory)]
143e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch
1445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        old_baselines = []
1455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        new_baselines = []
1465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        # Need to gather all the baseline paths before modifying the filesystem since
1485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        # the modifications can affect the results of port.expected_filename.
149e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch        for port in ports:
1505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            old_baseline = port.expected_filename(test_name, "." + suffix)
1515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            if not self._tool.filesystem.exists(old_baseline):
1525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                _log.debug("No existing baseline for %s." % test_name)
1535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                continue
1545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            new_baseline = self._tool.filesystem.join(port.baseline_path(), self._file_name_for_expected_result(test_name, suffix))
1565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            if self._tool.filesystem.exists(new_baseline):
1575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                _log.debug("Existing baseline at %s, not copying over it." % new_baseline)
1585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                continue
1595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
160e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)            expectations = TestExpectations(port, [test_name])
161e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)            if SKIP in expectations.get_expectations(test_name):
162e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)                _log.debug("%s is skipped on %s." % (test_name, port.name()))
163e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)                continue
164e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)
1655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            old_baselines.append(old_baseline)
1665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            new_baselines.append(new_baseline)
1675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        for i in range(len(old_baselines)):
1695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            old_baseline = old_baselines[i]
1705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            new_baseline = new_baselines[i]
1715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            _log.debug("Copying baseline from %s to %s." % (old_baseline, new_baseline))
1735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            self._tool.filesystem.maybe_make_directory(self._tool.filesystem.dirname(new_baseline))
1745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            self._tool.filesystem.copyfile(old_baseline, new_baseline)
1755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            if not self._tool.scm().exists(new_baseline):
176f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)                self._add_to_scm_later(new_baseline)
1775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
178e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch    def execute(self, options, args, tool):
179e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch        for suffix in options.suffixes.split(','):
180e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch            self._copy_existing_baseline(options.builder, options.test, suffix)
181e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch        print json.dumps(self._scm_changes)
182e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch
183e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch
184e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdochclass RebaselineTest(BaseInternalRebaselineCommand):
185e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch    name = "rebaseline-test-internal"
186e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch    help_text = "Rebaseline a single test from a buildbot. Only intended for use by other webkit-patch commands."
187e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch
188e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch    def _results_url(self, builder_name):
189e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch        return self._tool.buildbot_for_builder_name(builder_name).builder_with_name(builder_name).latest_layout_test_results_url()
190e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch
19153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    def _save_baseline(self, data, target_baseline, baseline_directory, test_name, suffix):
1925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if not data:
19353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)            _log.debug("No baseline data to save.")
1945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            return
19553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
1965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        filesystem = self._tool.filesystem
1975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        filesystem.maybe_make_directory(filesystem.dirname(target_baseline))
1985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        filesystem.write_binary_file(target_baseline, data)
1995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if not self._tool.scm().exists(target_baseline):
200f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)            self._add_to_scm_later(target_baseline)
2015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
202e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch    def _rebaseline_test(self, builder_name, test_name, suffix, results_url):
2035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        baseline_directory = self._baseline_directory(builder_name)
2045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        source_baseline = "%s/%s" % (results_url, self._file_name_for_actual_result(test_name, suffix))
2065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        target_baseline = self._tool.filesystem.join(baseline_directory, self._file_name_for_expected_result(test_name, suffix))
2075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        _log.debug("Retrieving %s." % source_baseline)
20953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        self._save_baseline(self._tool.web.get_binary(source_baseline, convert_404_to_None=True), target_baseline, baseline_directory, test_name, suffix)
2105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    def _rebaseline_test_and_update_expectations(self, options):
21253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        port = self._tool.port_factory.get_from_builder_name(options.builder)
21353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        if (port.reference_files(options.test)):
21453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)            _log.warning("Cannot rebaseline reftest: %s", options.test)
21553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)            return
21653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
2175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if options.results_directory:
2185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            results_url = 'file://' + options.results_directory
2195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        else:
2205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            results_url = self._results_url(options.builder)
2215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        self._baseline_suffix_list = options.suffixes.split(',')
22281a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)
2235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        for suffix in self._baseline_suffix_list:
224e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch            self._rebaseline_test(options.builder, options.test, suffix, results_url)
22553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        self._scm_changes['remove-lines'].append({'builder': options.builder, 'test': options.test})
2265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    def execute(self, options, args, tool):
2285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        self._rebaseline_test_and_update_expectations(options)
2295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        print json.dumps(self._scm_changes)
2305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)class OptimizeBaselines(AbstractRebaseliningCommand):
2335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    name = "optimize-baselines"
2345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    help_text = "Reshuffles the baselines for the given tests to use as litte space on disk as possible."
2351e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)    show_in_main_help = True
2365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    argument_names = "TEST_NAMES"
2375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    def __init__(self):
239f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        super(OptimizeBaselines, self).__init__(options=[
240f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)            self.suffixes_option,
241f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)            optparse.make_option('--no-modify-scm', action='store_true', default=False, help='Dump SCM commands as JSON instead of '),
242f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)            ] + self.platform_options)
2435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    def _optimize_baseline(self, optimizer, test_name):
2457242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        files_to_delete = []
2467242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        files_to_add = []
2475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        for suffix in self._baseline_suffix_list:
2485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            baseline_name = _baseline_name(self._tool.filesystem, test_name, suffix)
2497242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci            succeeded, more_files_to_delete, more_files_to_add = optimizer.optimize(baseline_name)
250f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)            if not succeeded:
2515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                print "Heuristics failed to optimize %s" % baseline_name
2527242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci            files_to_delete.extend(more_files_to_delete)
2537242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci            files_to_add.extend(more_files_to_add)
2547242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        return files_to_delete, files_to_add
2555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    def execute(self, options, args, tool):
2575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        self._baseline_suffix_list = options.suffixes.split(',')
2585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        port_names = tool.port_factory.all_port_names(options.platform)
2595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if not port_names:
2605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            print "No port names match '%s'" % options.platform
2615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            return
2625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        port = tool.port_factory.get(port_names[0])
2637242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        optimizer = BaselineOptimizer(tool, port, port_names, skip_scm_commands=options.no_modify_scm)
2647242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        tests = port.tests(args)
2657242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        for test_name in tests:
266f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)            files_to_delete, files_to_add = self._optimize_baseline(optimizer, test_name)
267f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)            for path in files_to_delete:
268f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)                self._delete_from_scm_later(path)
269f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)            for path in files_to_add:
270f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)                self._add_to_scm_later(path)
271f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)
272f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        print json.dumps(self._scm_changes)
2735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)class AnalyzeBaselines(AbstractRebaseliningCommand):
2765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    name = "analyze-baselines"
2775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    help_text = "Analyzes the baselines for the given tests and prints results that are identical."
2781e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)    show_in_main_help = True
2795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    argument_names = "TEST_NAMES"
2805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    def __init__(self):
2825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        super(AnalyzeBaselines, self).__init__(options=[
2835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            self.suffixes_option,
2845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            optparse.make_option('--missing', action='store_true', default=False, help='show missing baselines as well'),
2855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            ] + self.platform_options)
2865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        self._optimizer_class = BaselineOptimizer  # overridable for testing
2875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        self._baseline_optimizer = None
2885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        self._port = None
2895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    def _write(self, msg):
2915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        print msg
2925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    def _analyze_baseline(self, options, test_name):
2945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        for suffix in self._baseline_suffix_list:
2955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            baseline_name = _baseline_name(self._tool.filesystem, test_name, suffix)
2965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            results_by_directory = self._baseline_optimizer.read_results_by_directory(baseline_name)
2975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            if results_by_directory:
2985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                self._write("%s:" % baseline_name)
2995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                self._baseline_optimizer.write_by_directory(results_by_directory, self._write, "  ")
3005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            elif options.missing:
3015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                self._write("%s: (no baselines found)" % baseline_name)
3025c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    def execute(self, options, args, tool):
3045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        self._baseline_suffix_list = options.suffixes.split(',')
3055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        port_names = tool.port_factory.all_port_names(options.platform)
3065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if not port_names:
3075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            print "No port names match '%s'" % options.platform
3085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            return
3095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        self._port = tool.port_factory.get(port_names[0])
3107242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci        self._baseline_optimizer = self._optimizer_class(tool, self._port, port_names, skip_scm_commands=False)
3115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        for test_name in self._port.tests(args):
3125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            self._analyze_baseline(options, test_name)
3135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)class AbstractParallelRebaselineCommand(AbstractRebaseliningCommand):
316926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)    # not overriding execute() - pylint: disable=W0223
3175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3183c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch    def __init__(self, options=None):
3193c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch        super(AbstractParallelRebaselineCommand, self).__init__(options=options)
3203c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch        self._builder_data = {}
3213c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch
3223c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch    def builder_data(self):
3233c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch        if not self._builder_data:
3243c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch            for builder_name in self._release_builders():
3253c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch                builder = self._tool.buildbot_for_builder_name(builder_name).builder_with_name(builder_name)
3263c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch                self._builder_data[builder_name] = builder.latest_layout_test_results()
3273c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch        return self._builder_data
3283c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch
329e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch    # The release builders cycle much faster than the debug ones and cover all the platforms.
330e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch    def _release_builders(self):
331e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch        release_builders = []
332e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch        for builder_name in builders.all_builder_names():
333d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)            if builder_name.find('ASAN') != -1:
334d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)                continue
335e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch            port = self._tool.port_factory.get_from_builder_name(builder_name)
336e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch            if port.test_configuration().build_type == 'release':
337e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch                release_builders.append(builder_name)
338e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch        return release_builders
339e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch
3405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    def _run_webkit_patch(self, args, verbose):
3415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        try:
3425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            verbose_args = ['--verbose'] if verbose else []
3435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            stderr = self._tool.executive.run_command([self._tool.path()] + verbose_args + args, cwd=self._tool.scm().checkout_root, return_stderr=True)
3445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            for line in stderr.splitlines():
34583750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch                _log.warning(line)
3465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        except ScriptError, e:
3475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            _log.error(e)
3485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    def _builders_to_fetch_from(self, builders_to_check):
3505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        # This routine returns the subset of builders that will cover all of the baseline search paths
3515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        # used in the input list. In particular, if the input list contains both Release and Debug
3525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        # versions of a configuration, we *only* return the Release version (since we don't save
3535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        # debug versions of baselines).
3545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        release_builders = set()
3555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        debug_builders = set()
3565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        builders_to_fallback_paths = {}
3575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        for builder in builders_to_check:
3585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            port = self._tool.port_factory.get_from_builder_name(builder)
359e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch            if port.test_configuration().build_type == 'release':
3605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                release_builders.add(builder)
3615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            else:
3625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                debug_builders.add(builder)
3635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        for builder in list(release_builders) + list(debug_builders):
3645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            port = self._tool.port_factory.get_from_builder_name(builder)
3655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            fallback_path = port.baseline_search_path()
3665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            if fallback_path not in builders_to_fallback_paths.values():
3675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                builders_to_fallback_paths[builder] = fallback_path
3685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return builders_to_fallback_paths.keys()
3695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
37081a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)    def _rebaseline_commands(self, test_prefix_list, options):
3715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        path_to_webkit_patch = self._tool.path()
3725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        cwd = self._tool.scm().checkout_root
373e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch        copy_baseline_commands = []
374e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch        rebaseline_commands = []
375f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        lines_to_remove = {}
37681a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)        port = self._tool.port_factory.get()
37781a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)
37881a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)        for test_prefix in test_prefix_list:
37981a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)            for test in port.tests([test_prefix]):
38081a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)                for builder in self._builders_to_fetch_from(test_prefix_list[test_prefix]):
3813c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch                    actual_failures_suffixes = self._suffixes_for_actual_failures(test, builder, test_prefix_list[test_prefix][builder])
3823c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch                    if not actual_failures_suffixes:
383f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)                        # If we're not going to rebaseline the test because it's passing on this
384f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)                        # builder, we still want to remove the line from TestExpectations.
385f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)                        if test not in lines_to_remove:
386f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)                            lines_to_remove[test] = []
387f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)                        lines_to_remove[test].append(builder)
3883c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch                        continue
3893c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch
3903c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch                    suffixes = ','.join(actual_failures_suffixes)
391e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch                    cmd_line = ['--suffixes', suffixes, '--builder', builder, '--test', test]
39281a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)                    if options.results_directory:
39381a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)                        cmd_line.extend(['--results-directory', options.results_directory])
39481a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)                    if options.verbose:
39581a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)                        cmd_line.append('--verbose')
396c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)                    copy_baseline_commands.append(tuple([[self._tool.executable, path_to_webkit_patch, 'copy-existing-baselines-internal'] + cmd_line, cwd]))
397c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)                    rebaseline_commands.append(tuple([[self._tool.executable, path_to_webkit_patch, 'rebaseline-test-internal'] + cmd_line, cwd]))
398f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        return copy_baseline_commands, rebaseline_commands, lines_to_remove
3995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
400f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)    def _serial_commands(self, command_results):
4015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        files_to_add = set()
402f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        files_to_delete = set()
40353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        lines_to_remove = {}
4045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        for output in [result[1].split('\n') for result in command_results]:
4055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            file_added = False
4065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            for line in output:
4075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                try:
4085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                    if line:
40953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                        parsed_line = json.loads(line)
41053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                        if 'add' in parsed_line:
41153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                            files_to_add.update(parsed_line['add'])
412f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)                        if 'delete' in parsed_line:
413f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)                            files_to_delete.update(parsed_line['delete'])
41453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                        if 'remove-lines' in parsed_line:
41553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                            for line_to_remove in parsed_line['remove-lines']:
41653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                                test = line_to_remove['test']
41753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                                builder = line_to_remove['builder']
41853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                                if test not in lines_to_remove:
41953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                                    lines_to_remove[test] = []
42053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                                lines_to_remove[test].append(builder)
4215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                        file_added = True
4225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                except ValueError:
4235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                    _log.debug('"%s" is not a JSON object, ignoring' % line)
4245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
4255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            if not file_added:
4265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                _log.debug('Could not add file based off output "%s"' % output)
4275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
428f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        return list(files_to_add), list(files_to_delete), lines_to_remove
4295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
43081a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)    def _optimize_baselines(self, test_prefix_list, verbose=False):
431f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        optimize_commands = []
43281a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)        for test in test_prefix_list:
4335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            all_suffixes = set()
43481a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)            for builder in self._builders_to_fetch_from(test_prefix_list[test]):
4353c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch                all_suffixes.update(self._suffixes_for_actual_failures(test, builder, test_prefix_list[test][builder]))
436f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)
4375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            # FIXME: We should propagate the platform options as well.
438f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)            cmd_line = ['--no-modify-scm', '--suffixes', ','.join(all_suffixes), test]
439f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)            if verbose:
440f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)                cmd_line.append('--verbose')
441f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)
442f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)            path_to_webkit_patch = self._tool.path()
443f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)            cwd = self._tool.scm().checkout_root
444c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)            optimize_commands.append(tuple([[self._tool.executable, path_to_webkit_patch, 'optimize-baselines'] + cmd_line, cwd]))
445f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        return optimize_commands
4465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
44753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    def _update_expectations_files(self, lines_to_remove):
448f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        # FIXME: This routine is way too expensive. We're creating O(n ports) TestExpectations objects.
449f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        # This is slow and uses a lot of memory.
450f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        tests = lines_to_remove.keys()
451f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        to_remove = []
452f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)
453f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        # This is so we remove lines for builders that skip this test, e.g. Android skips most
454f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        # tests and we don't want to leave stray [ Android ] lines in TestExpectations..
4555d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)        # This is only necessary for "webkit-patch rebaseline" and for rebaselining expected
4565d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)        # failures from garden-o-matic. rebaseline-expectations and auto-rebaseline will always
4575d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)        # pass the exact set of ports to rebaseline.
458f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        for port_name in self._tool.port_factory.all_port_names():
459f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)            port = self._tool.port_factory.get(port_name)
460f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)            generic_expectations = TestExpectations(port, tests=tests, include_overrides=False)
461f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)            full_expectations = TestExpectations(port, tests=tests, include_overrides=True)
462f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)            for test in tests:
463f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)                if self._port_skips_test(port, test, generic_expectations, full_expectations):
464f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)                    for test_configuration in port.all_test_configurations():
465f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)                        if test_configuration.version == port.test_configuration().version:
466f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)                            to_remove.append((test, test_configuration))
467f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)
46853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        for test in lines_to_remove:
46953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)            for builder in lines_to_remove[test]:
47053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                port = self._tool.port_factory.get_from_builder_name(builder)
47153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                for test_configuration in port.all_test_configurations():
47253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                    if test_configuration.version == port.test_configuration().version:
473f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)                        to_remove.append((test, test_configuration))
47453e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
475f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        port = self._tool.port_factory.get()
476f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        expectations = TestExpectations(port, include_overrides=False)
477f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        expectationsString = expectations.remove_configurations(to_remove)
478f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        path = port.path_to_generic_test_expectations_file()
479f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        self._tool.filesystem.write_text_file(path, expectationsString)
480c0e19a689c8ac22cdc96b291a8d33a5d3b0b34a4Torne (Richard Coles)
481f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)    def _port_skips_test(self, port, test, generic_expectations, full_expectations):
4829bbd2f5e390b01907d97ecffde80aa1b06113aacTorne (Richard Coles)        fs = port.host.filesystem
4839bbd2f5e390b01907d97ecffde80aa1b06113aacTorne (Richard Coles)        if port.default_smoke_test_only():
4849bbd2f5e390b01907d97ecffde80aa1b06113aacTorne (Richard Coles)            smoke_test_filename = fs.join(port.layout_tests_dir(), 'SmokeTests')
4859bbd2f5e390b01907d97ecffde80aa1b06113aacTorne (Richard Coles)            if fs.exists(smoke_test_filename) and test not in fs.read_text_file(smoke_test_filename):
4869bbd2f5e390b01907d97ecffde80aa1b06113aacTorne (Richard Coles)                return True
4879bbd2f5e390b01907d97ecffde80aa1b06113aacTorne (Richard Coles)
4889bbd2f5e390b01907d97ecffde80aa1b06113aacTorne (Richard Coles)        return (SKIP in full_expectations.get_expectations(test) and
4899bbd2f5e390b01907d97ecffde80aa1b06113aacTorne (Richard Coles)                SKIP not in generic_expectations.get_expectations(test))
4909bbd2f5e390b01907d97ecffde80aa1b06113aacTorne (Richard Coles)
491e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch    def _run_in_parallel_and_update_scm(self, commands):
4925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        command_results = self._tool.executive.run_in_parallel(commands)
4935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        log_output = '\n'.join(result[2] for result in command_results).replace('\n\n', '\n')
4945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        for line in log_output.split('\n'):
4955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            if line:
4965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                print >> sys.stderr, line  # FIXME: Figure out how to log properly.
4975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
498f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        files_to_add, files_to_delete, lines_to_remove = self._serial_commands(command_results)
499f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        if files_to_delete:
500f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)            self._tool.scm().delete_list(files_to_delete)
5015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if files_to_add:
502f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)            self._tool.scm().add_list(files_to_add)
503f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        return lines_to_remove
504e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch
505e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch    def _rebaseline(self, options, test_prefix_list):
506e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch        for test, builders_to_check in sorted(test_prefix_list.items()):
507e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch            _log.info("Rebaselining %s" % test)
508e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch            for builder, suffixes in sorted(builders_to_check.items()):
509e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch                _log.debug("  %s: %s" % (builder, ",".join(suffixes)))
510e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch
511f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        copy_baseline_commands, rebaseline_commands, extra_lines_to_remove = self._rebaseline_commands(test_prefix_list, options)
512f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        lines_to_remove = {}
513f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)
5148abfc5808a4e34d6e03867af8bc440dee641886fTorne (Richard Coles)        if copy_baseline_commands:
5158abfc5808a4e34d6e03867af8bc440dee641886fTorne (Richard Coles)            self._run_in_parallel_and_update_scm(copy_baseline_commands)
5168abfc5808a4e34d6e03867af8bc440dee641886fTorne (Richard Coles)        if rebaseline_commands:
517f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)            lines_to_remove = self._run_in_parallel_and_update_scm(rebaseline_commands)
518f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)
519f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        for test in extra_lines_to_remove:
520f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)            if test in lines_to_remove:
521f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)                lines_to_remove[test] = lines_to_remove[test] + extra_lines_to_remove[test]
522f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)            else:
523f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)                lines_to_remove[test] = extra_lines_to_remove[test]
524f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)
525f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)        if lines_to_remove:
526f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)            self._update_expectations_files(lines_to_remove)
5275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
5285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if options.optimize:
529f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)            self._run_in_parallel_and_update_scm(self._optimize_baselines(test_prefix_list, options.verbose))
5305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
5313c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch    def _suffixes_for_actual_failures(self, test, builder_name, existing_suffixes):
5323c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch        actual_results = self.builder_data()[builder_name].actual_results(test)
5333c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch        if not actual_results:
5343c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch            return set()
5353c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch        return set(existing_suffixes) & TestExpectations.suffixes_for_actual_expectations_string(actual_results)
5363c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch
5375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
5385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)class RebaselineJson(AbstractParallelRebaselineCommand):
5395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    name = "rebaseline-json"
5405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    help_text = "Rebaseline based off JSON passed to stdin. Intended to only be called from other scripts."
5415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
5425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    def __init__(self,):
5435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        super(RebaselineJson, self).__init__(options=[
5445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            self.no_optimize_option,
5455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            self.results_directory_option,
5465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            ])
5475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
5485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    def execute(self, options, args, tool):
5495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        self._rebaseline(options, json.loads(sys.stdin.read()))
5505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
5515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
5525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)class RebaselineExpectations(AbstractParallelRebaselineCommand):
5535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    name = "rebaseline-expectations"
5545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    help_text = "Rebaselines the tests indicated in TestExpectations."
5551e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)    show_in_main_help = True
5565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
5575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    def __init__(self):
5585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        super(RebaselineExpectations, self).__init__(options=[
5595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            self.no_optimize_option,
5605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            ] + self.platform_options)
56181a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)        self._test_prefix_list = None
5625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
5635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    def _tests_to_rebaseline(self, port):
5645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        tests_to_rebaseline = {}
565fff8884795cb540f87cf6e6d67b629519b00eb8bBen Murdoch        for path, value in port.expectations_dict().items():
566fff8884795cb540f87cf6e6d67b629519b00eb8bBen Murdoch            expectations = TestExpectations(port, include_overrides=False, expectations_dict={path: value})
567fff8884795cb540f87cf6e6d67b629519b00eb8bBen Murdoch            for test in expectations.get_rebaselining_failures():
568fff8884795cb540f87cf6e6d67b629519b00eb8bBen Murdoch                suffixes = TestExpectations.suffixes_for_expectations(expectations.get_expectations(test))
569fff8884795cb540f87cf6e6d67b629519b00eb8bBen Murdoch                tests_to_rebaseline[test] = suffixes or BASELINE_SUFFIX_LIST
5705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return tests_to_rebaseline
5715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
5725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    def _add_tests_to_rebaseline_for_port(self, port_name):
5735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        builder_name = builders.builder_name_for_port_name(port_name)
5745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if not builder_name:
5755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            return
5765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        tests = self._tests_to_rebaseline(self._tool.port_factory.get(port_name)).items()
5775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
5785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if tests:
5795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            _log.info("Retrieving results for %s from %s." % (port_name, builder_name))
5805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
5815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        for test_name, suffixes in tests:
5825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            _log.info("    %s (%s)" % (test_name, ','.join(suffixes)))
58381a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)            if test_name not in self._test_prefix_list:
58481a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)                self._test_prefix_list[test_name] = {}
58581a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)            self._test_prefix_list[test_name][builder_name] = suffixes
5865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
5875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    def execute(self, options, args, tool):
5885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        options.results_directory = None
58981a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)        self._test_prefix_list = {}
5905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        port_names = tool.port_factory.all_port_names(options.platform)
5915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        for port_name in port_names:
5925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            self._add_tests_to_rebaseline_for_port(port_name)
59381a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)        if not self._test_prefix_list:
5945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            _log.warning("Did not find any tests marked Rebaseline.")
5955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            return
5965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
59781a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)        self._rebaseline(options, self._test_prefix_list)
5985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
5995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
6005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)class Rebaseline(AbstractParallelRebaselineCommand):
6015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    name = "rebaseline"
6025c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    help_text = "Rebaseline tests with results from the build bots. Shows the list of failing tests on the builders if no test names are provided."
6031e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)    show_in_main_help = True
6045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    argument_names = "[TEST_NAMES]"
6055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
6065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    def __init__(self):
6075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        super(Rebaseline, self).__init__(options=[
6085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            self.no_optimize_option,
6095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            # FIXME: should we support the platform options in addition to (or instead of) --builders?
6105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            self.suffixes_option,
611e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch            self.results_directory_option,
6125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            optparse.make_option("--builders", default=None, action="append", help="Comma-separated-list of builders to pull new baselines from (can also be provided multiple times)"),
6135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            ])
6145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
6155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    def _builders_to_pull_from(self):
616e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch        chosen_names = self._tool.user.prompt_with_list("Which builder to pull results from:", self._release_builders(), can_choose_multiple=True)
6175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return [self._builder_with_name(name) for name in chosen_names]
6185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
6195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    def _builder_with_name(self, name):
6205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return self._tool.buildbot_for_builder_name(name).builder_with_name(name)
6215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
6225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    def execute(self, options, args, tool):
623e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch        if not args:
624e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch            _log.error("Must list tests to rebaseline.")
625e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch            return
626e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch
6275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if options.builders:
6285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            builders_to_check = []
6295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            for builder_names in options.builders:
6305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                builders_to_check += [self._builder_with_name(name) for name in builder_names.split(",")]
6315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        else:
6325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            builders_to_check = self._builders_to_pull_from()
6335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
63481a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)        test_prefix_list = {}
6355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        suffixes_to_update = options.suffixes.split(",")
6365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
6375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        for builder in builders_to_check:
638e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch            for test in args:
63981a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)                if test not in test_prefix_list:
64081a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)                    test_prefix_list[test] = {}
64181a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)                test_prefix_list[test][builder.name()] = suffixes_to_update
6425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
6435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if options.verbose:
64481a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)            _log.debug("rebaseline-json: " + str(test_prefix_list))
6455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
64681a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)        self._rebaseline(options, test_prefix_list)
64783750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
64883750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
64983750176c3ee2cea66c8a9751271026a5901be3aBen Murdochclass AutoRebaseline(AbstractParallelRebaselineCommand):
65083750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch    name = "auto-rebaseline"
65183750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch    help_text = "Rebaselines any NeedsRebaseline lines in TestExpectations that have cycled through all the bots."
65283750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch    AUTO_REBASELINE_BRANCH_NAME = "auto-rebaseline-temporary-branch"
65383750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
65483750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch    # Rietveld uploader stinks. Limit the number of rebaselines in a given patch to keep upload from failing.
65583750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch    # FIXME: http://crbug.com/263676 Obviously we should fix the uploader here.
656d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)    MAX_LINES_TO_REBASELINE = 200
657d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)
658d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)    SECONDS_BEFORE_GIVING_UP = 300
65983750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
66083750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch    def __init__(self):
66183750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch        super(AutoRebaseline, self).__init__(options=[
66283750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            # FIXME: Remove this option.
66383750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            self.no_optimize_option,
66483750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            # FIXME: Remove this option.
66583750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            self.results_directory_option,
66683750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            ])
66783750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
6685d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)    def bot_revision_data(self):
66983750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch        revisions = []
670fff8884795cb540f87cf6e6d67b629519b00eb8bBen Murdoch        for result in self.builder_data().values():
67183750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            if result.run_was_interrupted():
672e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)                _log.error("Can't rebaseline because the latest run on %s exited early." % result.builder_name())
673e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)                return []
674e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)            revisions.append({
675e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)                "builder": result.builder_name(),
676e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)                "revision": result.blink_revision(),
677e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)            })
678e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)        return revisions
679e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)
6805d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)    def tests_to_rebaseline(self, tool, min_revision, print_revisions):
68183750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch        port = tool.port_factory.get()
68283750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch        expectations_file_path = port.path_to_generic_test_expectations_file()
68383750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
68483750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch        tests = set()
68583750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch        revision = None
68683750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch        author = None
68783750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch        bugs = set()
688e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)        has_any_needs_rebaseline_lines = False
68983750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
69083750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch        for line in tool.scm().blame(expectations_file_path).split("\n"):
691f79f16f17ddc4f842d7b7a38603e280e94be826aTorne (Richard Coles)            comment_index = line.find("#")
692f79f16f17ddc4f842d7b7a38603e280e94be826aTorne (Richard Coles)            if comment_index == -1:
693f79f16f17ddc4f842d7b7a38603e280e94be826aTorne (Richard Coles)                comment_index = len(line)
694f79f16f17ddc4f842d7b7a38603e280e94be826aTorne (Richard Coles)            line_without_comments = re.sub(r"\s+", " ", line[:comment_index].strip())
695f79f16f17ddc4f842d7b7a38603e280e94be826aTorne (Richard Coles)
696f79f16f17ddc4f842d7b7a38603e280e94be826aTorne (Richard Coles)            if "NeedsRebaseline" not in line_without_comments:
69783750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch                continue
698e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)
699e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)            has_any_needs_rebaseline_lines = True
700e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)
701f79f16f17ddc4f842d7b7a38603e280e94be826aTorne (Richard Coles)            parsed_line = re.match("^(\S*)[^(]*\((\S*).*?([^ ]*)\ \[[^[]*$", line_without_comments)
70283750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
70383750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            commit_hash = parsed_line.group(1)
70483750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            svn_revision = tool.scm().svn_revision_from_git_commit(commit_hash)
70583750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
70683750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            test = parsed_line.group(3)
70783750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            if print_revisions:
70883750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch                _log.info("%s is waiting for r%s" % (test, svn_revision))
70983750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
71083750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            if not svn_revision or svn_revision > min_revision:
71183750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch                continue
71283750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
71383750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            if revision and svn_revision != revision:
71483750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch                continue
71583750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
71683750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            if not revision:
71783750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch                revision = svn_revision
71883750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch                author = parsed_line.group(2)
71983750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
720f79f16f17ddc4f842d7b7a38603e280e94be826aTorne (Richard Coles)            bugs.update(re.findall("crbug\.com\/(\d+)", line_without_comments))
72183750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            tests.add(test)
72283750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
72383750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            if len(tests) >= self.MAX_LINES_TO_REBASELINE:
72483750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch                _log.info("Too many tests to rebaseline in one patch. Doing the first %d." % self.MAX_LINES_TO_REBASELINE)
72583750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch                break
72683750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
727e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)        return tests, revision, author, bugs, has_any_needs_rebaseline_lines
72883750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
72983750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch    def link_to_patch(self, revision):
73083750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch        return "http://src.chromium.org/viewvc/blink?view=revision&revision=" + str(revision)
73183750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
73283750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch    def commit_message(self, author, revision, bugs):
73383750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch        bug_string = ""
73483750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch        if bugs:
73583750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            bug_string = "BUG=%s\n" % ",".join(bugs)
73683750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
73783750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch        return """Auto-rebaseline for r%s
73883750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
73983750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch%s
74083750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
74183750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch%sTBR=%s
74283750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch""" % (revision, self.link_to_patch(revision), bug_string, author)
74383750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
74483750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch    def get_test_prefix_list(self, tests):
74583750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch        test_prefix_list = {}
746fff8884795cb540f87cf6e6d67b629519b00eb8bBen Murdoch        lines_to_remove = {}
74783750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
74883750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch        for builder_name in self._release_builders():
74983750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            port_name = builders.port_name_for_builder_name(builder_name)
75083750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            port = self._tool.port_factory.get(port_name)
75183750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            expectations = TestExpectations(port, include_overrides=True)
75283750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            for test in expectations.get_needs_rebaseline_failures():
75383750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch                if test not in tests:
75483750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch                    continue
755fff8884795cb540f87cf6e6d67b629519b00eb8bBen Murdoch
75683750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch                if test not in test_prefix_list:
7573c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch                    lines_to_remove[test] = []
75883750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch                    test_prefix_list[test] = {}
7593c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch                lines_to_remove[test].append(builder_name)
7603c9e4aeaee9f9b0a9a814da07bcb33319c7ea363Ben Murdoch                test_prefix_list[test][builder_name] = BASELINE_SUFFIX_LIST
76183750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
762fff8884795cb540f87cf6e6d67b629519b00eb8bBen Murdoch        return test_prefix_list, lines_to_remove
76383750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
76483750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch    def _run_git_cl_command(self, options, command):
76583750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch        subprocess_command = ['git', 'cl'] + command
76683750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch        if options.verbose:
76783750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            subprocess_command.append('--verbose')
768d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)
7695d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)        process = self._tool.executive.popen(subprocess_command, stdout=self._tool.executive.PIPE, stderr=self._tool.executive.STDOUT)
770d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)        last_output_time = time.time()
771d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)
772d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)        # git cl sometimes completely hangs. Bail if we haven't gotten any output to stdout/stderr in a while.
773d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)        while process.poll() == None and time.time() < last_output_time + self.SECONDS_BEFORE_GIVING_UP:
7745d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)            # FIXME: This doesn't make any sense. readline blocks, so all this code to
7755d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)            # try and bail is useless. Instead, we should do the readline calls on a
7765d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)            # subthread. Then the rest of this code would make sense.
777d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)            out = process.stdout.readline().rstrip('\n')
778d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)            if out:
779d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)                last_output_time = time.time()
780d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)                _log.info(out)
781d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)
782d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)        if process.poll() == None:
783d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)            _log.error('Command hung: %s' % subprocess_command)
784d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)            return False
785d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)        return True
78683750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
78783750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch    # FIXME: Move this somewhere more general.
78883750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch    def tree_status(self):
78983750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch        blink_tree_status_url = "http://blink-status.appspot.com/status"
79083750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch        status = urllib2.urlopen(blink_tree_status_url).read().lower()
791e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)        if status.find('closed') != -1 or status == "0":
79283750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            return 'closed'
793e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)        elif status.find('open') != -1 or status == "1":
79483750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            return 'open'
79583750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch        return 'unknown'
79683750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
79783750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch    def execute(self, options, args, tool):
79883750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch        if tool.scm().executable_name == "svn":
79983750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            _log.error("Auto rebaseline only works with a git checkout.")
80083750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            return
80183750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
80283750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch        if tool.scm().has_working_directory_changes():
80383750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            _log.error("Cannot proceed with working directory changes. Clean working directory first.")
80483750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            return
80583750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
8065d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)        revision_data = self.bot_revision_data()
807e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)        if not revision_data:
80883750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            return
80983750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
810e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)        min_revision = int(min([item["revision"] for item in revision_data]))
8115d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)        tests, revision, author, bugs, has_any_needs_rebaseline_lines = self.tests_to_rebaseline(tool, min_revision, print_revisions=options.verbose)
812e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)
813e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)        if options.verbose:
814e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)            _log.info("Min revision across all bots is %s." % min_revision)
815e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)            for item in revision_data:
816e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)                _log.info("%s: r%s" % (item["builder"], item["revision"]))
81783750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
81883750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch        if not tests:
81983750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            _log.debug('No tests to rebaseline.')
82083750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            return
82183750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
82283750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch        if self.tree_status() == 'closed':
82383750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            _log.info('Cannot proceed. Tree is closed.')
82483750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            return
82583750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
826e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)        _log.info('Rebaselining %s for r%s by %s.' % (list(tests), revision, author))
827e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)
828e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)        test_prefix_list, lines_to_remove = self.get_test_prefix_list(tests)
829e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)
830e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        did_finish = False
83183750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch        try:
83283750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            old_branch_name = tool.scm().current_branch()
83383750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            tool.scm().delete_branch(self.AUTO_REBASELINE_BRANCH_NAME)
83483750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            tool.scm().create_clean_branch(self.AUTO_REBASELINE_BRANCH_NAME)
83583750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
836fff8884795cb540f87cf6e6d67b629519b00eb8bBen Murdoch            # If the tests are passing everywhere, then this list will be empty. We don't need
837fff8884795cb540f87cf6e6d67b629519b00eb8bBen Murdoch            # to rebaseline, but we'll still need to update TestExpectations.
838fff8884795cb540f87cf6e6d67b629519b00eb8bBen Murdoch            if test_prefix_list:
839fff8884795cb540f87cf6e6d67b629519b00eb8bBen Murdoch                self._rebaseline(options, test_prefix_list)
84083750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
84183750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            tool.scm().commit_locally_with_message(self.commit_message(author, revision, bugs))
84283750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
84383750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            # FIXME: It would be nice if we could dcommit the patch without uploading, but still
84483750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            # go through all the precommit hooks. For rebaselines with lots of files, uploading
84583750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            # takes a long time and sometimes fails, but we don't want to commit if, e.g. the
84683750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            # tree is closed.
847d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)            did_finish = self._run_git_cl_command(options, ['upload', '-f'])
8483464d02a173573db42f8ee6bb07bb74fabf4f5f2Ben Murdoch
849d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)            if did_finish:
850d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)                # Uploading can take a very long time. Do another pull to make sure TestExpectations is up to date,
851d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)                # so the dcommit can go through.
852d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)                # FIXME: Log the pull and dcommit stdout/stderr to the log-server.
853d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)                tool.executive.run_command(['git', 'pull'])
8543464d02a173573db42f8ee6bb07bb74fabf4f5f2Ben Murdoch
855d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)                self._run_git_cl_command(options, ['dcommit', '-f'])
856e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        except Exception as e:
857e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)            _log.error(e)
85883750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch        finally:
859e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)            if did_finish:
860e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)                self._run_git_cl_command(options, ['set_close'])
86183750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            tool.scm().ensure_cleanly_tracking_remote_master()
86283750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            tool.scm().checkout_branch(old_branch_name)
86383750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            tool.scm().delete_branch(self.AUTO_REBASELINE_BRANCH_NAME)
86483750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
86583750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
86683750176c3ee2cea66c8a9751271026a5901be3aBen Murdochclass RebaselineOMatic(AbstractDeclarativeCommand):
86783750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch    name = "rebaseline-o-matic"
86883750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch    help_text = "Calls webkit-patch auto-rebaseline in a loop."
8691e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)    show_in_main_help = True
87083750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
87183750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch    SLEEP_TIME_IN_SECONDS = 30
8725d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)    LOG_SERVER = 'blinkrebaseline.appspot.com'
873c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)    QUIT_LOG = '##QUIT##'
8745d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)
8755d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)    # Uploaded log entries append to the existing entry unless the
8765d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)    # newentry flag is set. In that case it starts a new entry to
8775d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)    # start appending to.
8785d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)    def _log_to_server(self, log='', is_new_entry=False):
8795d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)        query = {
8805d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)            'log': log,
8815d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)        }
8825d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)        if is_new_entry:
8835d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)            query['newentry'] = 'on'
884c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)        try:
885c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)            urllib2.urlopen("http://" + self.LOG_SERVER + "/updatelog", data=urllib.urlencode(query))
886c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)        except:
887c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)            traceback.print_exc(file=sys.stderr)
888c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)
889c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)    def _log_to_server_thread(self):
890c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)        is_new_entry = True
891c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)        while True:
892c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)            messages = [self._log_queue.get()]
893c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)            while not self._log_queue.empty():
894c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)                messages.append(self._log_queue.get())
895c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)            self._log_to_server('\n'.join(messages), is_new_entry=is_new_entry)
896c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)            is_new_entry = False
897c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)            if self.QUIT_LOG in messages:
898c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)                return
899c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)
900c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)    def _post_log_to_server(self, log):
901c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)        self._log_queue.put(log)
9025d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)
9035d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)    def _log_line(self, handle):
9045d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)        out = handle.readline().rstrip('\n')
9055d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)        if out:
9065d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)            if self._verbose:
9075d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)                print out
908c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)            self._post_log_to_server(out)
9095d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)        return out
9105d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)
9115d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)    def _run_logged_command(self, command):
9125d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)        process = self._tool.executive.popen(command, stdout=self._tool.executive.PIPE, stderr=self._tool.executive.STDOUT)
9135d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)
9145d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)        out = self._log_line(process.stdout)
9155d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)        while out:
9165d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)            # FIXME: This should probably batch up lines if they're available and log to the server once.
9175d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)            out = self._log_line(process.stdout)
9185d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)
9195d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)    def _do_one_rebaseline(self):
920c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)        self._log_queue = Queue.Queue(256)
921c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)        log_thread = threading.Thread(name='LogToServer', target=self._log_to_server_thread)
922c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)        log_thread.start()
9235d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)        try:
9245d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)            old_branch_name = self._tool.scm().current_branch()
9255d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)            self._run_logged_command(['git', 'pull'])
9265d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)            rebaseline_command = [self._tool.filesystem.join(self._tool.scm().checkout_root, 'Tools', 'Scripts', 'webkit-patch'), 'auto-rebaseline']
9275d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)            if self._verbose:
9285d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)                rebaseline_command.append('--verbose')
9295d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)            self._run_logged_command(rebaseline_command)
9305d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)        except:
931e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)            self._log_queue.put(self.QUIT_LOG)
9325d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)            traceback.print_exc(file=sys.stderr)
9335d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)            # Sometimes git crashes and leaves us on a detached head.
9345d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)            self._tool.scm().checkout_branch(old_branch_name)
935e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)        else:
936e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)            self._log_queue.put(self.QUIT_LOG)
937c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)        log_thread.join()
93883750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch
93983750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch    def execute(self, options, args, tool):
9405d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)        self._verbose = options.verbose
94183750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch        while True:
9425d92fedcae5e801a8b224de090094f2d9df0b54aTorne (Richard Coles)            self._do_one_rebaseline()
94383750176c3ee2cea66c8a9751271026a5901be3aBen Murdoch            time.sleep(self.SLEEP_TIME_IN_SECONDS)
944