Title: [122234] trunk/Tools
Revision
122234
Author
[email protected]
Date
2012-07-10 10:33:41 -0700 (Tue, 10 Jul 2012)

Log Message

Improve webkit-patch rebaseline to work for more cases
https://bugs.webkit.org/show_bug.cgi?id=90504

Reviewed by Adam Barth.

-Makes it work for the build.chromium.org bots.
-Lets you rebaseline all builders instead of just one.
-Lets you pass in the builders or tests to rebaseline.

* Scripts/webkitpy/common/host.py:
(Host.buildbot_for_builder_name):
* Scripts/webkitpy/common/net/buildbot/buildbot.py:
(Builder.__init__):
(Builder.latest_layout_test_results):
Provide a way to get to the LayoutTestResults of the latest build.
Most of the time we only need to get the latest one and the Chromium bots
only expose the full_results.json file for the latest build.

(Builder):
(Builder._fetch_file_from_results):
(Builder.fetch_layout_test_results):
Move these functions into Builder so that Builder and Build can both
fetch layout test results.

(Build.results_zip_url):
(Build.layout_test_results):
* Scripts/webkitpy/common/net/buildbot/buildbot_unittest.py:
(BuilderTest.test_latest_layout_test_results):
(BuildTest.test_layout_test_results):
* Scripts/webkitpy/common/system/user.py:
(User.prompt_with_multiple_lists):
Prompt with multiple sublists, but still have a global numbering.
This lets the build.chromium.org bots be clearly separated from the
build.webkit.org bots, which helps understand the builder names.

(User):
(User._wait_on_list_response):
(User.prompt_with_list):
* Scripts/webkitpy/common/system/user_unittest.py:
(UserTest.test_prompt_with_multiple_lists):
(UserTest.test_prompt_with_multiple_lists.run_prompt_test):
(UserTest.test_prompt_with_multiple_lists.run_prompt_test.mock_raw_input):
* Scripts/webkitpy/layout_tests/port/base.py:
(Port.is_chromium):
Provide a way to tell if a port is a Chromium port that doesn't involve string
manipulation on the port name or builder name.

* Scripts/webkitpy/layout_tests/port/builders.py:
(builder_path_from_name):
(all_builder_names):
Memoizing here is incorrect because the test override _exact_matches,
so these can return different values. In either case, I'm pretty sure these
are not remotely hot codepaths. I manually inspected all call sites and they're
all in high-level calls (e.g. execute calls for webkit-patch commands) and not
called in a loop.

* Scripts/webkitpy/layout_tests/port/chromium.py:
(ChromiumPort.is_chromium):
* Scripts/webkitpy/tool/commands/rebaseline.py:
(RebaselineTest._results_url):
(AbstractParallelRebaselineCommand.__init__):
(Rebaseline.__init__):
(Rebaseline):
(Rebaseline._builders_to_pull_from):
(Rebaseline._tests_to_update):
(Rebaseline.execute):
* Scripts/webkitpy/tool/commands/rebaseline_unittest.py:
(test_rebaseline.mock_builders_to_pull_from):
(test_rebaseline):
(test_rebaseline_command_line_flags):
(test_rebaseline_multiple_builders):
(test_rebaseline_multiple_builders.mock_builders_to_pull_from):
(test_rebaseline_multiple_builders.mock_tests_to_update):
(test_rebaseline_multiple_builders_and_tests_command_line):
* Scripts/webkitpy/tool/mocktool.py:
(MockTool.irc):
(MockTool):
(MockTool.buildbot_for_builder_name):

Modified Paths

Diff

Modified: trunk/Tools/ChangeLog (122233 => 122234)


