Title: [118836] trunk/Tools
Revision
118836
Author
[email protected]
Date
2012-05-29 14:30:41 -0700 (Tue, 29 May 2012)

Log Message

garden-o-matic should rebaseline baselines in parallel
https://bugs.webkit.org/show_bug.cgi?id=87451

Reviewed by Ojan Vafai.

This modifies garden-o-matic so that we will fetch all
of the baselines in parallel from the bots, and then
optimize the baselines serially (since that should be fairly
quick and we get into trouble if we optimize in parallel due
to concurrent access to the source control system).

This change adds a flag to webkit-patch rebaseline-test (--print-scm-changes)
so that the files that need to be modified in the SCM are returned to
garden-o-matic rather than actually added or removed. This is done so that
we can run multiple rebaseline-tests in parallel (we can't modify the SCM
concurrently). In order to safely return the files (as a JSON dict) I
needed to shift all of the logging in rebaseline-test to use the logging
module (and hence log to stderr).

I also had to stub out a line in chromium.py that would get confused if
the skia overrides file didn't exist in a mock checkout.

* Scripts/webkitpy/common/system/executive.py:
(Executive.popen):
(Executive):
(Executive.run_in_parallel):
(_run_command_thunk):
* Scripts/webkitpy/common/system/executive_mock.py:
(MockExecutive.run_in_parallel):
* Scripts/webkitpy/common/system/executive_unittest.py:
(ExecutiveTest.test_run_in_parallel):
* Scripts/webkitpy/layout_tests/port/chromium.py:
(ChromiumPort._expectations_file_contents):
* Scripts/webkitpy/tool/commands/rebaseline.py:
(AbstractRebaseliningCommand.__init__):
(RebaselineTest.__init__):
(RebaselineTest._copy_existing_baseline):
(RebaselineTest._save_baseline):
(RebaselineTest):
(RebaselineTest._add_to_scm):
(RebaselineTest._rebaseline_test):
(RebaselineTest.execute):
(RebaselineExpectations._rebaseline_port):
(RebaselineExpectations.execute):
(Rebaseline._builder_to_pull_from):
(Rebaseline.execute):
* Scripts/webkitpy/tool/commands/rebaseline_unittest.py:
(test_rebaseline_updates_expectations_file):
(test_rebaseline_test):
(test_rebaseline_test_and_print_scm_changes):
(test_rebaseline_and_copy_test):
(test_rebaseline_and_copy_test_no_existing_result):
(test_rebaseline_and_copy_test_with_lion_result):
(test_rebaseline_and_copy_no_overwrite_test):
(test_rebaseline_expectations):
* Scripts/webkitpy/tool/servers/gardeningserver.py:
(GardeningHTTPRequestHandler._rebaseline_commands):
(GardeningHTTPRequestHandler):
(GardeningHTTPRequestHandler._files_to_add):
(GardeningHTTPRequestHandler._optimize_baselines):
(GardeningHTTPRequestHandler.rebaselineall):
* Scripts/webkitpy/tool/servers/gardeningserver_unittest.py:
(GardeningServerTest._post_to_path):
(GardeningServerTest.test_rebaselineall):
(GardeningServerTest.test_rebaselineall.run_command):

Modified Paths

Diff

Modified: trunk/Tools/ChangeLog (118835 => 118836)


