<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[196794] trunk/Websites/perf.webkit.org</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/196794">196794</a></dd>
<dt>Author</dt> <dd>rniwa@webkit.org</dd>
<dt>Date</dt> <dd>2016-02-18 20:18:27 -0800 (Thu, 18 Feb 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>Perf dashboard should let user cancel pending A/B testing and hide failed ones
https://bugs.webkit.org/show_bug.cgi?id=154433

Reviewed by Chris Dumez.

Added a button to hide a test group in the details view (the bottom table) in the analysis task page, and
&quot;Show hidden tests&quot; link to show the hidden test groups on demand. When a test group is hidden, all pending
requests in the group will also be canceled since a common scenario of using this feature is that the user
had triggered an useless A/B testing; e.g. all builds will fail, wrong, etc... We can revisit and add the
capability to just cancel the pending requests and leaving the group visible later if necessary.

Run `ALTER TYPE build_request_status_type ADD VALUE 'canceled';` to add the new type.

* init-database.sql: Added testgroup_hidden column to analysis_test_groups table and added 'canceled'
as a value to build_request_status_type table.
* public/api/test-groups.php:
(format_test_group): Added 'hidden' field in the JSON result.
* public/privileged-api/update-test-group.php:
(main): Added the support for updating testgroup_hidden column. When this column is set to true, also
cancel all pending build requests (by setting its request_status to 'canceled' which will be ignore by
the syncing script).
* public/v3/components/test-group-results-table.js:
(TestGroupResultsTable.prototype.setTestGroup): Reset _renderedTestGroup here so that the next call to
render() will update the table; e.g. when build requests' status change from 'Pending' to 'Canceled'.
* public/v3/models/build-request.js:
(BuildRequest.prototype.hasCompleted): A build request is considered complete/finished if it's canceled.
(BuildRequest.prototype.hasPending): Added.
(BuildRequest.prototype.statusLabel): Handle 'canceled' status.
* public/v3/models/test-group.js:
(TestGroup):
(TestGroup.prototype.updateSingleton): Added to update 'hidden' field.
(TestGroup.prototype.isHidden): Added.
(TestGroup.prototype.hasPending): Added.
(TestGroup.prototype.hasPending): Added.
(TestGroup.prototype.updateHiddenFlag): Added. Uses the privileged API to update testgroup_hidden column.
The JSON API also updates the status of the 'pending' build requests in the group to 'canceled'.
* public/v3/pages/analysis-task-page.js:
(AnalysisTaskPage): Added _showHiddenTestGroups and _filteredTestGroups as instance variables.
(AnalysisTaskPage.prototype._didFetchTestGroups):
(AnalysisTaskPage.prototype._showAllTestGroups): Added.
(AnalysisTaskPage.prototype._didUpdateTestGroupHiddenState): Extracted from _didFetchTestGroups.
(AnalysisTaskPage.prototype._renderTestGroupList): Use the filtered list of test groups to show the list
of test groups. When all test groups are shown, we would first show the hidden ones after the regular ones.
(AnalysisTaskPage.prototype._createTestGroupListItem): Extracted from _renderTestGroupList.
(AnalysisTaskPage.prototype._renderTestGroupDetails): Update the text inside the button to hide the test
group. Also show a warning text that the pending requests will be canceled if there are any.
(AnalysisTaskPage.prototype._hideCurrentTestGroup): Added.
(AnalysisTaskPage.cssTemplate): Updated the style.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkWebsitesperfwebkitorgChangeLog">trunk/Websites/perf.webkit.org/ChangeLog</a></li>
<li><a href="#trunkWebsitesperfwebkitorginitdatabasesql">trunk/Websites/perf.webkit.org/init-database.sql</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicapitestgroupsphp">trunk/Websites/perf.webkit.org/public/api/test-groups.php</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicprivilegedapiupdatetestgroupphp">trunk/Websites/perf.webkit.org/public/privileged-api/update-test-group.php</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv3componentstestgroupresultstablejs">trunk/Websites/perf.webkit.org/public/v3/components/test-group-results-table.js</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv3modelsbuildrequestjs">trunk/Websites/perf.webkit.org/public/v3/models/build-request.js</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv3modelstestgroupjs">trunk/Websites/perf.webkit.org/public/v3/models/test-group.js</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv3pagesanalysistaskpagejs">trunk/Websites/perf.webkit.org/public/v3/pages/analysis-task-page.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkWebsitesperfwebkitorgChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/ChangeLog (196793 => 196794)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/ChangeLog        2016-02-19 02:45:50 UTC (rev 196793)
+++ trunk/Websites/perf.webkit.org/ChangeLog        2016-02-19 04:18:27 UTC (rev 196794)
</span><span class="lines">@@ -1,5 +1,56 @@
</span><span class="cx"> 2016-02-18  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
</span><span class="cx"> 
</span><ins>+        Perf dashboard should let user cancel pending A/B testing and hide failed ones
+        https://bugs.webkit.org/show_bug.cgi?id=154433
+
+        Reviewed by Chris Dumez.
+
+        Added a button to hide a test group in the details view (the bottom table) in the analysis task page, and
+        &quot;Show hidden tests&quot; link to show the hidden test groups on demand. When a test group is hidden, all pending
+        requests in the group will also be canceled since a common scenario of using this feature is that the user
+        had triggered an useless A/B testing; e.g. all builds will fail, wrong, etc... We can revisit and add the
+        capability to just cancel the pending requests and leaving the group visible later if necessary.
+
+        Run `ALTER TYPE build_request_status_type ADD VALUE 'canceled';` to add the new type.
+
+        * init-database.sql: Added testgroup_hidden column to analysis_test_groups table and added 'canceled'
+        as a value to build_request_status_type table.
+        * public/api/test-groups.php:
+        (format_test_group): Added 'hidden' field in the JSON result.
+        * public/privileged-api/update-test-group.php:
+        (main): Added the support for updating testgroup_hidden column. When this column is set to true, also
+        cancel all pending build requests (by setting its request_status to 'canceled' which will be ignore by
+        the syncing script).
+        * public/v3/components/test-group-results-table.js:
+        (TestGroupResultsTable.prototype.setTestGroup): Reset _renderedTestGroup here so that the next call to
+        render() will update the table; e.g. when build requests' status change from 'Pending' to 'Canceled'.
+        * public/v3/models/build-request.js:
+        (BuildRequest.prototype.hasCompleted): A build request is considered complete/finished if it's canceled.
+        (BuildRequest.prototype.hasPending): Added.
+        (BuildRequest.prototype.statusLabel): Handle 'canceled' status.
+        * public/v3/models/test-group.js:
+        (TestGroup):
+        (TestGroup.prototype.updateSingleton): Added to update 'hidden' field.
+        (TestGroup.prototype.isHidden): Added.
+        (TestGroup.prototype.hasPending): Added.
+        (TestGroup.prototype.hasPending): Added.
+        (TestGroup.prototype.updateHiddenFlag): Added. Uses the privileged API to update testgroup_hidden column.
+        The JSON API also updates the status of the 'pending' build requests in the group to 'canceled'.
+        * public/v3/pages/analysis-task-page.js:
+        (AnalysisTaskPage): Added _showHiddenTestGroups and _filteredTestGroups as instance variables.
+        (AnalysisTaskPage.prototype._didFetchTestGroups):
+        (AnalysisTaskPage.prototype._showAllTestGroups): Added.
+        (AnalysisTaskPage.prototype._didUpdateTestGroupHiddenState): Extracted from _didFetchTestGroups.
+        (AnalysisTaskPage.prototype._renderTestGroupList): Use the filtered list of test groups to show the list
+        of test groups. When all test groups are shown, we would first show the hidden ones after the regular ones.
+        (AnalysisTaskPage.prototype._createTestGroupListItem): Extracted from _renderTestGroupList.
+        (AnalysisTaskPage.prototype._renderTestGroupDetails): Update the text inside the button to hide the test
+        group. Also show a warning text that the pending requests will be canceled if there are any.
+        (AnalysisTaskPage.prototype._hideCurrentTestGroup): Added.
+        (AnalysisTaskPage.cssTemplate): Updated the style.
+
+2016-02-18  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
+
</ins><span class="cx">         The rows in the analysis results table should be expandable
</span><span class="cx">         https://bugs.webkit.org/show_bug.cgi?id=154427
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorginitdatabasesql"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/init-database.sql (196793 => 196794)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/init-database.sql        2016-02-19 02:45:50 UTC (rev 196793)
+++ trunk/Websites/perf.webkit.org/init-database.sql        2016-02-19 04:18:27 UTC (rev 196794)
</span><span class="lines">@@ -225,10 +225,11 @@
</span><span class="cx"> 
</span><span class="cx"> CREATE TABLE analysis_test_groups (
</span><span class="cx">     testgroup_id serial PRIMARY KEY,
</span><del>-    testgroup_task integer REFERENCES analysis_tasks NOT NULL,
</del><ins>+    testgroup_task integer NOT NULL REFERENCES analysis_tasks ON DELETE CASCADE,
</ins><span class="cx">     testgroup_name varchar(256),
</span><span class="cx">     testgroup_author varchar(256),
</span><span class="cx">     testgroup_created_at timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP AT TIME ZONE 'UTC'),
</span><ins>+    testgroup_hidden boolean NOT NULL DEFAULT FALSE,
</ins><span class="cx">     CONSTRAINT testgroup_name_must_be_unique_for_each_task UNIQUE(testgroup_task, testgroup_name));
</span><span class="cx"> CREATE INDEX testgroup_task_index ON analysis_test_groups(testgroup_task);
</span><span class="cx"> 
</span><span class="lines">@@ -239,13 +240,13 @@
</span><span class="cx">     root_set integer REFERENCES root_sets NOT NULL,
</span><span class="cx">     root_commit integer REFERENCES commits NOT NULL);
</span><span class="cx"> 
</span><del>-CREATE TYPE build_request_status_type as ENUM ('pending', 'scheduled', 'running', 'failed', 'completed');
</del><ins>+CREATE TYPE build_request_status_type as ENUM ('pending', 'scheduled', 'running', 'failed', 'completed', 'canceled');
</ins><span class="cx"> CREATE TABLE build_requests (
</span><span class="cx">     request_id serial PRIMARY KEY,
</span><span class="cx">     request_triggerable integer REFERENCES build_triggerables NOT NULL,
</span><span class="cx">     request_platform integer REFERENCES platforms NOT NULL,
</span><span class="cx">     request_test integer REFERENCES tests NOT NULL,
</span><del>-    request_group integer REFERENCES analysis_test_groups NOT NULL,
</del><ins>+    request_group integer NOT NULL REFERENCES analysis_test_groups ON DELETE CASCADE,
</ins><span class="cx">     request_order integer NOT NULL,
</span><span class="cx">     request_root_set integer REFERENCES root_sets NOT NULL,
</span><span class="cx">     request_status build_request_status_type NOT NULL DEFAULT 'pending',
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicapitestgroupsphp"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/api/test-groups.php (196793 => 196794)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/api/test-groups.php        2016-02-19 02:45:50 UTC (rev 196793)
+++ trunk/Websites/perf.webkit.org/public/api/test-groups.php        2016-02-19 04:18:27 UTC (rev 196794)
</span><span class="lines">@@ -65,6 +65,7 @@
</span><span class="cx">         'name' =&gt; $group_row['testgroup_name'],
</span><span class="cx">         'author' =&gt; $group_row['testgroup_author'],
</span><span class="cx">         'createdAt' =&gt; strtotime($group_row['testgroup_created_at']) * 1000,
</span><ins>+        'hidden' =&gt; Database::is_true($group_row['testgroup_hidden']),
</ins><span class="cx">         'buildRequests' =&gt; array(),
</span><span class="cx">         'rootSets' =&gt; array(),
</span><span class="cx">     );
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicprivilegedapiupdatetestgroupphp"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/privileged-api/update-test-group.php (196793 => 196794)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/privileged-api/update-test-group.php        2016-02-19 02:45:50 UTC (rev 196793)
+++ trunk/Websites/perf.webkit.org/public/privileged-api/update-test-group.php        2016-02-19 04:18:27 UTC (rev 196794)
</span><span class="lines">@@ -14,6 +14,9 @@
</span><span class="cx">     if (array_key_exists('name', $data))
</span><span class="cx">         $values['name'] = $data['name'];
</span><span class="cx"> 
</span><ins>+    if (array_key_exists('hidden', $data))
+        $values['hidden'] = Database::to_database_boolean($data['hidden']);
+
</ins><span class="cx">     if (!$values)
</span><span class="cx">         exit_with_error('NothingToUpdate');
</span><span class="cx"> 
</span><span class="lines">@@ -25,6 +28,11 @@
</span><span class="cx">         exit_with_error('FailedToUpdateTestGroup', array('id' =&gt; $test_group_id, 'values' =&gt; $values));
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    if (array_get($data, 'hidden')) {
+        $db-&gt;query_and_get_affected_rows('UPDATE build_requests SET request_status = $1
+            WHERE request_group = $2 AND request_status = $3', array('canceled', $test_group_id, 'pending'));
+    }
+
</ins><span class="cx">     $db-&gt;commit_transaction();
</span><span class="cx"> 
</span><span class="cx">     exit_with_success();
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv3componentstestgroupresultstablejs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v3/components/test-group-results-table.js (196793 => 196794)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v3/components/test-group-results-table.js        2016-02-19 02:45:50 UTC (rev 196793)
+++ trunk/Websites/perf.webkit.org/public/v3/components/test-group-results-table.js        2016-02-19 04:18:27 UTC (rev 196794)
</span><span class="lines">@@ -8,7 +8,11 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     didUpdateResults() { this._renderedTestGroup = null; }
</span><del>-    setTestGroup(testGroup) { this._testGroup = testGroup; }
</del><ins>+    setTestGroup(testGroup)
+    {
+        this._testGroup = testGroup;
+        this._renderedTestGroup = null;
+    }
</ins><span class="cx"> 
</span><span class="cx">     heading()
</span><span class="cx">     {
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv3modelsbuildrequestjs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v3/models/build-request.js (196793 => 196794)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v3/models/build-request.js        2016-02-19 02:45:50 UTC (rev 196793)
+++ trunk/Websites/perf.webkit.org/public/v3/models/build-request.js        2016-02-19 04:18:27 UTC (rev 196794)
</span><span class="lines">@@ -30,8 +30,9 @@
</span><span class="cx">     order() { return this._order; }
</span><span class="cx">     rootSet() { return this._rootSet; }
</span><span class="cx"> 
</span><del>-    hasCompleted() { return this._status == 'failed' || this._status == 'completed'; }
</del><ins>+    hasCompleted() { return this._status == 'failed' || this._status == 'completed' || this._status == 'canceled'; }
</ins><span class="cx">     hasStarted() { return this._status != 'pending'; }
</span><ins>+    hasPending() { return this._status == 'pending'; }
</ins><span class="cx">     statusLabel()
</span><span class="cx">     {
</span><span class="cx">         switch (this._status) {
</span><span class="lines">@@ -45,6 +46,8 @@
</span><span class="cx">             return 'Failed';
</span><span class="cx">         case 'completed':
</span><span class="cx">             return 'Completed';
</span><ins>+        case 'canceled':
+            return 'Canceled';
</ins><span class="cx">         }
</span><span class="cx">     }
</span><span class="cx">     statusUrl() { return this._statusUrl; }
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv3modelstestgroupjs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v3/models/test-group.js (196793 => 196794)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v3/models/test-group.js        2016-02-19 02:45:50 UTC (rev 196793)
+++ trunk/Websites/perf.webkit.org/public/v3/models/test-group.js        2016-02-19 04:18:27 UTC (rev 196794)
</span><span class="lines">@@ -7,6 +7,7 @@
</span><span class="cx">         this._taskId = object.task;
</span><span class="cx">         this._authorName = object.author;
</span><span class="cx">         this._createdAt = new Date(object.createdAt);
</span><ins>+        this._isHidden = object.hidden;
</ins><span class="cx">         this._buildRequests = [];
</span><span class="cx">         this._requestsAreInOrder = false;
</span><span class="cx">         this._repositories = null;
</span><span class="lines">@@ -17,7 +18,19 @@
</span><span class="cx">         this._platform = object.platform;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    updateSingleton(object)
+    {
+        super.updateSingleton(object);
+
+        console.assert(this._taskId == object.task);
+        console.assert(+this._createdAt == +object.createdAt);
+        console.assert(this._platform == object.platform);
+
+        this._isHidden = object.hidden;
+    }
+
</ins><span class="cx">     createdAt() { return this._createdAt; }
</span><ins>+    isHidden() { return this._isHidden; }
</ins><span class="cx">     buildRequests() { return this._buildRequests; }
</span><span class="cx">     addBuildRequest(request)
</span><span class="cx">     {
</span><span class="lines">@@ -96,6 +109,11 @@
</span><span class="cx">         return this._buildRequests.some(function (request) { return request.hasStarted(); });
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    hasPending()
+    {
+        return this._buildRequests.some(function (request) { return request.hasPending(); });
+    }
+
</ins><span class="cx">     compareTestResults(rootSetA, rootSetB)
</span><span class="cx">     {
</span><span class="cx">         var beforeValues = this._valuesForRootSet(rootSetA);
</span><span class="lines">@@ -165,6 +183,19 @@
</span><span class="cx">         });
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    updateHiddenFlag(hidden)
+    {
+        var self = this;
+        var id = this.id();
+        return PrivilegedAPI.sendRequest('update-test-group', {
+            group: id,
+            hidden: !!hidden,
+        }).then(function (data) {
+            return TestGroup.cachedFetch(`../api/test-groups/${id}`, {}, true)
+                .then(TestGroup._createModelsFromFetchedTestGroups.bind(TestGroup));
+        });
+    }
+
</ins><span class="cx">     static createAndRefetchTestGroups(task, name, repetitionCount, rootSets)
</span><span class="cx">     {
</span><span class="cx">         var self = this;
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv3pagesanalysistaskpagejs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v3/pages/analysis-task-page.js (196793 => 196794)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v3/pages/analysis-task-page.js        2016-02-19 02:45:50 UTC (rev 196793)
+++ trunk/Websites/perf.webkit.org/public/v3/pages/analysis-task-page.js        2016-02-19 04:18:27 UTC (rev 196794)
</span><span class="lines">@@ -44,6 +44,8 @@
</span><span class="cx">         this._endPoint = null;
</span><span class="cx">         this._errorMessage = null;
</span><span class="cx">         this._currentTestGroup = null;
</span><ins>+        this._filteredTestGroups = null;
+        this._showHiddenTestGroups = false;
</ins><span class="cx"> 
</span><span class="cx">         this._chartPane = this.content().querySelector('analysis-task-chart-pane').component();
</span><span class="cx">         this._chartPane.setPage(this);
</span><span class="lines">@@ -69,8 +71,10 @@
</span><span class="cx">         this._newTestGroupFormForViewer = this.content().querySelector('.analysis-results-view customizable-test-group-form').component();
</span><span class="cx">         this._newTestGroupFormForViewer.setStartCallback(this._createNewTestGroupFromViewer.bind(this));
</span><span class="cx"> 
</span><del>-        this._retryForm = this.content().querySelector('.test-group-retry-form').firstChild.component();
</del><ins>+        this._retryForm = this.content().querySelector('.test-group-retry-form test-group-form').component();
</ins><span class="cx">         this._retryForm.setStartCallback(this._retryCurrentTestGroup.bind(this));
</span><ins>+        this._hideButton = this.content().querySelector('.test-group-hide-button');
+        this._hideButton.onclick = this._hideCurrentTestGroup.bind(this);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     title() { return this._task ? this._task.label() : 'Analysis Task'; }
</span><span class="lines">@@ -157,14 +161,31 @@
</span><span class="cx">     _didFetchTestGroups(testGroups)
</span><span class="cx">     {
</span><span class="cx">         this._testGroups = testGroups.sort(function (a, b) { return +a.createdAt() - b.createdAt(); });
</span><del>-        this._currentTestGroup = testGroups.length ? testGroups[testGroups.length - 1] : null;
-
-        this._analysisResultsViewer.setTestGroups(testGroups);
-        this._testGroupResultsTable.setTestGroup(this._currentTestGroup);
</del><ins>+        this._didUpdateTestGroupHiddenState();
</ins><span class="cx">         this._assignTestResultsIfPossible();
</span><span class="cx">         this.render();
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    _showAllTestGroups()
+    {
+        this._showHiddenTestGroups = true;
+        this._didUpdateTestGroupHiddenState();
+        this.render();
+    }
+
+    _didUpdateTestGroupHiddenState()
+    {
+        this._renderedCurrentTestGroup = null;
+        this._renderedTestGroups = null;
+        if (!this._showHiddenTestGroups)
+            this._filteredTestGroups = this._testGroups.filter(function (group) { return !group.isHidden(); });
+        else
+            this._filteredTestGroups = this._testGroups;
+        this._currentTestGroup = this._filteredTestGroups ? this._filteredTestGroups[this._filteredTestGroups.length - 1] : null;
+        this._analysisResultsViewer.setTestGroups(this._filteredTestGroups);
+        this._testGroupResultsTable.setTestGroup(this._currentTestGroup);
+    }
+
</ins><span class="cx">     _didFetchAnalysisResults(results)
</span><span class="cx">     {
</span><span class="cx">         this._analysisResults = results;
</span><span class="lines">@@ -277,26 +298,27 @@
</span><span class="cx">             this._renderedTestGroups = this._testGroups;
</span><span class="cx">             this._testGroupLabelMap.clear();
</span><span class="cx"> 
</span><del>-            var self = this;
-            var updateTestGroupName = this._updateTestGroupName.bind(this);
-            var showTestGroup = this._showTestGroup.bind(this);
</del><ins>+            var unhiddenTestGroups = this._filteredTestGroups.filter(function (group) { return !group.isHidden(); });
+            var hiddenTestGroups = this._filteredTestGroups.filter(function (group) { return group.isHidden(); });
</ins><span class="cx"> 
</span><del>-            this.renderReplace(this.content().querySelector('.test-group-list'),
-                this._testGroups.map(function (group) {
-                    var text = new EditableText(group.label());
-                    text.setStartedEditingCallback(function () { return text.render(); });
-                    text.setUpdateCallback(function () { return updateTestGroupName(group); });
</del><ins>+            var listItems = [];
+            for (var group of hiddenTestGroups)
+                listItems.unshift(this._createTestGroupListItem(group));
+            for (var group of unhiddenTestGroups)
+                listItems.unshift(this._createTestGroupListItem(group));
</ins><span class="cx"> 
</span><del>-                    self._testGroupLabelMap.set(group, text);
-                    return element('li', {class: 'test-group-list-' + group.id()},
-                        link(text, group.label(), function () { showTestGroup(group); }));
-                }).reverse());
</del><ins>+            if (this._testGroups.length != this._filteredTestGroups.length) {
+                listItems.push(element('li', {class: 'test-group-list-show-all'},
+                    link('Show hidden tests', this._showAllTestGroups.bind(this))));
+            }
</ins><span class="cx"> 
</span><ins>+            this.renderReplace(this.content().querySelector('.test-group-list'), listItems);
+
</ins><span class="cx">             this._renderedCurrentTestGroup = null;
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         if (this._testGroups) {
</span><del>-            for (var testGroup of this._testGroups) {
</del><ins>+            for (var testGroup of this._filteredTestGroups) {
</ins><span class="cx">                 var label = this._testGroupLabelMap.get(testGroup);
</span><span class="cx">                 label.setText(testGroup.label());
</span><span class="cx">                 label.render();
</span><span class="lines">@@ -304,6 +326,17 @@
</span><span class="cx">         }
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    _createTestGroupListItem(group)
+    {
+        var text = new EditableText(group.label());
+        text.setStartedEditingCallback(function () { return text.render(); });
+        text.setUpdateCallback(this._updateTestGroupName.bind(this, group));
+
+        this._testGroupLabelMap.set(group, text);
+        return ComponentBase.createElement('li', {class: 'test-group-list-' + group.id()},
+            ComponentBase.createLink(text, group.label(), this._showTestGroup.bind(this, group)));
+    }
+
</ins><span class="cx">     _renderTestGroupDetails()
</span><span class="cx">     {
</span><span class="cx">         if (this._renderedCurrentTestGroup !== this._currentTestGroup) {
</span><span class="lines">@@ -332,6 +365,12 @@
</span><span class="cx">                 this._retryForm.setRepetitionCount(this._currentTestGroup.repetitionCount());
</span><span class="cx">             this._retryForm.element().style.display = this._currentTestGroup ? null : 'none';
</span><span class="cx"> 
</span><ins>+            this.content().querySelector('.test-group-hide-button').textContent
+                = this._currentTestGroup &amp;&amp; this._currentTestGroup.isHidden() ? 'Unhide' : 'Hide';
+
+            this.content().querySelector('.pending-request-cancel-warning').style.display
+                = this._currentTestGroup &amp;&amp; this._currentTestGroup.hasPending() ? null : 'none';
+
</ins><span class="cx">             this._renderedCurrentTestGroup = this._currentTestGroup;
</span><span class="cx">         }
</span><span class="cx">         this._retryForm.render();
</span><span class="lines">@@ -373,10 +412,24 @@
</span><span class="cx">             self.render();
</span><span class="cx">         }, function (error) {
</span><span class="cx">             self.render();
</span><del>-            alert('Failed to update the name: ' + error);
</del><ins>+            alert('Failed to hide the test name: ' + error);
</ins><span class="cx">         });
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    _hideCurrentTestGroup()
+    {
+        var self = this;
+        console.assert(this._currentTestGroup);
+        return this._currentTestGroup.updateHiddenFlag(!this._currentTestGroup.isHidden()).then(function () {
+            self._didUpdateTestGroupHiddenState();
+            self.render();
+        }, function (error) {
+            self._mayHaveMutatedTestGroupHiddenState();
+            self.render();
+            alert('Failed to update the group: ' + error);
+        });
+    }
+
</ins><span class="cx">     _updateChangeType(event)
</span><span class="cx">     {
</span><span class="cx">         event.preventDefault();
</span><span class="lines">@@ -569,6 +622,8 @@
</span><span class="cx">                     &lt;div class=&quot;test-group-details&quot;&gt;
</span><span class="cx">                         &lt;test-group-results-table&gt;&lt;/test-group-results-table&gt;
</span><span class="cx">                         &lt;div class=&quot;test-group-retry-form&quot;&gt;&lt;test-group-form&gt;&lt;/test-group-form&gt;&lt;/div&gt;
</span><ins>+                        &lt;button class=&quot;test-group-hide-button&quot;&gt;Hide&lt;/button&gt;
+                        &lt;span class=&quot;pending-request-cancel-warning&quot;&gt;(cancels pending requests)&lt;/span&gt;
</ins><span class="cx">                     &lt;/div&gt;
</span><span class="cx">                 &lt;/section&gt;
</span><span class="cx">             &lt;/div&gt;
</span><span class="lines">@@ -690,6 +745,10 @@
</span><span class="cx">                 margin: 0.5rem;
</span><span class="cx">             }
</span><span class="cx"> 
</span><ins>+            .test-group-hide-button {
+                margin: 0.5rem;
+            }
+
</ins><span class="cx">             .test-group-list {
</span><span class="cx">                 display: table-cell;
</span><span class="cx">                 margin: 0;
</span><span class="lines">@@ -697,6 +756,7 @@
</span><span class="cx">                 list-style: none;
</span><span class="cx">                 border-right: solid 1px #ccc;
</span><span class="cx">                 white-space: nowrap;
</span><ins>+                min-width: 8rem;
</ins><span class="cx">             }
</span><span class="cx"> 
</span><span class="cx">             .test-group-list:empty {
</span><span class="lines">@@ -707,17 +767,29 @@
</span><span class="cx"> 
</span><span class="cx">             .test-group-list &gt; li {
</span><span class="cx">                 display: block;
</span><ins>+                font-size: 0.9rem;
</ins><span class="cx">             }
</span><span class="cx"> 
</span><span class="cx">             .test-group-list &gt; li &gt; a {
</span><span class="cx">                 display: block;
</span><span class="cx">                 color: inherit;
</span><span class="cx">                 text-decoration: none;
</span><del>-                font-size: 0.9rem;
</del><span class="cx">                 margin: 0;
</span><span class="cx">                 padding: 0.2rem;
</span><span class="cx">             }
</span><ins>+            
+            .test-group-list &gt; li.test-group-list-show-all {
+                font-size: 0.8rem;
+                margin-top: 0.5rem;
+                padding-right: 1rem;
+                text-align: center;
+                color: #999;
+            }
</ins><span class="cx"> 
</span><ins>+            .test-group-list &gt; li.test-group-list-show-all:not(.selected) a:hover {
+                background: inherit;
+            }
+
</ins><span class="cx">             .test-group-list &gt; li.selected &gt; a {
</span><span class="cx">                 background: rgba(204, 153, 51, 0.1);
</span><span class="cx">             }
</span></span></pre>
</div>
</div>

</body>
</html>