--- trunk/Tools/ChangeLog	2012-07-10 17:05:24 UTC (rev 122233)
+++ trunk/Tools/ChangeLog	2012-07-10 17:33:41 UTC (rev 122234)
@@ -1,3 +1,84 @@
+2012-07-09  Ojan Vafai  <[email protected]>
+
+        Improve webkit-patch rebaseline to work for more cases
+        https://bugs.webkit.org/show_bug.cgi?id=90504
+
+        Reviewed by Adam Barth.
+
+        -Makes it work for the build.chromium.org bots.
+        -Lets you rebaseline all builders instead of just one.
+        -Lets you pass in the builders or tests to rebaseline.
+
+        * Scripts/webkitpy/common/host.py:
+        (Host.buildbot_for_builder_name):
+        * Scripts/webkitpy/common/net/buildbot/buildbot.py:
+        (Builder.__init__):
+        (Builder.latest_layout_test_results):
+        Provide a way to get to the LayoutTestResults of the latest build.
+        Most of the time we only need to get the latest one and the Chromium bots
+        only expose the full_results.json file for the latest build.
+
+        (Builder):
+        (Builder._fetch_file_from_results):
+        (Builder.fetch_layout_test_results):
+        Move these functions into Builder so that Builder and Build can both
+        fetch layout test results.
+
+        (Build.results_zip_url):
+        (Build.layout_test_results):
+        * Scripts/webkitpy/common/net/buildbot/buildbot_unittest.py:
+        (BuilderTest.test_latest_layout_test_results):
+        (BuildTest.test_layout_test_results):
+        * Scripts/webkitpy/common/system/user.py:
+        (User.prompt_with_multiple_lists):
+        Prompt with multiple sublists, but still have a global numbering.
+        This lets the build.chromium.org bots be clearly separated from the
+        build.webkit.org bots, which helps understand the builder names.
+
+        (User):
+        (User._wait_on_list_response):
+        (User.prompt_with_list):
+        * Scripts/webkitpy/common/system/user_unittest.py:
+        (UserTest.test_prompt_with_multiple_lists):
+        (UserTest.test_prompt_with_multiple_lists.run_prompt_test):
+        (UserTest.test_prompt_with_multiple_lists.run_prompt_test.mock_raw_input):
+        * Scripts/webkitpy/layout_tests/port/base.py:
+        (Port.is_chromium):
+        Provide a way to tell if a port is a Chromium port that doesn't involve string
+        manipulation on the port name or builder name.
+
+        * Scripts/webkitpy/layout_tests/port/builders.py:
+        (builder_path_from_name):
+        (all_builder_names):
+        Memoizing here is incorrect because the test override _exact_matches,
+        so these can return different values. In either case, I'm pretty sure these
+        are not remotely hot codepaths. I manually inspected all call sites and they're
+        all in high-level calls (e.g. execute calls for webkit-patch commands) and not
+        called in a loop.
+
+        * Scripts/webkitpy/layout_tests/port/chromium.py:
+        (ChromiumPort.is_chromium):
+        * Scripts/webkitpy/tool/commands/rebaseline.py:
+        (RebaselineTest._results_url):
+        (AbstractParallelRebaselineCommand.__init__):
+        (Rebaseline.__init__):
+        (Rebaseline):
+        (Rebaseline._builders_to_pull_from):
+        (Rebaseline._tests_to_update):
+        (Rebaseline.execute):
+        * Scripts/webkitpy/tool/commands/rebaseline_unittest.py:
+        (test_rebaseline.mock_builders_to_pull_from):
+        (test_rebaseline):
+        (test_rebaseline_command_line_flags):
+        (test_rebaseline_multiple_builders):
+        (test_rebaseline_multiple_builders.mock_builders_to_pull_from):
+        (test_rebaseline_multiple_builders.mock_tests_to_update):
+        (test_rebaseline_multiple_builders_and_tests_command_line):
+        * Scripts/webkitpy/tool/mocktool.py:
+        (MockTool.irc):
+        (MockTool):
+        (MockTool.buildbot_for_builder_name):
+
 2012-07-10  Alice Cheng  <[email protected]>
 
         Editing: Reproducible crasher when pasting a 0x0 image into Mail

Modified: trunk/Tools/Scripts/webkitpy/common/host.py (122233 => 122234)


--- trunk/Tools/Scripts/webkitpy/common/host.py	2012-07-10 17:05:24 UTC (rev 122233)
+++ trunk/Tools/Scripts/webkitpy/common/host.py	2012-07-10 17:33:41 UTC (rev 122234)
@@ -138,6 +138,11 @@
     def checkout(self):
         return self._checkout
 
+    def buildbot_for_builder_name(self, name):
+        if self.port_factory.get_from_builder_name(name).is_chromium():
+            return self.chromium_buildbot()
+        return self.buildbot
+
     @memoized
     def chromium_buildbot(self):
         return ChromiumBuildBot()

Modified: trunk/Tools/Scripts/webkitpy/common/net/buildbot/buildbot.py (122233 => 122234)


--- trunk/Tools/Scripts/webkitpy/common/net/buildbot/buildbot.py	2012-07-10 17:05:24 UTC (rev 122233)
+++ trunk/Tools/Scripts/webkitpy/common/net/buildbot/buildbot.py	2012-07-10 17:33:41 UTC (rev 122234)
@@ -33,6 +33,7 @@
 import urllib2
 
 import webkitpy.common.config.urls as config_urls