--- trunk/Tools/ChangeLog	2012-05-29 21:20:30 UTC (rev 118835)
+++ trunk/Tools/ChangeLog	2012-05-29 21:30:41 UTC (rev 118836)
@@ -1,3 +1,71 @@
+2012-05-29  Dirk Pranke  <[email protected]>
+
+        garden-o-matic should rebaseline baselines in parallel
+        https://bugs.webkit.org/show_bug.cgi?id=87451
+
+        Reviewed by Ojan Vafai.
+                                                                               
+        This modifies garden-o-matic so that we will fetch all                  
+        of the baselines in parallel from the bots, and then                    
+        optimize the baselines serially (since that should be fairly            
+        quick and we get into trouble if we optimize in parallel due            
+        to concurrent access to the source control system).                     
+
+        This change adds a flag to webkit-patch rebaseline-test (--print-scm-changes)
+        so that the files that need to be modified in the SCM are returned to 
+        garden-o-matic rather than actually added or removed. This is done so that
+        we can run multiple rebaseline-tests in parallel (we can't modify the SCM
+        concurrently). In order to safely return the files (as a JSON dict) I
+        needed to shift all of the logging in rebaseline-test to use the logging
+        module (and hence log to stderr).
+
+        I also had to stub out a line in chromium.py that would get confused if
+        the skia overrides file didn't exist in a mock checkout.
+
+        * Scripts/webkitpy/common/system/executive.py:
+        (Executive.popen):
+        (Executive):
+        (Executive.run_in_parallel):
+        (_run_command_thunk):
+        * Scripts/webkitpy/common/system/executive_mock.py:
+        (MockExecutive.run_in_parallel):
+        * Scripts/webkitpy/common/system/executive_unittest.py:
+        (ExecutiveTest.test_run_in_parallel):
+        * Scripts/webkitpy/layout_tests/port/chromium.py:
+        (ChromiumPort._expectations_file_contents):
+        * Scripts/webkitpy/tool/commands/rebaseline.py:
+        (AbstractRebaseliningCommand.__init__):
+        (RebaselineTest.__init__):
+        (RebaselineTest._copy_existing_baseline):
+        (RebaselineTest._save_baseline):
+        (RebaselineTest):
+        (RebaselineTest._add_to_scm):
+        (RebaselineTest._rebaseline_test):
+        (RebaselineTest.execute):
+        (RebaselineExpectations._rebaseline_port):
+        (RebaselineExpectations.execute):
+        (Rebaseline._builder_to_pull_from):
+        (Rebaseline.execute):
+        * Scripts/webkitpy/tool/commands/rebaseline_unittest.py:
+        (test_rebaseline_updates_expectations_file):
+        (test_rebaseline_test):
+        (test_rebaseline_test_and_print_scm_changes):
+        (test_rebaseline_and_copy_test):
+        (test_rebaseline_and_copy_test_no_existing_result):
+        (test_rebaseline_and_copy_test_with_lion_result):
+        (test_rebaseline_and_copy_no_overwrite_test):
+        (test_rebaseline_expectations):
+        * Scripts/webkitpy/tool/servers/gardeningserver.py:
+        (GardeningHTTPRequestHandler._rebaseline_commands):
+        (GardeningHTTPRequestHandler):
+        (GardeningHTTPRequestHandler._files_to_add):
+        (GardeningHTTPRequestHandler._optimize_baselines):
+        (GardeningHTTPRequestHandler.rebaselineall):
+        * Scripts/webkitpy/tool/servers/gardeningserver_unittest.py:
+        (GardeningServerTest._post_to_path):
+        (GardeningServerTest.test_rebaselineall):
+        (GardeningServerTest.test_rebaselineall.run_command):
+
 2012-05-29  Benjamin Poulain  <[email protected]>
 
         Fix the type of dispatch_time() offset

Modified: trunk/Tools/Scripts/webkitpy/common/system/executive.py (118835 => 118836)


--- trunk/Tools/Scripts/webkitpy/common/system/executive.py	2012-05-29 21:20:30 UTC (rev 118835)
+++ trunk/Tools/Scripts/webkitpy/common/system/executive.py	2012-05-29 21:30:41 UTC (rev 118836)
@@ -451,3 +451,15 @@
 
     def popen(self, *args, **kwargs):
         return subprocess.Popen(*args, **kwargs)
+
+    def run_in_parallel(self, command_lines_and_cwds, processes=None):
+        """Runs a list of (cmd_line list, cwd string) tuples in parallel and returns a list of (retcode, stdout, stderr) tuples."""
+        return multiprocessing.Pool(processes=processes).map(_run_command_thunk, command_lines_and_cwds)
+
+
+def _run_command_thunk(cmd_line_and_cwd):
+    # Note that this needs to be a bare module (and hence Picklable) method to work with multiprocessing.Pool.
+    (cmd_line, cwd) = cmd_line_and_cwd
+    proc = subprocess.Popen(cmd_line, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    stdout, stderr = proc.communicate()
+    return (proc.returncode, stdout, stderr)

Modified: trunk/Tools/Scripts/webkitpy/common/system/executive_mock.py (118835 => 118836)


--- trunk/Tools/Scripts/webkitpy/common/system/executive_mock.py	2012-05-29 21:20:30 UTC (rev 118835)
+++ trunk/Tools/Scripts/webkitpy/common/system/executive_mock.py	2012-05-29 21:30:41 UTC (rev 118836)
@@ -108,6 +108,11 @@
             self._proc = MockProcess()
         return self._proc
 
+    def run_in_parallel(self, commands):
+        command_outputs = []
+        for cmd_line, cwd in commands:
+            command_outputs.append([0, self.run_command(cmd_line, cwd=cwd), ''])
+        return command_outputs
 
 class MockExecutive2(object):
     @staticmethod

Modified: trunk/Tools/Scripts/webkitpy/common/system/executive_unittest.py (118835 => 118836)


--- trunk/Tools/Scripts/webkitpy/common/system/executive_unittest.py	2012-05-29 21:20:30 UTC (rev 118835)
+++ trunk/Tools/Scripts/webkitpy/common/system/executive_unittest.py	2012-05-29 21:30:41 UTC (rev 118836)
@@ -31,6 +31,7 @@
 import signal
 import subprocess
 import sys
+import time
 import unittest
 
 # Since we execute this script directly as part of the unit tests, we need to ensure
@@ -216,7 +217,19 @@
         pids = executive.running_pids()
         self.assertTrue(os.getpid() in pids)
 
+    def test_run_in_parallel(self):
+        NUM_PROCESSES = 4
+        DELAY_SECS = 0.25
+        cmd_line = [sys.executable, '-c', 'import time; time.sleep(%f); print "hello"' % DELAY_SECS]
+        cwd = os.getcwd()
+        commands = [tuple([cmd_line, cwd])] * NUM_PROCESSES
+        start = time.time()
+        command_outputs = Executive().run_in_parallel(commands, processes=NUM_PROCESSES)
+        done = time.time()
+        self.assertTrue(done - start < NUM_PROCESSES * DELAY_SECS)
+        self.assertEquals([output[1] for output in command_outputs], ["hello\n"] * NUM_PROCESSES)
 
+
 def main(platform, stdin, stdout, cmd, args):
     if platform == 'win32' and hasattr(stdout, 'fileno'):
         import msvcrt

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/port/chromium.py (118835 => 118836)


--- trunk/Tools/Scripts/webkitpy/layout_tests/port/chromium.py	2012-05-29 21:20:30 UTC (rev 118835)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/port/chromium.py	2012-05-29 21:30:41 UTC (rev 118836)
@@ -327,9 +327,12 @@
                 (filetype, filepath))
             return (self._filesystem.read_text_file(filepath) or '')
         else:
