Add links to existing bugs related to failing tests on TestFailures page
authoraroben@apple.com <aroben@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 22 Jun 2011 17:27:22 +0000 (17:27 +0000)
committeraroben@apple.com <aroben@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 22 Jun 2011 17:27:22 +0000 (17:27 +0000)
I changed the layout of the page a little to make it easier to read with all the new
information. Passing/failing revisions have been moved down below the list of tests to be
closer to the existing bugs and the new bug link. And each set of tests and its relevant
information is in a light gray box.

Fixes <http://webkit.org/b/61665> TestFailures page should link to existing bugs when
possible

Reviewed by Darin Adler.

* BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/Bugzilla.js: Added.
(Bugzilla): This new class represents a single Bugzilla instance.
(Bugzilla.prototype.quickSearch): Searches Bugzilla using its Quick Search functionality,
passing the resulting bug titles and URLs to the callback when complete. If called multiple
times with the same query before the query returns, caches the callbacks so that only one
query is sent over the wire. When the query completes, all pending callbacks are called.

* BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/TestFailures.css:
(#failure-history): Reduce the margin/padding on the top-level list a bit.
(#failure-history > li): Put each set of tests in a gray box, and indent most information
inside the box.
(.test-list): Unindent the list of failing tests so it is visually at the top level.
(.new-and-existing-bugs): Reduce the space at the bottom of this area so that the bottom of
each box isn't a big empty space.
(.existing-bugs-list): Use a smaller text size for existing bugs, since their titles can be
quite long.

* BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/Utilities.js:
(addQueryParametersToURL): New function extracted from
ViewController.prototype._domForNewAndExistingBugs.

* BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/ViewController.js:
(ViewController): Take and store an optional Bugzilla instance.
(ViewController.prototype._displayBuilder): Give the top-level list an id for styling
purposes and move the list of failing tests above all other information. Only show bug
information once we've determined the most-recent passing revision for a set of tests. It's
not that useful to file a new bug before this information has been determined, and searching
for existing bugs before we've figured out which tests started failing at the same time
would end up giving you information about a bunch of unrelated tests.
(ViewController.prototype._domForNewAndExistingBugs): Renamed from _domForNewBugLink. Now
returns a DocumentFragment instead of an HTMLParagraphElement. If we don't have a Bugzilla
instance, just returns an empty DocumentFragment. Starts a search for bugs related to the
failing tests, and adds links to the bugs when the search completes.

* BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/index.html: Pass a
Bugzilla instance for bugs.webkit.org to the ViewController.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@89447 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Tools/BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/Bugzilla.js [new file with mode: 0644]
Tools/BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/TestFailures.css
Tools/BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/Utilities.js
Tools/BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/ViewController.js
Tools/BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/index.html
Tools/ChangeLog

diff --git a/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/Bugzilla.js b/Tools/BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/Bugzilla.js
new file mode 100644 (file)
index 0000000..db1320b
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2011 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+function Bugzilla(baseURL) {
+    this.baseURL = baseURL;
+    this._cache = {};
+}
+
+Bugzilla.prototype = {
+    quickSearch: function(query, callback) {
+        var cacheKey = 'quickSearch_' + query;
+        if (cacheKey in this._cache) {
+            callback(this._cache[cacheKey]);
+            return;
+        }
+
+        var callbacksCacheKey = 'quickSearchCallbacks_' + query;
+        if (callbacksCacheKey in this._cache) {
+            this._cache[callbacksCacheKey].push(callback);
+            return;
+        }
+
+        this._cache[callbacksCacheKey] = [callback];
+
+        var queryParameters = {
+            ctype: 'rss',
+            order: 'bugs.bug_id desc',
+            quicksearch: query,
+        };
+
+        var self = this;
+        getResource(addQueryParametersToURL(this.baseURL + 'buglist.cgi', queryParameters), function(xhr) {
+            var entries = xhr.responseXML.getElementsByTagName('entry');
+            var results = Array.prototype.map.call(entries, function(entry) {
+                return {
+                    title: entry.getElementsByTagName('title')[0].textContent,
+                    url: entry.getElementsByTagName('id')[0].textContent,
+                };
+            });
+
+            self._cache[cacheKey] = results;
+
+            var callbacks = self._cache[callbacksCacheKey];
+            delete self._cache[callbacksCacheKey];
+
+            callbacks.forEach(function(callback) {
+                callback(results);
+            });
+        });
+    },
+};
index 66fe816d0b82cc0df414d78d841dd9f35d9abaa7..6dbab5a64b8b5636e04169aa3aad57d69c0b1e57 100644 (file)
@@ -12,6 +12,30 @@ dt::after {
     content: ':';
 }
 
+#failure-history {
+    margin: 0;
+    padding: 0 0 0 15px;
+}
+
+#failure-history > li {
+    background-color: #f0f0f0;
+    padding: 10px 10px 10px 50px;
+    margin-bottom: 10px;
+}
+
+.test-list {
+    margin: 0 0 0 -40px;
+    padding: 0;
+}
+
 .info {
     font-style: italic;
 }