+from webkitpy.common.memoized import memoized
 from webkitpy.common.net.failuremap import FailureMap
 from webkitpy.common.net.layouttestresults import LayoutTestResults
 from webkitpy.common.net.networktransaction import NetworkTransaction
@@ -65,6 +66,31 @@
     def accumulated_results_url(self):
         return None
 
+    def latest_layout_test_results_url(self):
+        return self.accumulated_results_url() or self.latest_cached_build().results_url();
+
+    @memoized
+    def latest_layout_test_results(self):
+        return self.fetch_layout_test_results(self.latest_layout_test_results_url())
+
+    def _fetch_file_from_results(self, results_url, file_name):
+        # It seems this can return None if the url redirects and then returns 404.
+        result = urllib2.urlopen("%s/%s" % (results_url, file_name))
+        if not result:
+            return None
+        # urlopen returns a file-like object which sometimes works fine with str()
+        # but sometimes is a addinfourl object.  In either case calling read() is correct.
+        return result.read()
+
+    def fetch_layout_test_results(self, results_url):
+        # FIXME: This should cache that the result was a 404 and stop hitting the network.
+        results_file = NetworkTransaction(convert_404_to_None=True).run(lambda: self._fetch_file_from_results(results_url, "full_results.json"))
+        if not results_file:
+            results_file = NetworkTransaction(convert_404_to_None=True).run(lambda: self._fetch_file_from_results(results_url, "results.html"))
+
+        # results_from_string accepts either ORWT html or NRWT json.
+        return LayoutTestResults.results_from_string(results_file)
+
     def url_encoded_name(self):
         return urllib.quote(self._name)
 
@@ -229,7 +255,6 @@
         self._number = build_number
         self._revision = revision
         self._is_green = is_green
-        self._layout_test_results = None
 
     @staticmethod
     def build_url(builder, build_number):
@@ -245,28 +270,10 @@
     def results_zip_url(self):
         return "%s.zip" % self.results_url()
 
-    def _fetch_file_from_results(self, file_name):
-        # It seems this can return None if the url redirects and then returns 404.
-        result = urllib2.urlopen("%s/%s" % (self.results_url(), file_name))
-        if not result:
-            return None
-        # urlopen returns a file-like object which sometimes works fine with str()
-        # but sometimes is a addinfourl object.  In either case calling read() is correct.
-        return result.read()
-
+    @memoized
     def layout_test_results(self):
-        if self._layout_test_results:
-            return self._layout_test_results
+        return self._builder.fetch_layout_test_results(self.results_url())
 
-        # FIXME: This should cache that the result was a 404 and stop hitting the network.
-        results_file = NetworkTransaction(convert_404_to_None=True).run(lambda: self._fetch_file_from_results("full_results.json"))
-        if not results_file:
-            results_file = NetworkTransaction(convert_404_to_None=True).run(lambda: self._fetch_file_from_results("results.html"))
-
-        # results_from_string accepts either ORWT html or NRWT json.
-        self._layout_test_results = LayoutTestResults.results_from_string(results_file)
-        return self._layout_test_results
-
     def builder(self):
         return self._builder
 

Modified: trunk/Tools/Scripts/webkitpy/common/net/buildbot/buildbot_mock.py (122233 => 122234)


--- trunk/Tools/Scripts/webkitpy/common/net/buildbot/buildbot_mock.py	2012-07-10 17:05:24 UTC (rev 122233)
+++ trunk/Tools/Scripts/webkitpy/common/net/buildbot/buildbot_mock.py	2012-07-10 17:33:41 UTC (rev 122234)
@@ -49,6 +49,9 @@
     def accumulated_results_url(self):
         return "http://example.com/f/builders/%s/results/layout-test-results" % self.name()
 
+    def latest_layout_test_results_url(self):
+        return self.accumulated_results_url()
+
     def force_build(self, username, comments):
         log("MOCK: force_build: name=%s, username=%s, comments=%s" % (
             self._name, username, comments))

Modified: trunk/Tools/Scripts/webkitpy/common/net/buildbot/buildbot_unittest.py (122233 => 122234)


--- trunk/Tools/Scripts/webkitpy/common/net/buildbot/buildbot_unittest.py	2012-07-10 17:05:24 UTC (rev 122233)
+++ trunk/Tools/Scripts/webkitpy/common/net/buildbot/buildbot_unittest.py	2012-07-10 17:33:41 UTC (rev 122234)
@@ -48,7 +48,10 @@
                 is_green=build_number < 4
             )
             results = [self._mock_test_result(testname) for testname in failure(build_number)]