-            _log.warning(
-                "%s test_expectations overrides file '%s' does not exist" %
-                (filetype, filepath))
+            # FIXME: This allows this to work with mock checkouts;
+            # This probably shouldn't be used with a mock checkout, though.
+            if not 'mock-checkout' in filepath:
+                _log.warning(
+                    "%s test_expectations overrides file '%s' does not exist" %
+                    (filetype, filepath))
             return ''
 
     def test_expectations_overrides(self):

Modified: trunk/Tools/Scripts/webkitpy/tool/commands/rebaseline.py (118835 => 118836)


--- trunk/Tools/Scripts/webkitpy/tool/commands/rebaseline.py	2012-05-29 21:20:30 UTC (rev 118835)
+++ trunk/Tools/Scripts/webkitpy/tool/commands/rebaseline.py	2012-05-29 21:30:41 UTC (rev 118836)
@@ -26,6 +26,8 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+import json
+import logging
 import optparse
 import os.path
 import re
@@ -51,16 +53,19 @@
 _baseline_suffix_list = ['png', 'wav', 'txt']
 
 
+_log = logging.getLogger(__name__)
+
 # FIXME: Should TestResultWriter know how to compute this string?
 def _baseline_name(fs, test_name, suffix):
     return fs.splitext(test_name)[0] + TestResultWriter.FILENAME_SUFFIX_EXPECTED + "." + suffix
 
 
 class AbstractRebaseliningCommand(AbstractDeclarativeCommand):
-    def __init__(self):
-        options = [
+    def __init__(self, options=None):
+        options = options or []
+        options.extend([
             optparse.make_option('--suffixes', default=','.join(_baseline_suffix_list), action='',
-                                 help='file types to rebaseline')]
+                                 help='file types to rebaseline')])
         AbstractDeclarativeCommand.__init__(self, options=options)
         self._baseline_suffix_list = _baseline_suffix_list
 
@@ -70,6 +75,14 @@
     help_text = "Rebaseline a single test from a buildbot.  (Currently works only with build.chromium.org buildbots.)"
     argument_names = "BUILDER_NAME TEST_NAME [PLATFORMS_TO_MOVE_EXISTING_BASELINES_TO]"
 
+    def __init__(self):
+        options = [
+            optparse.make_option("--print-scm-changes", action="" help="Print modifcations to the scm (as a json dict) rather than actually modifying the scm"),
+        ]
+        AbstractRebaseliningCommand.__init__(self, options=options)
+        self._print_scm_changes = False
+        self._scm_changes = {}
+
     def _results_url(self, builder_name):
         # FIXME: Generalize this command to work with non-build.chromium.org builders.
         builder = self._tool.chromium_buildbot().builder_with_name(builder_name)
@@ -89,12 +102,12 @@
             port = self._tool.port_factory.get(platform)
             old_baseline = port.expected_filename(test_name, "." + suffix)
             if not self._tool.filesystem.exists(old_baseline):
-                print("No existing baseline for %s." % test_name)
+                _log.info("No existing baseline for %s." % test_name)
                 continue
 
             new_baseline = self._tool.filesystem.join(port.baseline_path(), self._file_name_for_expected_result(test_name, suffix))
             if self._tool.filesystem.exists(new_baseline):
-                print("Existing baseline at %s, not copying over it." % new_baseline)
+                _log.info("Existing baseline at %s, not copying over it." % new_baseline)
                 continue
 
             old_baselines.append(old_baseline)