+
+.existing-and-new-bugs {
+    margin-bottom: 0;
+}
+
+.existing-bugs-list {
+    font-size: smaller;
+}
index 85cdf4c32cb84673dd37a7d2ac43b7f49149c802..4abd448e504e5e327604cb8f0b139bebd0fe8622 100644 (file)
@@ -51,6 +51,19 @@ function getResource(url, callback, errorCallback) {
     xhr.send();
 }
 
+function addQueryParametersToURL(url, queryParameters) {
+    var encodedParameters = Object.keys(queryParameters).map(function(key) {
+        return key + '=' + encodeURIComponent(queryParameters[key])
+    });
+
+    if (url.indexOf('?') < 0)
+        url += '?';
+    else
+        url += '&';
+
+    return url + encodedParameters.join('&');
+}
+
 Array.prototype.findFirst = function(predicate) {
     for (var i = 0; i < this.length; ++i) {
         if (predicate(this[i]))
index eb858f679c0d14987dbb905abc94b406173744a1..328a061b7dc447470222263e90f04e67abe98aca 100644 (file)
@@ -23,8 +23,9 @@
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-function ViewController(buildbot) {
+function ViewController(buildbot, bugzilla) {
     this._buildbot = buildbot;
+    this._bugzilla = bugzilla;
 
     var self = this;
     addEventListener('load', function() { self.loaded() }, false);
@@ -48,26 +49,24 @@ ViewController.prototype = {
         var self = this;
         builder.startFetchingBuildHistory(function(history) {
             var list = document.createElement('ol');
+            list.id = 'failure-history';
             Object.keys(history).forEach(function(buildName, buildIndex, buildNameArray) {
                 var failingTestNames = Object.keys(history[buildName].tests);
                 if (!failingTestNames.length)
                     return;
 
-                var passingBuildName;
-                if (buildIndex + 1 < buildNameArray.length)
-                    passingBuildName = buildNameArray[buildIndex + 1];
-
-                var dlItems = [
-                    [document.createTextNode('Failed'), self._domForBuildName(builder, buildName)],
-                ];
-                if (passingBuildName)
-                    dlItems.push([document.createTextNode('Passed'), self._domForBuildName(builder, buildNameArray[buildIndex + 1])]);
-
                 var item = document.createElement('li');
-                item.appendChild(createDefinitionList(dlItems));
                 list.appendChild(item);
 
-                item.appendChild(self._domForNewBugLink(builder, buildName, passingBuildName, failingTestNames));
+                var testList = document.createElement('ol');
+                item.appendChild(testList);
+
+                testList.className = 'test-list';
+                for (var testName in history[buildName].tests) {
+                    var testItem = document.createElement('li');
+                    testItem.appendChild(self._domForFailedTest(builder, buildName, testName, history[buildName].tests[testName]));
+                    testList.appendChild(testItem);
+                }
 
                 if (history[buildName].tooManyFailures) {
                     var p = document.createElement('p');
@@ -76,13 +75,19 @@ ViewController.prototype = {
                     item.appendChild(p);
                 }
 
-                var testList = document.createElement('ol');
-                for (var testName in history[buildName].tests) {
-                    var testItem = document.createElement('li');
-                    testItem.appendChild(self._domForFailedTest(builder, buildName, testName, history[buildName].tests[testName]));
-                    testList.appendChild(testItem);
-                }
-                item.appendChild(testList);
+                var passingBuildName;
+                if (buildIndex + 1 < buildNameArray.length)
+                    passingBuildName = buildNameArray[buildIndex + 1];
+
+                var dlItems = [
+                    [document.createTextNode('Failed'), self._domForBuildName(builder, buildName)],
+                ];
+                if (passingBuildName)
+                    dlItems.push([document.createTextNode('Passed'), self._domForBuildName(builder, buildNameArray[buildIndex + 1])]);
+                item.appendChild(createDefinitionList(dlItems));
+
+                if (passingBuildName)
+                    item.appendChild(self._domForNewAndExistingBugs(builder, buildName, passingBuildName, failingTestNames));
             });
 
             var header = document.createElement('h1');
@@ -184,7 +189,50 @@ ViewController.prototype = {
         return result;
     },
 
-    _domForNewBugLink: function(tester, failingBuildName, passingBuildName, failingTests) {
+    _domForNewAndExistingBugs: function(tester, failingBuildName, passingBuildName, failingTests) {
+        var result = document.createDocumentFragment();
+
+        if (!this._bugzilla)
+            return result;
+
+        var container = document.createElement('p');
+        result.appendChild(container);
+
+        container.className = 'existing-and-new-bugs';
+
+        var bugsContainer = document.createElement('div');
+        container.appendChild(bugsContainer);
+
+        bugsContainer.appendChild(document.createTextNode('Searching for bugs related to ' + (failingTests.length > 1 ? 'these tests' : 'this test') + '\u2026'));
+
+        this._bugzilla.quickSearch(failingTests.join('|'), function(bugs) {
+            if (!bugs.length) {
+                bugsContainer.parentNode.removeChild(bugsContainer);
+                return;
+            }
+
+            while (bugsContainer.firstChild)
+                bugsContainer.removeChild(bugsContainer.firstChild);
+
+            bugsContainer.appendChild(document.createTextNode('Existing bugs related to ' + (failingTests.length > 1 ? 'these tests' : 'this test') + ':'));
+
+            var list = document.createElement('ul');
+            bugsContainer.appendChild(list);
+
+            list.className = 'existing-bugs-list';
+
+            bugs.forEach(function(bug) {
+                var link = document.createElement('a');
+                link.href = bug.url;
+                link.appendChild(document.createTextNode(bug.title));
+
+                var item = document.createElement('li');
+                item.appendChild(link);
+
+                list.appendChild(item);
+            });
+        });
+
         var parsedFailingBuildName = this._buildbot.parseBuildName(failingBuildName);
         var regressionRangeString = 'r' + parsedFailingBuildName.revision;
         if (passingBuildName) {
@@ -202,6 +250,8 @@ ViewController.prototype = {
         var failingResultsHTML = tester.resultsPageURL(failingBuildName);
         description += encodeURI(failingResultsHTML) + ' failed\n';
 
+        // FIXME: Some of this code should move into a new method on the Bugzilla class.
+
         // FIXME: When a newly-added test has been failing since its introduction, it isn't really a
         // "regression". We should use a different title and keywords in that case.
         // <http://webkit.org/b/61645>
@@ -229,17 +279,13 @@ ViewController.prototype = {
                 queryParameters.op_sys = 'Mac OS X 10.5';
         }
 
-        var encodedParameters = Object.keys(queryParameters).map(function(key) {
-            return key + '=' + encodeURIComponent(queryParameters[key])
-        });
-
         var link = document.createElement('a');
-        link.href = 'https://bugs.webkit.org/enter_bug.cgi?' + encodedParameters.join('&');
+        container.appendChild(link);
+
+        link.href = addQueryParametersToURL('https://bugs.webkit.org/enter_bug.cgi', queryParameters);
         link.target = '_blank';
         link.appendChild(document.createTextNode('File bug for ' + (failingTests.length > 1 ? 'these failures' : 'this failure')));
 
-        var p = document.createElement('p');
-        p.appendChild(link);
-        return p;
+        return result;
     },
 };
index 6446ae116fff7ce0eb8c8aa1947d614ec5bbf6e4..15c47bddabf6e9ed5acefc9862c2d1d80ab23aa5 100644 (file)
@@ -27,6 +27,7 @@ THE POSSIBILITY OF SUCH DAMAGE.
 <html>
 <head>
     <link rel="stylesheet" href="TestFailures.css"></link>
+    <script src="Bugzilla.js"></script>
     <script src="Buildbot.js"></script>
     <script src="Builder.js"></script>
     <script src="Utilities.js"></script>
@@ -34,7 +35,7 @@ THE POSSIBILITY OF SUCH DAMAGE.
 
     <script src="WebKitBuildbot.js"></script>
     <script>
-        var viewController = new ViewController(new WebKitBuildbot());
+        var viewController = new ViewController(new WebKitBuildbot(), new Bugzilla('https://bugs.webkit.org/'));
     </script>
 </head>
 <body>
index f7dca9a6108b2eeeab41b945ace690346b087f20..2293bdeda2d771e11b51f6491554864051315529 100644 (file)
@@ -1,3 +1,54 @@
+2011-06-22  Adam Roben  <aroben@apple.com>
+
+        Add links to existing bugs related to failing tests on TestFailures page
+
+        I changed the layout of the page a little to make it easier to read with all the new
+        information. Passing/failing revisions have been moved down below the list of tests to be
+        closer to the existing bugs and the new bug link. And each set of tests and its relevant
+        information is in a light gray box.
+
+        Fixes <http://webkit.org/b/61665> TestFailures page should link to existing bugs when
+        possible
+
+        Reviewed by Darin Adler.
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/Bugzilla.js: Added.
+        (Bugzilla): This new class represents a single Bugzilla instance.
+        (Bugzilla.prototype.quickSearch): Searches Bugzilla using its Quick Search functionality,
+        passing the resulting bug titles and URLs to the callback when complete. If called multiple
+        times with the same query before the query returns, caches the callbacks so that only one
+        query is sent over the wire. When the query completes, all pending callbacks are called.
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/TestFailures.css:
+        (#failure-history): Reduce the margin/padding on the top-level list a bit.
+        (#failure-history > li): Put each set of tests in a gray box, and indent most information
+        inside the box.
+        (.test-list): Unindent the list of failing tests so it is visually at the top level.
+        (.new-and-existing-bugs): Reduce the space at the bottom of this area so that the bottom of
+        each box isn't a big empty space.
+        (.existing-bugs-list): Use a smaller text size for existing bugs, since their titles can be
+        quite long.
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/Utilities.js:
+        (addQueryParametersToURL): New function extracted from
+        ViewController.prototype._domForNewAndExistingBugs.
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/ViewController.js:
+        (ViewController): Take and store an optional Bugzilla instance.
+        (ViewController.prototype._displayBuilder): Give the top-level list an id for styling
+        purposes and move the list of failing tests above all other information. Only show bug
+        information once we've determined the most-recent passing revision for a set of tests. It's
+        not that useful to file a new bug before this information has been determined, and searching
+        for existing bugs before we've figured out which tests started failing at the same time
+        would end up giving you information about a bunch of unrelated tests.
+        (ViewController.prototype._domForNewAndExistingBugs): Renamed from _domForNewBugLink. Now
+        returns a DocumentFragment instead of an HTMLParagraphElement. If we don't have a Bugzilla
+        instance, just returns an empty DocumentFragment. Starts a search for bugs related to the
+        failing tests, and adds links to the bugs when the search completes.
+
+        * BuildSlaveSupport/build.webkit.org-config/public_html/TestFailures/index.html: Pass a
+        Bugzilla instance for bugs.webkit.org to the ViewController.
+
 2011-06-22  Carlos Garcia Campos  <cgarcia@igalia.com>
 
         Reviewed by Martin Robinson.