-            build._layout_test_results = LayoutTestResults(results)
+            layout_test_results = LayoutTestResults(results)
+            def mock_layout_test_results():
+                return layout_test_results
+            build.layout_test_results = mock_layout_test_results
             return build
         self.builder._fetch_build = _mock_fetch_build
 
@@ -57,6 +60,11 @@
         self.builder = Builder(u"Test Builder \u2661", self.buildbot)
         self._install_fetch_build(lambda build_number: ["test1", "test2"])
 
+    def test_latest_layout_test_results(self):
+        self.builder.fetch_layout_test_results = lambda results_url: LayoutTestResults([self._mock_test_result(testname) for testname in ["test1", "test2"]])
+        self.builder.accumulated_results_url = lambda: "http://dummy_url.org"
+        self.assertTrue(self.builder.latest_layout_test_results())
+
     def test_find_regression_window(self):
         regression_window = self.builder.find_regression_window(self.builder.build(10))
         self.assertEqual(regression_window.build_before_failure().revision(), 1003)
@@ -147,8 +155,8 @@
     def test_layout_test_results(self):
         buildbot = BuildBot()
         builder = Builder(u"Foo Builder (test)", buildbot)
+        builder._fetch_file_from_results = lambda results_url, file_name: None
         build = Build(builder, None, None, None)
-        build._fetch_file_from_results = lambda file_name: None
         # Test that layout_test_results() returns None if the fetch fails.
         self.assertEqual(build.layout_test_results(), None)
 

Modified: trunk/Tools/Scripts/webkitpy/common/system/user.py (122233 => 122234)


--- trunk/Tools/Scripts/webkitpy/common/system/user.py	2012-07-10 17:05:24 UTC (rev 122233)
+++ trunk/Tools/Scripts/webkitpy/common/system/user.py	2012-07-10 17:33:41 UTC (rev 122234)
@@ -74,14 +74,20 @@
         return cls.prompt(message, repeat=repeat, raw_input=getpass.getpass)
 
     @classmethod
-    def prompt_with_list(cls, list_title, list_items, can_choose_multiple=False, raw_input=raw_input):
+    def prompt_with_multiple_lists(cls, list_title, subtitles, lists, can_choose_multiple=False, raw_input=raw_input):
+        item_index = 0
+        cumulated_list = []
         print list_title
-        i = 0
-        for item in list_items:
-            i += 1
-            print "%2d. %s" % (i, item)
+        for i in range(len(subtitles)):
+            print "\n" + subtitles[i]
+            for item in lists[i]:
+                item_index += 1
+                print "%2d. %s" % (item_index, item)
+            cumulated_list += lists[i]
+        return cls._wait_on_list_response(cumulated_list, can_choose_multiple, raw_input)
 
-        # Loop until we get valid input
+    @classmethod
+    def _wait_on_list_response(cls, list_items, can_choose_multiple, raw_input):
         while True:
             if can_choose_multiple:
                 response = cls.prompt("Enter one or more numbers (comma-separated), or \"all\": ", raw_input=raw_input)
@@ -99,6 +105,15 @@
                     continue
                 return list_items[result]
 
+    @classmethod
+    def prompt_with_list(cls, list_title, list_items, can_choose_multiple=False, raw_input=raw_input):
+        print list_title
+        i = 0
+        for item in list_items:
+            i += 1
+            print "%2d. %s" % (i, item)
+        return cls._wait_on_list_response(list_items, can_choose_multiple, raw_input)
+
     def edit(self, files):
         editor = os.environ.get("EDITOR") or "vi"
         args = shlex.split(editor)

Modified: trunk/Tools/Scripts/webkitpy/common/system/user_unittest.py (122233 => 122234)


--- trunk/Tools/Scripts/webkitpy/common/system/user_unittest.py	2012-07-10 17:05:24 UTC (rev 122233)
+++ trunk/Tools/Scripts/webkitpy/common/system/user_unittest.py	2012-07-10 17:33:41 UTC (rev 122234)
@@ -51,6 +51,32 @@
             return None
         self.assertEqual(User.prompt("input", repeat=self.repeatsRemaining, raw_input=mock_raw_input), None)
 