@@ -104,11 +117,11 @@
             old_baseline = old_baselines[i]
             new_baseline = new_baselines[i]
 
-            print("Copying baseline from %s to %s." % (old_baseline, new_baseline))
+            _log.info("Copying baseline from %s to %s." % (old_baseline, new_baseline))
             self._tool.filesystem.maybe_make_directory(self._tool.filesystem.dirname(new_baseline))
             self._tool.filesystem.copyfile(old_baseline, new_baseline)
             if not self._tool.scm().exists(new_baseline):
-                self._tool.scm().add(new_baseline)
+                self._add_to_scm(new_baseline)
 
     def _save_baseline(self, data, target_baseline):
         if not data:
@@ -117,8 +130,14 @@
         filesystem.maybe_make_directory(filesystem.dirname(target_baseline))
         filesystem.write_binary_file(target_baseline, data)
         if not self._tool.scm().exists(target_baseline):
-            self._tool.scm().add(target_baseline)
+            self._add_to_scm(target_baseline)
 
+    def _add_to_scm(self, path):
+        if self._print_scm_changes:
+            self._scm_changes['add'].append(path)
+        else:
+            self._tool.scm().add(path)
+
     def _update_expectations_file(self, builder_name, test_name):
         port = self._tool.port_factory.get_from_builder_name(builder_name)
         expectations = TestExpectations(port)
@@ -148,7 +167,7 @@
         if platforms_to_move_existing_baselines_to:
             self._copy_existing_baseline(platforms_to_move_existing_baselines_to, test_name, suffix)
 
-        print "Retrieving %s." % source_baseline
+        _log.info("Retrieving %s." % source_baseline)
         self._save_baseline(self._tool.web.get_binary(source_baseline, convert_404_to_None=True), target_baseline)
 
     def _rebaseline_test_and_update_expectations(self, builder_name, test_name, platforms_to_move_existing_baselines_to):
@@ -158,12 +177,16 @@
 
     def execute(self, options, args, tool):
         self._baseline_suffix_list = options.suffixes.split(',')
+        self._print_scm_changes = options.print_scm_changes
+        self._scm_changes = {'add': [], 'rm': []}
 
         if len(args) > 2:
             platforms_to_move_existing_baselines_to = args[2:]
         else:
             platforms_to_move_existing_baselines_to = None
         self._rebaseline_test_and_update_expectations(args[0], args[1], platforms_to_move_existing_baselines_to)
+        if self._print_scm_changes:
+            print json.dumps(self._scm_changes)
 
 
 class OptimizeBaselines(AbstractRebaseliningCommand):
@@ -257,10 +280,10 @@
         builder_name = builders.builder_name_for_port_name(port_name)
         if not builder_name:
             return
-        print "Retrieving results for %s from %s." % (port_name, builder_name)
+        _log.info("Retrieving results for %s from %s." % (port_name, builder_name))
         for test_name in self._tests_to_rebaseline(self._tool.port_factory.get(port_name)):
             self._touched_test_names.add(test_name)
-            print "    %s" % test_name
+            _log.info("    %s" % test_name)
             # FIXME: need to extract the correct list of suffixes here.
             self._run_webkit_patch(['rebaseline-test', builder_name, test_name])
 
@@ -273,7 +296,7 @@
         if not options.optimize:
             return
         for test_name in self._touched_test_names:
-            print "Optimizing baselines for %s." % test_name
+            _log.info("Optimizing baselines for %s." % test_name)
             self._run_webkit_patch(['optimize-baselines', test_name])
 
 
@@ -285,7 +308,7 @@
     def _builder_to_pull_from(self):
         builder_statuses = self._tool.buildbot.builder_statuses()
         red_statuses = [status for status in builder_statuses if not status["is_green"]]
-        print "%s failing" % (pluralize("builder", len(red_statuses)))
+        _log.info("%s failing" % (pluralize("builder", len(red_statuses))))
         builder_choices = [status["name"] for status in red_statuses]
         chosen_name = self._tool.user.prompt_with_list("Which builder to pull results from:", builder_choices)
         # FIXME: prompt_with_list should really take a set of objects and a set of names and then return the object.
@@ -315,7 +338,7 @@
             results_url = self._results_url_for_test(build, test)
             # Port operates with absolute paths.
             expected_file = port.expected_filename(test, '.txt')
-            print test
+            _log.info(test)
             self._replace_expectation_with_remote_result(expected_file, results_url)
 
         # FIXME: We should handle new results too.

Modified: trunk/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py (118835 => 118836)


--- trunk/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py	2012-05-29 21:20:30 UTC (rev 118835)
+++ trunk/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py	2012-05-29 21:30:41 UTC (rev 118836)
@@ -55,11 +55,11 @@
         tool.filesystem.write_text_file(os.path.join(lion_port.layout_tests_dir(), "fast/css/large-list-of-rules-crash.html"), "Dummy test contents")
         tool.filesystem.write_text_file(os.path.join(lion_port.layout_tests_dir(), "userscripts/another-test.html"), "Dummy test contents")
 
-        expected_stdout = """Retrieving http://example.com/f/builders/Webkit Mac10.7/results/layout-test-results/userscripts/another-test-actual.png.
+        expected_logs = """Retrieving http://example.com/f/builders/Webkit Mac10.7/results/layout-test-results/userscripts/another-test-actual.png.
 Retrieving http://example.com/f/builders/Webkit Mac10.7/results/layout-test-results/userscripts/another-test-actual.wav.
 Retrieving http://example.com/f/builders/Webkit Mac10.7/results/layout-test-results/userscripts/another-test-actual.txt.
 """
-        OutputCapture().assert_outputs(self, command._rebaseline_test_and_update_expectations, ["Webkit Mac10.7", "userscripts/another-test.html", None], expected_stdout=expected_stdout)
+        OutputCapture().assert_outputs(self, command._rebaseline_test_and_update_expectations, ["Webkit Mac10.7", "userscripts/another-test.html", None], expected_logs=expected_logs)
 
         new_expectations = tool.filesystem.read_text_file(lion_port.path_to_test_expectations_file())
         self.assertEqual(new_expectations, """BUGB MAC LINUX XP DEBUG : fast/dom/Window/window-postmessage-clone-really-deep-array.html = PASS
@@ -75,11 +75,11 @@
         tool.filesystem.write_text_file(lion_port.path_to_test_expectations_file(), "BUGX MAC : userscripts/another-test.html = IMAGE\nBUGZ LINUX : userscripts/another-test.html = IMAGE\n")
         tool.filesystem.write_text_file(os.path.join(lion_port.layout_tests_dir(), "userscripts/another-test.html"), "Dummy test contents")
 
-        expected_stdout = """Retrieving http://example.com/f/builders/Webkit Mac10.7/results/layout-test-results/userscripts/another-test-actual.png.
+        expected_logs = """Retrieving http://example.com/f/builders/Webkit Mac10.7/results/layout-test-results/userscripts/another-test-actual.png.
 Retrieving http://example.com/f/builders/Webkit Mac10.7/results/layout-test-results/userscripts/another-test-actual.wav.
 Retrieving http://example.com/f/builders/Webkit Mac10.7/results/layout-test-results/userscripts/another-test-actual.txt.
 """
-        OutputCapture().assert_outputs(self, command._rebaseline_test_and_update_expectations, ["Webkit Mac10.7", "userscripts/another-test.html", None], expected_stdout=expected_stdout)
+        OutputCapture().assert_outputs(self, command._rebaseline_test_and_update_expectations, ["Webkit Mac10.7", "userscripts/another-test.html", None], expected_logs=expected_logs)
 
         new_expectations = tool.filesystem.read_text_file(lion_port.path_to_test_expectations_file())
         self.assertEqual(new_expectations, "BUGX LEOPARD SNOWLEOPARD : userscripts/another-test.html = IMAGE\nBUGZ LINUX : userscripts/another-test.html = IMAGE\n")
@@ -87,9 +87,19 @@
     def test_rebaseline_test(self):
         command = RebaselineTest()
         command.bind_to_tool(MockTool())
-        expected_stdout = "Retrieving http://example.com/f/builders/Webkit Linux/results/layout-test-results/userscripts/another-test-actual.txt.\n"
-        OutputCapture().assert_outputs(self, command._rebaseline_test, ["Webkit Linux", "userscripts/another-test.html", None, "txt"], expected_stdout=expected_stdout)
+        expected_logs = "Retrieving http://example.com/f/builders/Webkit Linux/results/layout-test-results/userscripts/another-test-actual.txt.\n"
+        OutputCapture().assert_outputs(self, command._rebaseline_test, ["Webkit Linux", "userscripts/another-test.html", None, "txt"], expected_logs=expected_logs)
 
+    def test_rebaseline_test_and_print_scm_changes(self):
+        command = RebaselineTest()
+        command.bind_to_tool(MockTool())
+        expected_logs = "Retrieving http://example.com/f/builders/Webkit Linux/results/layout-test-results/userscripts/another-test-actual.txt.\n"
+        command._print_scm_changes = True
+        command._scm_changes = {'add': [], 'rm': []}
+        command._tool._scm.exists = lambda x: False
+        OutputCapture().assert_outputs(self, command._rebaseline_test, ["Webkit Linux", "userscripts/another-test.html", None, "txt"], expected_logs=expected_logs)
+        self.assertEquals(command._scm_changes, {'add': ['/mock-checkout/LayoutTests/platform/chromium-linux/userscripts/another-test-expected.txt'], 'rm': []})
+
     def test_rebaseline_and_copy_test(self):
         command = RebaselineTest()
         tool = MockTool()
@@ -98,20 +108,20 @@
         lion_port = tool.port_factory.get_from_builder_name("Webkit Mac10.7")
         tool.filesystem.write_text_file(os.path.join(lion_port.layout_tests_dir(), "userscripts/another-test-expected.txt"), "Dummy expected result")
 
-        expected_stdout = """Copying baseline from /mock-checkout/LayoutTests/userscripts/another-test-expected.txt to /mock-checkout/LayoutTests/platform/chromium-mac-snowleopard/userscripts/another-test-expected.txt.
+        expected_logs = """Copying baseline from /mock-checkout/LayoutTests/userscripts/another-test-expected.txt to /mock-checkout/LayoutTests/platform/chromium-mac-snowleopard/userscripts/another-test-expected.txt.
 Retrieving http://example.com/f/builders/Webkit Mac10.7/results/layout-test-results/userscripts/another-test-actual.txt.
 """