+    def test_prompt_with_multiple_lists(self):
+        def run_prompt_test(inputs, expected_result, can_choose_multiple=False):
+            def mock_raw_input(message):
+                return inputs.pop(0)
+            output_capture = OutputCapture()
+            actual_result = output_capture.assert_outputs(
+                self,
+                User.prompt_with_multiple_lists,
+                args=["title", ["subtitle1", "subtitle2"], [["foo", "bar"], ["foobar", "barbaz"]]],
+                kwargs={"can_choose_multiple": can_choose_multiple, "raw_input": mock_raw_input},
+                expected_stdout="title\n\nsubtitle1\n 1. foo\n 2. bar\n\nsubtitle2\n 3. foobar\n 4. barbaz\n")
+            self.assertEqual(actual_result, expected_result)
+            self.assertEqual(len(inputs), 0)
+
+        run_prompt_test(["1"], "foo")
+        run_prompt_test(["badinput", "2"], "bar")
+        run_prompt_test(["3"], "foobar")
+        run_prompt_test(["4"], "barbaz")
+
+        run_prompt_test(["1,2"], ["foo", "bar"], can_choose_multiple=True)
+        run_prompt_test(["  1,  2   "], ["foo", "bar"], can_choose_multiple=True)
+        run_prompt_test(["all"], ["foo", "bar", 'foobar', 'barbaz'], can_choose_multiple=True)
+        run_prompt_test([""], ["foo", "bar", 'foobar', 'barbaz'], can_choose_multiple=True)
+        run_prompt_test(["  "], ["foo", "bar", 'foobar', 'barbaz'], can_choose_multiple=True)
+        run_prompt_test(["badinput", "all"], ["foo", "bar", 'foobar', 'barbaz'], can_choose_multiple=True)
+
     def test_prompt_with_list(self):
         def run_prompt_test(inputs, expected_result, can_choose_multiple=False):
             def mock_raw_input(message):

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/port/base.py (122233 => 122234)


--- trunk/Tools/Scripts/webkitpy/layout_tests/port/base.py	2012-07-10 17:05:24 UTC (rev 122233)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/port/base.py	2012-07-10 17:33:41 UTC (rev 122234)
@@ -654,6 +654,9 @@
                 return True
         return False
 