-        OutputCapture().assert_outputs(self, command._rebaseline_test, ["Webkit Mac10.7", "userscripts/another-test.html", ["chromium-mac-snowleopard"], "txt"], expected_stdout=expected_stdout)
+        OutputCapture().assert_outputs(self, command._rebaseline_test, ["Webkit Mac10.7", "userscripts/another-test.html", ["chromium-mac-snowleopard"], "txt"], expected_logs=expected_logs)
 
     def test_rebaseline_and_copy_test_no_existing_result(self):
         command = RebaselineTest()
         tool = MockTool()
         command.bind_to_tool(tool)
 
-        expected_stdout = """No existing baseline for userscripts/another-test.html.
+        expected_logs = """No existing baseline for userscripts/another-test.html.
 Retrieving http://example.com/f/builders/Webkit Mac10.7/results/layout-test-results/userscripts/another-test-actual.txt.
 """
-        OutputCapture().assert_outputs(self, command._rebaseline_test, ["Webkit Mac10.7", "userscripts/another-test.html", ["chromium-mac-snowleopard"], "txt"], expected_stdout=expected_stdout)
+        OutputCapture().assert_outputs(self, command._rebaseline_test, ["Webkit Mac10.7", "userscripts/another-test.html", ["chromium-mac-snowleopard"], "txt"], expected_logs=expected_logs)
 
     def test_rebaseline_and_copy_test_with_lion_result(self):
         command = RebaselineTest()
@@ -121,11 +131,11 @@
         lion_port = tool.port_factory.get_from_builder_name("Webkit Mac10.7")
         tool.filesystem.write_text_file(os.path.join(lion_port.baseline_path(), "userscripts/another-test-expected.txt"), "Dummy expected result")
 
-        expected_stdout = """Copying baseline from /mock-checkout/LayoutTests/platform/chromium-mac/userscripts/another-test-expected.txt to /mock-checkout/LayoutTests/platform/chromium-mac-snowleopard/userscripts/another-test-expected.txt.
+        expected_logs = """Copying baseline from /mock-checkout/LayoutTests/platform/chromium-mac/userscripts/another-test-expected.txt to /mock-checkout/LayoutTests/platform/chromium-mac-snowleopard/userscripts/another-test-expected.txt.
 Copying baseline from /mock-checkout/LayoutTests/platform/chromium-mac/userscripts/another-test-expected.txt to /mock-checkout/LayoutTests/platform/chromium-mac-leopard/userscripts/another-test-expected.txt.
 Retrieving http://example.com/f/builders/Webkit Mac10.7/results/layout-test-results/userscripts/another-test-actual.txt.
 """
-        OutputCapture().assert_outputs(self, command._rebaseline_test, ["Webkit Mac10.7", "userscripts/another-test.html", ["chromium-mac-snowleopard", "chromium-mac-leopard"], "txt"], expected_stdout=expected_stdout)
+        OutputCapture().assert_outputs(self, command._rebaseline_test, ["Webkit Mac10.7", "userscripts/another-test.html", ["chromium-mac-snowleopard", "chromium-mac-leopard"], "txt"], expected_logs=expected_logs)
 
     def test_rebaseline_and_copy_no_overwrite_test(self):
         command = RebaselineTest()
@@ -138,10 +148,10 @@
         snowleopard_port = tool.port_factory.get_from_builder_name("Webkit Mac10.6")
         tool.filesystem.write_text_file(os.path.join(snowleopard_port.baseline_path(), "userscripts/another-test-expected.txt"), "Dummy expected result")
 
-        expected_stdout = """Existing baseline at /mock-checkout/LayoutTests/platform/chromium-mac-snowleopard/userscripts/another-test-expected.txt, not copying over it.
+        expected_logs = """Existing baseline at /mock-checkout/LayoutTests/platform/chromium-mac-snowleopard/userscripts/another-test-expected.txt, not copying over it.
 Retrieving http://example.com/f/builders/Webkit Mac10.7/results/layout-test-results/userscripts/another-test-actual.txt.
 """
-        OutputCapture().assert_outputs(self, command._rebaseline_test, ["Webkit Mac10.7", "userscripts/another-test.html", ["chromium-mac-snowleopard"], "txt"], expected_stdout=expected_stdout)
+        OutputCapture().assert_outputs(self, command._rebaseline_test, ["Webkit Mac10.7", "userscripts/another-test.html", ["chromium-mac-snowleopard"], "txt"], expected_logs=expected_logs)
 
     def test_rebaseline_expectations(self):
         command = RebaselineExpectations()
@@ -155,7 +165,7 @@
         # Don't enable logging until after we create the mock expectation files as some Port.__init__'s run subcommands.
         tool.executive = MockExecutive(should_log=True)
 
-        expected_stdout = """Retrieving results for chromium-linux-x86 from Webkit Linux 32.
+        expected_logs = """Retrieving results for chromium-linux-x86 from Webkit Linux 32.
     userscripts/another-test.html
     userscripts/images.svg
 Retrieving results for chromium-linux-x86_64 from Webkit Linux.
@@ -200,9 +210,9 @@
 """
 
         command._tests_to_rebaseline = lambda port: ['userscripts/another-test.html', 'userscripts/images.svg']
-        OutputCapture().assert_outputs(self, command.execute, [MockOptions(optimize=False), [], tool], expected_stdout=expected_stdout, expected_stderr=expected_stderr)
+        OutputCapture().assert_outputs(self, command.execute, [MockOptions(optimize=False), [], tool], expected_logs=expected_logs, expected_stderr=expected_stderr)
 
-        expected_stdout_with_optimize = expected_stdout + (
+        expected_logs_with_optimize = expected_logs + (
             "Optimizing baselines for userscripts/another-test.html.\n"
             "Optimizing baselines for userscripts/images.svg.\n")
         expected_stderr_with_optimize = expected_stderr + (
@@ -210,4 +220,4 @@
             "MOCK run_command: ['echo', 'optimize-baselines', 'userscripts/images.svg'], cwd=/mock-checkout\n")
 
         command._tests_to_rebaseline = lambda port: ['userscripts/another-test.html', 'userscripts/images.svg']
-        OutputCapture().assert_outputs(self, command.execute, [MockOptions(optimize=True), [], tool], expected_stdout=expected_stdout_with_optimize, expected_stderr=expected_stderr_with_optimize)
+        OutputCapture().assert_outputs(self, command.execute, [MockOptions(optimize=True), [], tool], expected_logs=expected_logs_with_optimize, expected_stderr=expected_stderr_with_optimize)

Modified: trunk/Tools/Scripts/webkitpy/tool/servers/gardeningserver.py (118835 => 118836)


--- trunk/Tools/Scripts/webkitpy/tool/servers/gardeningserver.py	2012-05-29 21:20:30 UTC (rev 118835)
+++ trunk/Tools/Scripts/webkitpy/tool/servers/gardeningserver.py	2012-05-29 21:30:41 UTC (rev 118836)
@@ -23,6 +23,8 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 import BaseHTTPServer
+import logging
+import json
 import os
 
 from webkitpy.common.memoized import memoized
@@ -33,6 +35,9 @@
 from webkitpy.layout_tests.port import builders
 
 
+_log = logging.getLogger(__name__)
+
+
 class BuildCoverageExtrapolator(object):
     def __init__(self, test_configuration_converter):
         self._test_configuration_converter = test_configuration_converter
@@ -171,15 +176,43 @@
                 builders_to_fallback_paths[builder] = fallback_path
         return builders_to_fallback_paths.keys()
 
-    def rebaselineall(self):
-        # FIXME: Optimize this to run in parallel, cache zips, etc.
-        test_list = self._read_entity_body_as_json()
+    def _rebaseline_commands(self, test_list):
+        path_to_webkit_patch = self.server.tool.path()
+        cwd = self.server.tool.scm().checkout_root
+        commands = []
         for test in test_list:
+            for builder in self._builders_to_fetch_from(test_list[test]):
+                suffixes = ','.join(test_list[test][builder])
+                cmd_line = [path_to_webkit_patch, 'rebaseline-test', '--print-scm-changes', '--suffixes', suffixes, builder, test]
+                commands.append(tuple([cmd_line, cwd]))
+        return commands
+
+    def _files_to_add(self, command_results):
+        files_to_add = set()
+        for output in [result[1] for result in command_results]:
+            try:
+                files_to_add.update(json.loads(output)['add'])
+            except ValueError, e:
+                _log.warning('"%s" is not a JSON object, ignoring' % output)
+
+        return list(files_to_add)
+
+    def _optimize_baselines(self, test_list):
+        # We don't run this in parallel because modifying the SCM in parallel is unreliable.
+        for test in test_list:
             all_suffixes = set()
-            builders = self._builders_to_fetch_from(test_list[test])
-            for builder in builders:
-                suffixes = test_list[test][builder]
-                all_suffixes.update(suffixes)
-                self._run_webkit_patch(['rebaseline-test', '--suffixes', ','.join(suffixes), builder, test])
+            for builder in self._builders_to_fetch_from(test_list[test]):
+                all_suffixes.update(test_list[test][builder])
             self._run_webkit_patch(['optimize-baselines', '--suffixes', ','.join(all_suffixes), test])
+
+    def rebaselineall(self):
+        test_list = self._read_entity_body_as_json()
+
+        commands = self._rebaseline_commands(test_list)
+        command_results = self.server.tool.executive.run_in_parallel(commands)
+
+        files_to_add = self._files_to_add(command_results)
+        self.server.tool.scm().add_list(list(files_to_add))
+
+        self._optimize_baselines(test_list)
         self._serve_text('success')

Modified: trunk/Tools/Scripts/webkitpy/tool/servers/gardeningserver_unittest.py (118835 => 118836)


--- trunk/Tools/Scripts/webkitpy/tool/servers/gardeningserver_unittest.py	2012-05-29 21:20:30 UTC (rev 118835)
+++ trunk/Tools/Scripts/webkitpy/tool/servers/gardeningserver_unittest.py	2012-05-29 21:30:41 UTC (rev 118836)
@@ -27,6 +27,7 @@
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 import json
+import sys
 import unittest
 
 from webkitpy.common.system.outputcapture import OutputCapture
@@ -170,8 +171,8 @@
 
 
 class GardeningServerTest(unittest.TestCase):
-    def _post_to_path(self, path, body=None, expected_stderr=None, expected_stdout=None):
-        handler = TestGardeningHTTPRequestHandler(MockServer())
+    def _post_to_path(self, path, body=None, expected_stderr=None, expected_stdout=None, server=None):
+        handler = TestGardeningHTTPRequestHandler(server or MockServer())
         handler.path = path
         handler.body = body
         OutputCapture().assert_outputs(self, handler.do_POST, expected_stderr=expected_stderr, expected_stdout=expected_stdout)
@@ -186,10 +187,19 @@
             "MOCK builder": {"port_name": "test-mac-leopard", "specifiers": set(["mock-specifier"])},
             "MOCK builder (Debug)": {"port_name": "test-mac-leopard", "specifiers": set(["mock-specifier", "debug"])},
         }