+    def is_chromium(self):
+        return False
+
     def name(self):
         """Returns a name that uniquely identifies this particular type of port
         (e.g., "mac-snowleopard" or "chromium-linux-x86_x64" and can be passed

Modified: trunk/Tools/Scripts/webkitpy/layout_tests/port/builders.py (122233 => 122234)


--- trunk/Tools/Scripts/webkitpy/layout_tests/port/builders.py	2012-07-10 17:05:24 UTC (rev 122233)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/port/builders.py	2012-07-10 17:33:41 UTC (rev 122234)
@@ -96,12 +96,10 @@
     return re.sub(r'[\s().]', '_', builder_name)
 
 
-@memoized
 def all_builder_names():
     return sorted(set(_exact_matches.keys()))
 
 
-@memoized
 def all_port_names():
     return sorted(set(map(lambda x: x["port_name"], _exact_matches.values()) + _ports_without_builders))
 

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


--- trunk/Tools/Scripts/webkitpy/layout_tests/port/chromium.py	2012-07-10 17:05:24 UTC (rev 122233)
+++ trunk/Tools/Scripts/webkitpy/layout_tests/port/chromium.py	2012-07-10 17:33:41 UTC (rev 122234)
@@ -110,6 +110,9 @@
         # All sub-classes override this, but we need an initial value for testing.
         self._chromium_base_dir_path = None
 
+    def is_chromium(self):
+        return True
+
     def default_pixel_tests(self):
         return True
 

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


--- trunk/Tools/Scripts/webkitpy/tool/commands/rebaseline.py	2012-07-10 17:05:24 UTC (rev 122233)
+++ trunk/Tools/Scripts/webkitpy/tool/commands/rebaseline.py	2012-07-10 17:33:41 UTC (rev 122234)
@@ -81,11 +81,7 @@
         self._scm_changes = {'add': []}
 
     def _results_url(self, builder_name):
-        port = self._tool.port_factory.get_from_builder_name(builder_name)
-        # FIXME: Come up with a better way than string manipulation to see if the port is a chromium port.
-        if port.name().startswith('chromium-'):
-            return self._tool.chromium_buildbot().builder_with_name(builder_name).accumulated_results_url()
-        return self._tool.buildbot.builder_with_name(builder_name).latest_cached_build().results_url()
+        return self._tool.buildbot_for_builder_name(builder_name).builder_with_name(builder_name).latest_layout_test_results_url()
 
     def _baseline_directory(self, builder_name):
         port = self._tool.port_factory.get_from_builder_name(builder_name)
@@ -229,13 +225,13 @@
 
 
 class AbstractParallelRebaselineCommand(AbstractDeclarativeCommand):
-    def __init__(self):
-        options = [
+    def __init__(self, options=None):
+        options = options or []
+        options.extend([
             optparse.make_option('--no-optimize', dest='optimize', action='', default=True,
                 help=('Do not optimize/de-dup the expectations after rebaselining '
                       '(default is to de-dup automatically). '
-                      'You can use "webkit-patch optimize-baselines" to optimize separately.')),
-        ]
+                      'You can use "webkit-patch optimize-baselines" to optimize separately.'))])
         AbstractDeclarativeCommand.__init__(self, options=options)
 
     def _run_webkit_patch(self, args):
@@ -369,30 +365,56 @@
 
 class Rebaseline(AbstractParallelRebaselineCommand):
     name = "rebaseline"
-    help_text = "Replaces local expected.txt files with new results from build bots"
+    help_text = "Replaces local expected.txt files with new results from build bots. Shows the list of failing tests on the builders if no test names are provided."
+    argument_names = "[TEST_NAMES]"
 
-    # FIXME: This should share more code with FailureReason._builder_to_explain
-    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"]]
-        _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.
-        for status in red_statuses:
-            if status["name"] == chosen_name:
-                return (self._tool.buildbot.builder_with_name(chosen_name), status["build_number"])
+    def __init__(self):
+        options = [
+            optparse.make_option("--builders", default=None, action="" help="Comma-separated-list of builders to pull new baselines from (can also be provided multiple times)"),
+        ]
+        AbstractParallelRebaselineCommand.__init__(self, options=options)
 
-    def _tests_to_update(self, build):
-        failing_tests = build.layout_test_results().tests_matching_failure_types([test_failures.FailureTextMismatch])
-        return self._tool.user.prompt_with_list("Which test(s) to rebaseline:", failing_tests, can_choose_multiple=True)
+    def _builders_to_pull_from(self):
+        chromium_buildbot_builder_names = []
+        webkit_buildbot_builder_names = []
+        for name in builders.all_builder_names():
+            if self._tool.port_factory.get_from_builder_name(name).is_chromium():
+                chromium_buildbot_builder_names.append(name)
+            else:
+                webkit_buildbot_builder_names.append(name)
 
+        titles = ["build.webkit.org bots", "build.chromium.org bots"]
+        lists = [webkit_buildbot_builder_names, chromium_buildbot_builder_names]
+
+        chosen_names = self._tool.user.prompt_with_multiple_lists("Which builder to pull results from:", titles, lists, can_choose_multiple=True)
+        return [self._builder_with_name(name) for name in chosen_names]
+
+    def _builder_with_name(self, name):
+        return self._tool.buildbot_for_builder_name(name).builder_with_name(name)
+
+    def _tests_to_update(self, builder):
+        failing_tests = builder.latest_layout_test_results().tests_matching_failure_types([test_failures.FailureTextMismatch])
+        return self._tool.user.prompt_with_list("Which test(s) to rebaseline for %s:" % builder.name(), failing_tests, can_choose_multiple=True)
+
     def execute(self, options, args, tool):
-        builder, build_number = self._builder_to_pull_from()
-        build = builder.build(build_number)
+        if options.builders:
+            builders = []
+            for builder_names in options.builders:
+                builders += [self._builder_with_name(name) for name in builder_names.split(",")]
+        else:
+            builders = self._builders_to_pull_from()
 
-        builder_name = builder.name()
         test_list = {}
-        for test in self._tests_to_update(build):
-            test_list[test] = {builder_name: ['txt']}
+
+        for builder in builders:
+            tests = args or self._tests_to_update(builder)
+            for test in tests:
+                if test not in test_list:
+                    test_list[test] = {}
+                # FIXME: Allow for choosing the suffixes.
+                test_list[test][builder.name()] = ['txt']
+
+        if options.verbose:
+            print "rebaseline-json: " + str(test_list)
+
         self._rebaseline(options, test_list)

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


--- trunk/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py	2012-07-10 17:05:24 UTC (rev 122233)
+++ trunk/Tools/Scripts/webkitpy/tool/commands/rebaseline_unittest.py	2012-07-10 17:33:41 UTC (rev 122234)
@@ -351,20 +351,132 @@
 
             tool.executive = MockExecutive(should_log=True)
 
-            def mock_builder_to_pull_from():
-                return MockBuilder('MOCK builder'), 1234
+            def mock_builders_to_pull_from():
+                return [MockBuilder('MOCK builder')]
 
             def mock_tests_to_update(build):
                 return ['mock/path/to/test.html']
 
-            command._builder_to_pull_from = mock_builder_to_pull_from
+            command._builders_to_pull_from = mock_builders_to_pull_from
             command._tests_to_update = mock_tests_to_update
 
+            expected_stdout = """rebaseline-json: {'mock/path/to/test.html': {'MOCK builder': ['txt']}}
+"""
+
             expected_stderr = """MOCK run_command: ['echo', 'rebaseline-test-internal', '--suffixes', 'txt', '--builder', 'MOCK builder', '--test', 'mock/path/to/test.html'], cwd=/mock-checkout
 MOCK run_command: ['echo', 'optimize-baselines', '--suffixes', 'txt', 'mock/path/to/test.html'], cwd=/mock-checkout
 """
 
-            OutputCapture().assert_outputs(self, command.execute, [MockOptions(optimize=True), [], tool], expected_stderr=expected_stderr)
+            OutputCapture().assert_outputs(self, command.execute, [MockOptions(optimize=True, builders=None, verbose=True), [], tool], expected_stdout=expected_stdout, expected_stderr=expected_stderr)
 
         finally:
             builders._exact_matches = old_exact_matches
+
+    def test_rebaseline_command_line_flags(self):
+        old_exact_matches = builders._exact_matches
+        try:
+            builders._exact_matches = {
+                "MOCK builder": {"port_name": "test-mac-leopard", "specifiers": set(["mock-specifier"])},
+            }
+
+            command = Rebaseline()
+            tool = MockTool()
+            command.bind_to_tool(tool)
+
+            for port_name in tool.port_factory.all_port_names():
+                port = tool.port_factory.get(port_name)
+                for path in port.expectations_files():
+                    tool.filesystem.write_text_file(path, '')
+
+            tool.executive = MockExecutive(should_log=True)
+
+            expected_stdout = """rebaseline-json: {'mock/path/to/test.html': {'MOCK builder': ['txt']}}
+"""
+
+            expected_stderr = """MOCK run_command: ['echo', 'rebaseline-test-internal', '--suffixes', 'txt', '--builder', 'MOCK builder', '--test', 'mock/path/to/test.html'], cwd=/mock-checkout
+MOCK run_command: ['echo', 'optimize-baselines', '--suffixes', 'txt', 'mock/path/to/test.html'], cwd=/mock-checkout
+"""
+
+            builder = "MOCK builder"
+            test = "mock/path/to/test.html"
+            OutputCapture().assert_outputs(self, command.execute, [MockOptions(optimize=True, builders=[builder], verbose=True), [test], tool], expected_stdout=expected_stdout, expected_stderr=expected_stderr)
+
+        finally:
+            builders._exact_matches = old_exact_matches
+
+    def test_rebaseline_multiple_builders(self):
+        old_exact_matches = builders._exact_matches
+        try:
+            builders._exact_matches = {
+                "MOCK builder": {"port_name": "test-mac-leopard", "specifiers": set(["mock-specifier"])},
+                "MOCK builder2": {"port_name": "test-mac-snowleopard", "specifiers": set(["mock-specifier2"])},
+            }
+
+            command = Rebaseline()
+            tool = MockTool()
+            command.bind_to_tool(tool)
+
+            for port_name in tool.port_factory.all_port_names():
+                port = tool.port_factory.get(port_name)
+                for path in port.expectations_files():
+                    tool.filesystem.write_text_file(path, '')
+
+            tool.executive = MockExecutive(should_log=True)
+
+            def mock_builders_to_pull_from():
+                return [MockBuilder('MOCK builder'), MockBuilder('MOCK builder2')]
+
+            def mock_tests_to_update(build):
+                return ['mock/path/to/test.html']
+
+            command._builders_to_pull_from = mock_builders_to_pull_from
+            command._tests_to_update = mock_tests_to_update
+
+            expected_stdout = """rebaseline-json: {'mock/path/to/test.html': {'MOCK builder2': ['txt'], 'MOCK builder': ['txt']}}
+"""
+
+            expected_stderr = """MOCK run_command: ['echo', 'rebaseline-test-internal', '--suffixes', 'txt', '--builder', 'MOCK builder2', '--test', 'mock/path/to/test.html'], cwd=/mock-checkout
+MOCK run_command: ['echo', 'rebaseline-test-internal', '--suffixes', 'txt', '--builder', 'MOCK builder', '--test', 'mock/path/to/test.html'], cwd=/mock-checkout
+MOCK run_command: ['echo', 'optimize-baselines', '--suffixes', 'txt', 'mock/path/to/test.html'], cwd=/mock-checkout
+"""
+
+            OutputCapture().assert_outputs(self, command.execute, [MockOptions(optimize=True, builders=None, verbose=True), [], tool], expected_stdout=expected_stdout, expected_stderr=expected_stderr)
+
+        finally:
+            builders._exact_matches = old_exact_matches
+
+    def test_rebaseline_multiple_builders_and_tests_command_line(self):
+        old_exact_matches = builders._exact_matches
+        try:
+            builders._exact_matches = {
+                "MOCK builder": {"port_name": "test-mac-leopard", "specifiers": set(["mock-specifier"])},
+                "MOCK builder2": {"port_name": "test-mac-snowleopard", "specifiers": set(["mock-specifier2"])},
+                "MOCK builder3": {"port_name": "test-mac-snowleopard", "specifiers": set(["mock-specifier2"])},
+            }
+
+            command = Rebaseline()
+            tool = MockTool()
+            command.bind_to_tool(tool)
+
+            for port_name in tool.port_factory.all_port_names():
+                port = tool.port_factory.get(port_name)
+                for path in port.expectations_files():
+                    tool.filesystem.write_text_file(path, '')
+
+            tool.executive = MockExecutive(should_log=True)
+
+            expected_stdout = """rebaseline-json: {'mock/path/to/test.html': {'MOCK builder2': ['txt'], 'MOCK builder': ['txt'], 'MOCK builder3': ['txt']}, 'mock/path/to/test2.html': {'MOCK builder2': ['txt'], 'MOCK builder': ['txt'], 'MOCK builder3': ['txt']}}
+"""
+
+            expected_stderr = """MOCK run_command: ['echo', 'rebaseline-test-internal', '--suffixes', 'txt', '--builder', 'MOCK builder2', '--test', 'mock/path/to/test.html'], cwd=/mock-checkout
+MOCK run_command: ['echo', 'rebaseline-test-internal', '--suffixes', 'txt', '--builder', 'MOCK builder', '--test', 'mock/path/to/test.html'], cwd=/mock-checkout
+MOCK run_command: ['echo', 'rebaseline-test-internal', '--suffixes', 'txt', '--builder', 'MOCK builder2', '--test', 'mock/path/to/test2.html'], cwd=/mock-checkout
+MOCK run_command: ['echo', 'rebaseline-test-internal', '--suffixes', 'txt', '--builder', 'MOCK builder', '--test', 'mock/path/to/test2.html'], cwd=/mock-checkout
+MOCK run_command: ['echo', 'optimize-baselines', '--suffixes', 'txt', 'mock/path/to/test.html'], cwd=/mock-checkout
+MOCK run_command: ['echo', 'optimize-baselines', '--suffixes', 'txt', 'mock/path/to/test2.html'], cwd=/mock-checkout
+"""
+
+            OutputCapture().assert_outputs(self, command.execute, [MockOptions(optimize=True, builders=["MOCK builder,MOCK builder2", "MOCK builder3"], verbose=True), ["mock/path/to/test.html", "mock/path/to/test2.html"], tool], expected_stdout=expected_stdout, expected_stderr=expected_stderr)
+
+        finally:
+            builders._exact_matches = old_exact_matches

Modified: trunk/Tools/Scripts/webkitpy/tool/mocktool.py (122233 => 122234)


--- trunk/Tools/Scripts/webkitpy/tool/mocktool.py	2012-07-10 17:05:24 UTC (rev 122233)
+++ trunk/Tools/Scripts/webkitpy/tool/mocktool.py	2012-07-10 17:33:41 UTC (rev 122234)
@@ -29,6 +29,7 @@
 import threading
 
 from webkitpy.common.host_mock import MockHost
+from webkitpy.common.net.buildbot.buildbot_mock import MockBuildBot
 from webkitpy.common.net.statusserver_mock import MockStatusServer
 from webkitpy.common.net.irc.irc_mock import MockIRC
 
@@ -82,3 +83,6 @@
 
     def irc(self):
         return self._irc
+
+    def buildbot_for_builder_name(self, name):
+        return MockBuildBot()
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to