-        expected_stderr = "MOCK run_command: ['echo', 'rebaseline-test', '--suffixes', u'%s', u'%s', u'user-scripts/another-test.html'], cwd=/mock-checkout\nMOCK run_command: ['echo', 'optimize-baselines', '--suffixes', u'%s', u'user-scripts/another-test.html'], cwd=/mock-checkout\n"
+        expected_stderr = "MOCK run_command: ['echo', 'rebaseline-test', '--print-scm-changes', '--suffixes', u'%s', u'%s', u'user-scripts/another-test.html'], cwd=/mock-checkout\nMOCK run_command: ['echo', 'optimize-baselines', '--suffixes', u'%s', u'user-scripts/another-test.html'], cwd=/mock-checkout\n"
         expected_stdout = "== Begin Response ==\nsuccess\n== End Response ==\n"
-        self._post_to_path("/rebaselineall", body='{"user-scripts/another-test.html":{"MOCK builder": ["txt","png"]}}', expected_stderr=expected_stderr % ('txt,png', 'MOCK builder', 'txt,png'), expected_stdout=expected_stdout)
+        server = MockServer()
 
+        self.output = ['{"add": [], "rm": []}', '']
+
+        def run_command(args, cwd=None, **kwargs):
+            print >> sys.stderr, "MOCK run_command: %s, cwd=%s" % (args, cwd)
+            return self.output.pop(0)
+
+        server.tool.executive.run_command = run_command
+        self._post_to_path("/rebaselineall", body='{"user-scripts/another-test.html":{"MOCK builder": ["txt","png"]}}', expected_stderr=expected_stderr % ('txt,png', 'MOCK builder', 'txt,png'), expected_stdout=expected_stdout, server=server)
+
         self._post_to_path("/rebaselineall", body='{"user-scripts/another-test.html":{"MOCK builder (Debug)": ["txt","png"]}}', expected_stderr=expected_stderr % ('txt,png', 'MOCK builder (Debug)', 'txt,png'), expected_stdout=expected_stdout)
 
         self._post_to_path("/rebaselineall", body='{"user-scripts/another-test.html":{"MOCK builder (Debug)": ["txt","png"], "MOCK builder": ["txt"]}}', expected_stderr=expected_stderr % ('txt', 'MOCK builder', 'txt'), expected_stdout=expected_stdout)
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to