<!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>[214202] 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/214202">214202</a></dd>
<dt>Author</dt> <dd>rniwa@webkit.org</dd>
<dt>Date</dt> <dd>2017-03-20 18:19:39 -0700 (Mon, 20 Mar 2017)</dd>
</dl>

<h3>Log Message</h3>
<pre>Charts page show an inconsistent list of revisions for Git and Subversion
https://bugs.webkit.org/show_bug.cgi?id=169888

Reviewed by Andreas Kling.

With Git, CommitLogViewer was showing the list of revisions including the starting hash,
which was the last data point's revision instead of all revisions after the last data point.

Fixed the bug by always specifying the revision at the last data point in both Subversion
and Git and then making /api/commits/&lt;repository&gt;/?from=X&amp;to=Y exclude the first revision.
For clarity, &quot;from&quot; and &quot;to&quot; query parameters have been renamed to &quot;precedingRevision&quot; and
&quot;lastRevision&quot; respectively.

We also no longer adds 1 to the starting revision of Subversion-like starting revisions. e.g.
when the last data point was at <a href="http://trac.webkit.org/projects/webkit/changeset/1234">r1234</a>, new data point is at <a href="http://trac.webkit.org/projects/webkit/changeset/1250">r1250</a>, the label is now &quot;<a href="http://trac.webkit.org/projects/webkit/changeset/1234">r1234</a>-<a href="http://trac.webkit.org/projects/webkit/changeset/1250">r1250</a>&quot;
instead of &quot;<a href="http://trac.webkit.org/projects/webkit/changeset/1235">r1235</a>-<a href="http://trac.webkit.org/projects/webkit/changeset/1250">r1250</a>&quot;.

* browser-tests/chart-revision-range-tests.js: Fixed the tests since revisionList no longer
specifies from/to revisions.
* browser-tests/commit-log-viewer-tests.js: Added. Added tests for CommitLogViewer.
* browser-tests/index.html: Include the new test. Also use a local copy of mocha.js/css.

* public/api/commits.php:
(main): Renamed &quot;from&quot; and &quot;to&quot; query parameters.

* public/include/commit-log-fetcher.php:
(CommitLogFetcher::fetch_between): Added a check that commit time should either be specified
in both rows or not specified in either. Also reject when before_first_revision is identical
or after last_revision instead of re-ordering them since it no longer makes sense to do so with
new query parameter names.

* public/v3/components/base.js:
(ComponentBase._addContentToElement): Use Array.isArray instead of instanceof. It's resilient
againt realm (global object) differences.

* public/v3/components/chart-pane-base.js:
(ChartPaneBase.prototype._updateCommitLogViewer): No longer calls enqueueToRender on this since
CommitLogViewer does that on its own now.
(ChartPaneBase.prototype.render): Juse use this._openRepository instead of relying on CommitLogViewer
to remember which repository is current. This was the only use of currentRepository.

* public/v3/components/commit-log-viewer.js:
(CommitLogViewer):
(CommitLogViewer.prototype.currentRepository): Deleted.
(CommitLogViewer.prototype.view):
(CommitLogViewer.prototype._fetchCommitLogs): Modernized and extracted from view to make it lazy.
Call fetchForSingleRevision when precedingRevision is not specified or it's identical to lastRevision
since the generic JSON API no longer supports being called with the identical revisions.
(CommitLogViewer.prototype.render): Modernized &amp; simplified the code.
(CommitLogViewer.prototype._renderCommitList): Extracted from render to make it lazy.
(CommitLogViewer.htmlTemplate): Add ID on caption &amp; tbody so that they're more easily addressable.
(CommitLogViewer.cssTemplate):

* public/v3/models/commit-log.js:
(CommitLog.prototype.diff): No longer includes from/to revisions in the result. Also avoid adding
1 to a Subversion-like starting revision for creating the label. See above. But we still do this
for forming URLs due to the way tools like Trac work with Subversion revisions.
(CommitLog.fetchBetweenRevisions): Rewritten using DataModel.prototype.cachedFetch with FIXME for
what this function is supposed to be doing.
(CommitLog._cachedCommitLogs): Deleted.
(CommitLog.fetchForSingleRevision): Added.
(CommitLog._constructFromRawData): Added.

* public/v3/models/data-model.js:
(DataModelObject.cachedFetch): Don't parse query values as an integer. Just URL-escape them.

* public/v3/remote.js:
(BrowserRemoteAPI.prototype.sendHttpRequest): Fixed a typo.

* server-tests/api-commits-tests.js: Renamed from api-commits.js. Updated the existing tests to
use new query parameters and added more test cases.

* unit-tests/commit-log-tests.js: Updated the test cases now that CommitLog.prototype.diff no longer
includes from/to values. They're computed in ChartRevisionRange instead.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkWebsitesperfwebkitorgChangeLog">trunk/Websites/perf.webkit.org/ChangeLog</a></li>
<li><a href="#trunkWebsitesperfwebkitorgbrowsertestschartrevisionrangetestsjs">trunk/Websites/perf.webkit.org/browser-tests/chart-revision-range-tests.js</a></li>
<li><a href="#trunkWebsitesperfwebkitorgbrowsertestsindexhtml">trunk/Websites/perf.webkit.org/browser-tests/index.html</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicapicommitsphp">trunk/Websites/perf.webkit.org/public/api/commits.php</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicincludecommitlogfetcherphp">trunk/Websites/perf.webkit.org/public/include/commit-log-fetcher.php</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv3componentsbasejs">trunk/Websites/perf.webkit.org/public/v3/components/base.js</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv3componentschartpanebasejs">trunk/Websites/perf.webkit.org/public/v3/components/chart-pane-base.js</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv3componentscommitlogviewerjs">trunk/Websites/perf.webkit.org/public/v3/components/commit-log-viewer.js</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv3modelscommitlogjs">trunk/Websites/perf.webkit.org/public/v3/models/commit-log.js</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv3modelsdatamodeljs">trunk/Websites/perf.webkit.org/public/v3/models/data-model.js</a></li>
<li><a href="#trunkWebsitesperfwebkitorgpublicv3remotejs">trunk/Websites/perf.webkit.org/public/v3/remote.js</a></li>
<li><a href="#trunkWebsitesperfwebkitorgunittestscommitlogtestsjs">trunk/Websites/perf.webkit.org/unit-tests/commit-log-tests.js</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkWebsitesperfwebkitorgbrowsertestscommitlogviewertestsjs">trunk/Websites/perf.webkit.org/browser-tests/commit-log-viewer-tests.js</a></li>
<li><a href="#trunkWebsitesperfwebkitorgservertestsapicommitstestsjs">trunk/Websites/perf.webkit.org/server-tests/api-commits-tests.js</a></li>
</ul>

<h3>Removed Paths</h3>
<ul>
<li><a href="#trunkWebsitesperfwebkitorgservertestsapicommitsjs">trunk/Websites/perf.webkit.org/server-tests/api-commits.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 (214201 => 214202)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/ChangeLog        2017-03-21 01:13:50 UTC (rev 214201)
+++ trunk/Websites/perf.webkit.org/ChangeLog        2017-03-21 01:19:39 UTC (rev 214202)
</span><span class="lines">@@ -1,3 +1,80 @@
</span><ins>+2017-03-19  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
+
+        Charts page show an inconsistent list of revisions for Git and Subversion
+        https://bugs.webkit.org/show_bug.cgi?id=169888
+
+        Reviewed by Andreas Kling.
+
+        With Git, CommitLogViewer was showing the list of revisions including the starting hash,
+        which was the last data point's revision instead of all revisions after the last data point.
+
+        Fixed the bug by always specifying the revision at the last data point in both Subversion
+        and Git and then making /api/commits/&lt;repository&gt;/?from=X&amp;to=Y exclude the first revision.
+        For clarity, &quot;from&quot; and &quot;to&quot; query parameters have been renamed to &quot;precedingRevision&quot; and
+        &quot;lastRevision&quot; respectively.
+
+        We also no longer adds 1 to the starting revision of Subversion-like starting revisions. e.g.
+        when the last data point was at r1234, new data point is at r1250, the label is now &quot;r1234-r1250&quot;
+        instead of &quot;r1235-r1250&quot;.
+
+        * browser-tests/chart-revision-range-tests.js: Fixed the tests since revisionList no longer
+        specifies from/to revisions.
+        * browser-tests/commit-log-viewer-tests.js: Added. Added tests for CommitLogViewer.
+        * browser-tests/index.html: Include the new test. Also use a local copy of mocha.js/css.
+
+        * public/api/commits.php:
+        (main): Renamed &quot;from&quot; and &quot;to&quot; query parameters.
+
+        * public/include/commit-log-fetcher.php:
+        (CommitLogFetcher::fetch_between): Added a check that commit time should either be specified
+        in both rows or not specified in either. Also reject when before_first_revision is identical
+        or after last_revision instead of re-ordering them since it no longer makes sense to do so with
+        new query parameter names.
+
+        * public/v3/components/base.js:
+        (ComponentBase._addContentToElement): Use Array.isArray instead of instanceof. It's resilient
+        againt realm (global object) differences.
+
+        * public/v3/components/chart-pane-base.js:
+        (ChartPaneBase.prototype._updateCommitLogViewer): No longer calls enqueueToRender on this since
+        CommitLogViewer does that on its own now.
+        (ChartPaneBase.prototype.render): Juse use this._openRepository instead of relying on CommitLogViewer
+        to remember which repository is current. This was the only use of currentRepository.
+
+        * public/v3/components/commit-log-viewer.js:
+        (CommitLogViewer):
+        (CommitLogViewer.prototype.currentRepository): Deleted.
+        (CommitLogViewer.prototype.view):
+        (CommitLogViewer.prototype._fetchCommitLogs): Modernized and extracted from view to make it lazy.
+        Call fetchForSingleRevision when precedingRevision is not specified or it's identical to lastRevision
+        since the generic JSON API no longer supports being called with the identical revisions.
+        (CommitLogViewer.prototype.render): Modernized &amp; simplified the code.
+        (CommitLogViewer.prototype._renderCommitList): Extracted from render to make it lazy.
+        (CommitLogViewer.htmlTemplate): Add ID on caption &amp; tbody so that they're more easily addressable.
+        (CommitLogViewer.cssTemplate):
+
+        * public/v3/models/commit-log.js:
+        (CommitLog.prototype.diff): No longer includes from/to revisions in the result. Also avoid adding
+        1 to a Subversion-like starting revision for creating the label. See above. But we still do this
+        for forming URLs due to the way tools like Trac work with Subversion revisions.
+        (CommitLog.fetchBetweenRevisions): Rewritten using DataModel.prototype.cachedFetch with FIXME for
+        what this function is supposed to be doing.
+        (CommitLog._cachedCommitLogs): Deleted.
+        (CommitLog.fetchForSingleRevision): Added.
+        (CommitLog._constructFromRawData): Added.
+
+        * public/v3/models/data-model.js:
+        (DataModelObject.cachedFetch): Don't parse query values as an integer. Just URL-escape them.
+
+        * public/v3/remote.js:
+        (BrowserRemoteAPI.prototype.sendHttpRequest): Fixed a typo.
+
+        * server-tests/api-commits-tests.js: Renamed from api-commits.js. Updated the existing tests to
+        use new query parameters and added more test cases.
+
+        * unit-tests/commit-log-tests.js: Updated the test cases now that CommitLog.prototype.diff no longer
+        includes from/to values. They're computed in ChartRevisionRange instead.
+
</ins><span class="cx"> 2017-03-20  Ryosuke Niwa  &lt;rniwa@webkit.org&gt;
</span><span class="cx"> 
</span><span class="cx">         Fix os-build-fetcher.js and subprocess.js to make them work
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgbrowsertestschartrevisionrangetestsjs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/browser-tests/chart-revision-range-tests.js (214201 => 214202)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/browser-tests/chart-revision-range-tests.js        2017-03-21 01:13:50 UTC (rev 214201)
+++ trunk/Websites/perf.webkit.org/browser-tests/chart-revision-range-tests.js        2017-03-21 01:19:39 UTC (rev 214202)
</span><span class="lines">@@ -33,13 +33,9 @@
</span><span class="cx"> 
</span><span class="cx">                 expect(revisionList[0].repository.label()).to.be('SomeApp');
</span><span class="cx">                 expect(revisionList[0].label).to.be('r4006');
</span><del>-                expect(revisionList[0].from).to.be(null);
-                expect(revisionList[0].to).to.be('4006');
</del><span class="cx"> 
</span><span class="cx">                 expect(revisionList[1].repository.label()).to.be('macOS');
</span><span class="cx">                 expect(revisionList[1].label).to.be('15C50');
</span><del>-                expect(revisionList[1].from).to.be(null);
-                expect(revisionList[1].to).to.be('15C50');
</del><span class="cx">             })
</span><span class="cx">         });
</span><span class="cx">     });
</span><span class="lines">@@ -96,14 +92,10 @@
</span><span class="cx">                 expect(revisionList.length).to.be(2);
</span><span class="cx"> 
</span><span class="cx">                 expect(revisionList[0].repository.label()).to.be('SomeApp');
</span><del>-                expect(revisionList[0].label).to.be('r4005-r4006');
-                expect(revisionList[0].from).to.be('4005');
-                expect(revisionList[0].to).to.be('4006');
</del><ins>+                expect(revisionList[0].label).to.be('r4004-r4006');
</ins><span class="cx"> 
</span><span class="cx">                 expect(revisionList[1].repository.label()).to.be('macOS');
</span><span class="cx">                 expect(revisionList[1].label).to.be('15C50');
</span><del>-                expect(revisionList[1].from).to.be(null);
-                expect(revisionList[1].to).to.be('15C50');
</del><span class="cx"> 
</span><span class="cx">                 chart.setIndicator(1004, true); // Across macOS change.
</span><span class="cx"> 
</span><span class="lines">@@ -111,14 +103,10 @@
</span><span class="cx">                 expect(revisionList.length).to.be(2);
</span><span class="cx"> 
</span><span class="cx">                 expect(revisionList[0].repository.label()).to.be('SomeApp');
</span><del>-                expect(revisionList[0].label).to.be('r4004-r4004');
-                expect(revisionList[0].from).to.be('4004');
-                expect(revisionList[0].to).to.be('4004');
</del><ins>+                expect(revisionList[0].label).to.be('r4003-r4004');
</ins><span class="cx"> 
</span><span class="cx">                 expect(revisionList[1].repository.label()).to.be('macOS');
</span><span class="cx">                 expect(revisionList[1].label).to.be('15B42 - 15C50');
</span><del>-                expect(revisionList[1].from).to.be('15B42');
-                expect(revisionList[1].to).to.be('15C50');
</del><span class="cx">             });
</span><span class="cx">         });
</span><span class="cx"> 
</span><span class="lines">@@ -150,14 +138,10 @@
</span><span class="cx">                 expect(revisionList.length).to.be(2);
</span><span class="cx"> 
</span><span class="cx">                 expect(revisionList[0].repository.label()).to.be('SomeApp');
</span><del>-                expect(revisionList[0].label).to.be('r4003-r4004'); // 4002 and 4005 are outliers and skipped.
-                expect(revisionList[0].from).to.be('4003');
-                expect(revisionList[0].to).to.be('4004');
</del><ins>+                expect(revisionList[0].label).to.be('r4002-r4004'); // 4001 and 4005 are outliers and skipped.
</ins><span class="cx"> 
</span><span class="cx">                 expect(revisionList[1].repository.label()).to.be('macOS');
</span><span class="cx">                 expect(revisionList[1].label).to.be('15B42 - 15C50');
</span><del>-                expect(revisionList[1].from).to.be('15B42');
-                expect(revisionList[1].to).to.be('15C50');
</del><span class="cx">             });
</span><span class="cx">         });
</span><span class="cx">     });
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgbrowsertestscommitlogviewertestsjs"></a>
<div class="addfile"><h4>Added: trunk/Websites/perf.webkit.org/browser-tests/commit-log-viewer-tests.js (0 => 214202)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/browser-tests/commit-log-viewer-tests.js                                (rev 0)
+++ trunk/Websites/perf.webkit.org/browser-tests/commit-log-viewer-tests.js        2017-03-21 01:19:39 UTC (rev 214202)
</span><span class="lines">@@ -0,0 +1,123 @@
</span><ins>+describe('CommitLogViewer', () =&gt; {
+
+    function importCommitLogViewer(context)
+    {
+        const scripts = [
+            '../shared/statistics.js',
+            'instrumentation.js',
+            'lazily-evaluated-function.js',
+            'models/data-model.js',
+            'models/repository.js',
+            'models/commit-set.js',
+            'models/commit-log.js',
+            'components/base.js',
+            'components/spinner-icon.js',
+            'components/commit-log-viewer.js'];
+        return context.importScripts(scripts, 'ComponentBase', 'CommitLogViewer', 'Repository', 'CommitLog', 'RemoteAPI').then(() =&gt; {
+            return context.symbols.CommitLogViewer;
+        });
+    }
+
+    const webkitCommit210949 = {
+        &quot;id&quot;: &quot;185334&quot;,
+        &quot;revision&quot;: &quot;210949&quot;,
+        &quot;previousCommit&quot;: null,
+        &quot;time&quot;: +new Date(&quot;2017-01-20T03:23:50.645Z&quot;),
+        &quot;authorName&quot;: &quot;Chris Dumez&quot;,
+        &quot;authorEmail&quot;: &quot;cdumez@apple.com&quot;,
+        &quot;message&quot;: &quot;some message&quot;,
+    };
+
+    const webkitCommit210950 = {
+        &quot;id&quot;: &quot;185338&quot;,
+        &quot;revision&quot;: &quot;210950&quot;,
+        &quot;previousCommit&quot;: null,
+        &quot;time&quot;: +new Date(&quot;2017-01-20T03:49:37.887Z&quot;),
+        &quot;authorName&quot;: &quot;Commit Queue&quot;,
+        &quot;authorEmail&quot;: &quot;commit-queue@webkit.org&quot;,
+        &quot;message&quot;: &quot;another message&quot;,
+    };
+
+    it('should initially be empty with no spinner', () =&gt; {
+        const context = new BrowsingContext();
+        return importCommitLogViewer(context).then((CommitLogViewer) =&gt; {
+            const viewer = new CommitLogViewer;
+            context.document.body.appendChild(viewer.element());
+            viewer.enqueueToRender();
+            return waitForComponentsToRender(context).then(() =&gt; {
+                expect(viewer.content('spinner-container').offsetHeight).to.be(0);
+                expect(viewer.content('commits-list').matches(':empty')).to.be(true);
+                expect(viewer.content('repository-name').matches(':empty')).to.be(true);
+            });
+        });
+    });
+
+    it('should show the repository name and the spinner once it started fetching the list of commits ', () =&gt; {
+        const context = new BrowsingContext();
+        return importCommitLogViewer(context).then((CommitLogViewer) =&gt; {
+            const Repository = context.symbols.Repository;
+            const SpinnerIcon = context.symbols.SpinnerIcon;
+            const ComponentBase = context.symbols.ComponentBase;
+            const RemoteAPI = context.symbols.RemoteAPI;
+            const viewer = new CommitLogViewer;
+            const webkit = new Repository(1, {name: 'WebKit'});
+            context.document.body.appendChild(viewer.element());
+            viewer.enqueueToRender();
+            return waitForComponentsToRender(context).then(() =&gt; {
+                expect(viewer.content('spinner-container').offsetHeight).to.be(0);
+                expect(viewer.content('commits-list').matches(':empty')).to.be(true);
+                expect(viewer.content('repository-name').matches(':empty')).to.be(true);
+                expect(RemoteAPI.requests.length, 0);
+                viewer.view(webkit, '210948', '210950');
+                expect(RemoteAPI.requests.length, 1);
+                return waitForComponentsToRender(context);
+            }).then(() =&gt; {
+                expect(viewer.content('spinner-container').offsetHeight).to.not.be(0);
+                expect(viewer.content('commits-list').matches(':empty')).to.be(true);
+                expect(viewer.content('repository-name').matches(':empty')).to.be(false);
+                expect(viewer.content('repository-name').textContent).to.contain('WebKit');
+            });
+        });
+    });
+
+    it('should show the repository name, the list of commits, and hide the spinner once the list of commits are fetched', () =&gt; {
+        const context = new BrowsingContext();
+        return importCommitLogViewer(context).then((CommitLogViewer) =&gt; {
+            const Repository = context.symbols.Repository;
+            const SpinnerIcon = context.symbols.SpinnerIcon;
+            const ComponentBase = context.symbols.ComponentBase;
+            const requests = context.symbols.RemoteAPI.requests;
+            const viewer = new CommitLogViewer;
+            const webkit = new Repository(1, {name: 'WebKit'});
+            context.document.body.appendChild(viewer.element());
+            viewer.enqueueToRender();
+            return waitForComponentsToRender(context).then(() =&gt; {
+                viewer.view(webkit, '210948', '210950');
+                return waitForComponentsToRender(context);
+            }).then(() =&gt; {
+                expect(viewer.content('spinner-container').offsetHeight).to.not.be(0);
+                expect(viewer.content('commits-list').matches(':empty')).to.be(true);
+                expect(viewer.content('repository-name').matches(':empty')).to.be(false);
+                expect(viewer.content('repository-name').textContent).to.contain('WebKit');
+                expect(requests.length).to.be(1);
+                expect(requests[0].url).to.be('/api/commits/1/?precedingRevision=210948&amp;lastRevision=210950');
+                requests[0].resolve({
+                    &quot;status&quot;: &quot;OK&quot;,
+                    &quot;commits&quot;: [webkitCommit210949, webkitCommit210950],
+                });
+                return waitForComponentsToRender(context);
+            }).then(() =&gt; {
+                expect(viewer.content('spinner-container').offsetHeight).to.be(0);
+                expect(viewer.content('commits-list').matches(':empty')).to.be(false);
+                expect(viewer.content('commits-list').textContent).to.contain('r210949');
+                expect(viewer.content('commits-list').textContent).to.contain('Chris Dumez');
+                expect(viewer.content('commits-list').textContent).to.contain('r210950');
+                expect(viewer.content('commits-list').textContent).to.contain('Commit Queue');
+                expect(viewer.content('repository-name').matches(':empty')).to.be(false);
+                expect(viewer.content('repository-name').textContent).to.contain('WebKit');
+                expect(viewer.content('commits-list').querySelector('a')).to.be(null);
+            });
+        });
+    });
+
+});
</ins></span></pre></div>
<a id="trunkWebsitesperfwebkitorgbrowsertestsindexhtml"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/browser-tests/index.html (214201 => 214202)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/browser-tests/index.html        2017-03-21 01:13:50 UTC (rev 214201)
+++ trunk/Websites/perf.webkit.org/browser-tests/index.html        2017-03-21 01:19:39 UTC (rev 214202)
</span><span class="lines">@@ -1,9 +1,10 @@
</span><span class="cx"> &lt;!DOCTYPE html&gt;
</span><span class="cx"> &lt;html&gt;
</span><span class="cx"> &lt;head&gt;
</span><ins>+&lt;meta charset=&quot;utf-8&quot;&gt;
</ins><span class="cx"> &lt;title&gt;In-Browser Tests for Performance Dashboard&lt;/title&gt;
</span><del>-&lt;link rel=&quot;stylesheet&quot; href=&quot;https://cdn.rawgit.com/mochajs/mocha/2.2.5/mocha.css&quot;&gt;
-&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/mocha/2.2.5/mocha.js&quot;&gt;&lt;/script&gt;
</del><ins>+&lt;link rel=&quot;stylesheet&quot; href=&quot;../node_modules/mocha/mocha.css&quot;&gt;
+&lt;script src=&quot;../node_modules/mocha/mocha.js&quot;&gt;&lt;/script&gt;
</ins><span class="cx"> &lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/expect.js/0.2.0/expect.min.js&quot;&gt;&lt;/script&gt;
</span><span class="cx"> &lt;script&gt;
</span><span class="cx"> 
</span><span class="lines">@@ -21,6 +22,7 @@
</span><span class="cx"> &lt;script src=&quot;interactive-time-series-chart-tests.js&quot;&gt;&lt;/script&gt;
</span><span class="cx"> &lt;script src=&quot;chart-status-evaluator-tests.js&quot;&gt;&lt;/script&gt;
</span><span class="cx"> &lt;script src=&quot;chart-revision-range-tests.js&quot;&gt;&lt;/script&gt;
</span><ins>+&lt;script src=&quot;commit-log-viewer-tests.js&quot;&gt;&lt;/script&gt;
</ins><span class="cx"> &lt;script&gt;
</span><span class="cx"> 
</span><span class="cx"> afterEach(() =&gt; {
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicapicommitsphp"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/api/commits.php (214201 => 214202)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/api/commits.php        2017-03-21 01:13:50 UTC (rev 214201)
+++ trunk/Websites/perf.webkit.org/public/api/commits.php        2017-03-21 01:19:39 UTC (rev 214202)
</span><span class="lines">@@ -24,9 +24,9 @@
</span><span class="cx">     $commits = array();
</span><span class="cx">     if (!$filter) {
</span><span class="cx">         $keyword = array_get($_GET, 'keyword'); // V2 UI compatibility.
</span><del>-        $from = array_get($_GET, 'from');
-        $to = array_get($_GET, 'to');
-        $commits = $fetcher-&gt;fetch_between($repository_id, $from, $to, $keyword);
</del><ins>+        $preceding_revision = array_get($_GET, 'precedingRevision');
+        $last_revision = array_get($_GET, 'lastRevision');
+        $commits = $fetcher-&gt;fetch_between($repository_id, $preceding_revision, $last_revision, $keyword);
</ins><span class="cx">     } else if ($filter == 'oldest') {
</span><span class="cx">         $commits = $fetcher-&gt;fetch_oldest($repository_id);
</span><span class="cx">     } else if ($filter == 'latest') {
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicincludecommitlogfetcherphp"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/include/commit-log-fetcher.php (214201 => 214202)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/include/commit-log-fetcher.php        2017-03-21 01:13:50 UTC (rev 214201)
+++ trunk/Websites/perf.webkit.org/public/include/commit-log-fetcher.php        2017-03-21 01:19:39 UTC (rev 214202)
</span><span class="lines">@@ -33,7 +33,7 @@
</span><span class="cx">         return $repository_row['repository_id'];
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    function fetch_between($repository_id, $first, $second, $keyword = NULL)
</del><ins>+    function fetch_between($repository_id, $before_first_revision, $last_revision, $keyword = NULL)
</ins><span class="cx">     {
</span><span class="cx">         $statements = 'SELECT commit_id as &quot;id&quot;,
</span><span class="cx">             commit_revision as &quot;revision&quot;,
</span><span class="lines">@@ -46,22 +46,29 @@
</span><span class="cx">             WHERE commit_repository = $1 AND commit_reported = true';
</span><span class="cx">         $values = array($repository_id);
</span><span class="cx"> 
</span><del>-        if ($first &amp;&amp; $second) {
-            $first_commit = $this-&gt;commit_for_revision($repository_id, $first);
-            $second_commit = $this-&gt;commit_for_revision($repository_id, $second);
-            $first = $first_commit['commit_time'];
-            $second = $second_commit['commit_time'];
-            $column_name = 'commit_time';
-            if (!$first || !$second) {
-                $first = $first_commit['commit_order'];
-                $second = $second_commit['commit_order'];
</del><ins>+        if ($before_first_revision &amp;&amp; $last_revision) {
+            $preceding_commit = $this-&gt;commit_for_revision($repository_id, $before_first_revision);
+            $last_commit = $this-&gt;commit_for_revision($repository_id, $last_revision);
+
+            $preceding = $preceding_commit['commit_time'];
+            $last = $last_commit['commit_time'];
+            if (!$preceding != !$last)
+                exit_with_error('InconsistentCommits');
+            if ($preceding)
+                $column_name = 'commit_time';
+            else {
+                $preceding = $preceding_commit['commit_order'];
+                $last = $last_commit['commit_order'];
</ins><span class="cx">                 $column_name = 'commit_order';
</span><ins>+                if (!$preceding || !$last)
+                    return array();
</ins><span class="cx">             }
</span><ins>+            if ($preceding &gt;= $last)
+                exit_with_error('InvalidCommitRange');
</ins><span class="cx"> 
</span><del>-            $in_order = $first &lt; $second;
-            array_push($values, $in_order ? $first : $second);
-            $statements .= ' AND ' . $column_name . ' &gt;= $' . count($values);
-            array_push($values, $in_order ? $second : $first);
</del><ins>+            array_push($values, $preceding);
+            $statements .= ' AND ' . $column_name . ' &gt; $' . count($values);
+            array_push($values, $last);
</ins><span class="cx">             $statements .= ' AND ' . $column_name . ' &lt;= $' . count($values);
</span><span class="cx">         }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv3componentsbasejs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v3/components/base.js (214201 => 214202)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v3/components/base.js        2017-03-21 01:13:50 UTC (rev 214201)
+++ trunk/Websites/perf.webkit.org/public/v3/components/base.js        2017-03-21 01:19:39 UTC (rev 214202)
</span><span class="lines">@@ -238,7 +238,7 @@
</span><span class="cx"> 
</span><span class="cx">     static _addContentToElement(element, content)
</span><span class="cx">     {
</span><del>-        if (content instanceof Array) {
</del><ins>+        if (Array.isArray(content)) {
</ins><span class="cx">             for (var nestedChild of content)
</span><span class="cx">                 this._addContentToElement(element, nestedChild);
</span><span class="cx">         } else if (content instanceof Node)
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv3componentschartpanebasejs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v3/components/chart-pane-base.js (214201 => 214202)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v3/components/chart-pane-base.js        2017-03-21 01:13:50 UTC (rev 214201)
+++ trunk/Websites/perf.webkit.org/public/v3/components/chart-pane-base.js        2017-03-21 01:19:39 UTC (rev 214202)
</span><span class="lines">@@ -163,9 +163,8 @@
</span><span class="cx">     _updateCommitLogViewer()
</span><span class="cx">     {
</span><span class="cx">         const range = this._revisionRange.rangeForRepository(this._openRepository);
</span><del>-        const updateRendering = () =&gt; { this.enqueueToRender(); };
-        this._commitLogViewer.view(this._openRepository, range.from, range.to).then(updateRendering);
-        updateRendering();
</del><ins>+        this._commitLogViewer.view(this._openRepository, range.from, range.to);
+        this.enqueueToRender();
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     _openAnalysisTask(annotation)
</span><span class="lines">@@ -257,7 +256,7 @@
</span><span class="cx">             this._mainChartStatus.enqueueToRender();
</span><span class="cx"> 
</span><span class="cx">         var body = this.content().querySelector('.chart-pane-body');
</span><del>-        if (this._commitLogViewer &amp;&amp; this._commitLogViewer.currentRepository()) {
</del><ins>+        if (this._openRepository) {
</ins><span class="cx">             body.classList.add('has-second-sidebar');
</span><span class="cx">             this._commitLogViewer.enqueueToRender();
</span><span class="cx">         } else
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv3componentscommitlogviewerjs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v3/components/commit-log-viewer.js (214201 => 214202)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v3/components/commit-log-viewer.js        2017-03-21 01:13:50 UTC (rev 214201)
+++ trunk/Websites/perf.webkit.org/public/v3/components/commit-log-viewer.js        2017-03-21 01:19:39 UTC (rev 214202)
</span><span class="lines">@@ -4,81 +4,77 @@
</span><span class="cx">     constructor()
</span><span class="cx">     {
</span><span class="cx">         super('commit-log-viewer');
</span><ins>+        this._lazilyFetchCommitLogs = new LazilyEvaluatedFunction(this._fetchCommitLogs.bind(this));
</ins><span class="cx">         this._repository = null;
</span><span class="cx">         this._fetchingPromise = null;
</span><span class="cx">         this._commits = null;
</span><del>-        this._from = null;
-        this._to = null;
</del><ins>+        this._renderCommitListLazily = new LazilyEvaluatedFunction(this._renderCommitList.bind(this));
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    currentRepository() { return this._repository; }
</del><ins>+    view(repository, precedingRevision, lastRevision)
+    {
+        this._lazilyFetchCommitLogs.evaluate(repository, precedingRevision, lastRevision);
+    }
</ins><span class="cx"> 
</span><del>-    view(repository, from, to)
</del><ins>+    _fetchCommitLogs(repository, precedingRevision, lastRevision)
</ins><span class="cx">     {
</span><del>-        if (this._repository == repository &amp;&amp; this._from == from &amp;&amp; this._to == to)
-            return Promise.resolve(null);
-
</del><ins>+        this._fetchingPromise = null;
</ins><span class="cx">         this._commits = null;
</span><del>-        this._repository = repository;
-        this._from = from;
-        this._to = to;
</del><span class="cx"> 
</span><del>-        if (!repository) {
-            this._fetchingPromise = null;
</del><ins>+        if (!repository || !lastRevision) {
</ins><span class="cx">             this._repository = null;
</span><del>-            return Promise.resolve(null);
-        }
-
-        if (!to) {
-            this._fetchingPromise = null;
</del><span class="cx">             this.enqueueToRender();
</span><del>-            return Promise.resolve(null);
</del><ins>+            return;
</ins><span class="cx">         }
</span><span class="cx"> 
</span><del>-        var promise = CommitLog.fetchBetweenRevisions(repository, from || to, to);
</del><ins>+        let promise;
+        if (!precedingRevision || precedingRevision == lastRevision)
+            promise = CommitLog.fetchForSingleRevision(repository, lastRevision);
+        else
+            promise = CommitLog.fetchBetweenRevisions(repository, precedingRevision, lastRevision);
</ins><span class="cx"> 
</span><ins>+        this._repository = repository;
</ins><span class="cx">         this._fetchingPromise = promise;
</span><span class="cx"> 
</span><del>-        var self = this;
-        var spinnerTimer = setTimeout(function () {
-            self.enqueueToRender();
-        }, 300);
-
-        this._fetchingPromise.then(function (commits) {
-            clearTimeout(spinnerTimer);
-            if (self._fetchingPromise != promise)
</del><ins>+        this._fetchingPromise.then((commits) =&gt; {
+            if (this._fetchingPromise != promise)
</ins><span class="cx">                 return;
</span><del>-            self._fetchingPromise = null;
-            self._commits = commits;
-        }, function (error) {
-            if (self._fetchingPromise != promise)
</del><ins>+            this._fetchingPromise = null;
+            this._commits = commits;
+            this.enqueueToRender();
+        }, (error) =&gt; {
+            if (this._fetchingPromise != promise)
</ins><span class="cx">                 return;
</span><del>-            self._fetchingPromise = null;
-            self._commits = null;
</del><ins>+            this._fetchingPromise = null;
+            this._commits = null;
+            this.enqueueToRender();
</ins><span class="cx">         });
</span><span class="cx"> 
</span><del>-        return this._fetchingPromise;
</del><ins>+        this.enqueueToRender();
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     render()
</span><span class="cx">     {
</span><del>-        var caption = '';
-        if (this._repository &amp;&amp; (this._commits || this._fetchingPromise))
-            caption = this._repository.name();
-        this.content().querySelector('caption').textContent = caption;
</del><ins>+        this.part('spinner').enqueueToRender();
</ins><span class="cx"> 
</span><del>-        var element = ComponentBase.createElement;
-        var link = ComponentBase.createLink;
</del><ins>+        const shouldShowRepositoryName = this._repository &amp;&amp; (this._commits || this._fetchingPromise);
+        this.content('repository-name').textContent = shouldShowRepositoryName ? this._repository.name() : '';
+        this.content('spinner-container').style.display = this._fetchingPromise ? null : 'none';
+        this._renderCommitListLazily.evaluate(this._commits);
+    }
</ins><span class="cx"> 
</span><del>-        this.renderReplace(this.content().querySelector('tbody'), (this._commits || []).map(function (commit) {
-            var label = commit.label();
-            var url = commit.url();
</del><ins>+    _renderCommitList(commits)
+    {
+        const element = ComponentBase.createElement;
+        const link = ComponentBase.createLink;
+
+        this.renderReplace(this.content('commits-list'), (commits || []).map((commit) =&gt; {
+            const label = commit.label();
+            const url = commit.url();
</ins><span class="cx">             return element('tr', [
</span><span class="cx">                 element('th', [element('h4', {class: 'revision'}, url ? link(label, commit.title(), url) : label), commit.author() || '']),
</span><span class="cx">                 element('td', commit.message() ? commit.message().substring(0, 80) : '')]);
</span><span class="cx">         }));
</span><del>-
-        this.content().querySelector('.commits-viewer-spinner').style.display = this._fetchingPromise ? null : 'none';
</del><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     static htmlTemplate()
</span><span class="lines">@@ -85,11 +81,10 @@
</span><span class="cx">     {
</span><span class="cx">         return `
</span><span class="cx">             &lt;div class=&quot;commits-viewer-container&quot;&gt;
</span><del>-                &lt;div class=&quot;commits-viewer-spinner&quot;&gt;&lt;spinner-icon&gt;&lt;/spinner-icon&gt;&lt;/div&gt;
-                &lt;table class=&quot;commits-viewer-table&quot;&gt;
-                    &lt;caption&gt;&lt;/caption&gt;
-                    &lt;tbody&gt;
-                    &lt;/tbody&gt;
</del><ins>+                &lt;div id=&quot;spinner-container&quot;&gt;&lt;spinner-icon id=&quot;spinner&quot;&gt;&lt;/spinner-icon&gt;&lt;/div&gt;
+                &lt;table id=&quot;commits-viewer-table&quot;&gt;
+                    &lt;caption id=&quot;repository-name&quot;&gt;&lt;/caption&gt;
+                    &lt;tbody id=&quot;commits-list&quot;&gt;&lt;/tbody&gt;
</ins><span class="cx">                 &lt;/table&gt;
</span><span class="cx">             &lt;/div&gt;
</span><span class="cx"> `;
</span><span class="lines">@@ -104,11 +99,13 @@
</span><span class="cx">                 overflow-y: scroll;
</span><span class="cx">             }
</span><span class="cx">             
</span><del>-            .commits-viewer-table {
</del><ins>+            #commits-viewer-table {
</ins><span class="cx">                 width: 100%;
</span><ins>+                border-collapse: collapse;
+                border-bottom: solid 1px #ccc;
</ins><span class="cx">             }
</span><span class="cx"> 
</span><del>-            .commits-viewer-table caption {
</del><ins>+            caption {
</ins><span class="cx">                 font-weight: inherit;
</span><span class="cx">                 font-size: 1rem;
</span><span class="cx">                 text-align: center;
</span><span class="lines">@@ -115,12 +112,7 @@
</span><span class="cx">                 padding: 0.2rem;
</span><span class="cx">             }
</span><span class="cx"> 
</span><del>-            .commits-viewer-table {
-                border-collapse: collapse;
-                border-bottom: solid 1px #ccc;
-            }
-
-            .commits-viewer-table .revision {
</del><ins>+            .revision {
</ins><span class="cx">                 white-space: nowrap;
</span><span class="cx">                 font-weight: normal;
</span><span class="cx">                 margin: 0;
</span><span class="lines">@@ -127,8 +119,7 @@
</span><span class="cx">                 padding: 0;
</span><span class="cx">             }
</span><span class="cx"> 
</span><del>-            .commits-viewer-table td,
-            .commits-viewer-table th {
</del><ins>+            td, th {
</ins><span class="cx">                 word-break: break-word;
</span><span class="cx">                 border-top: solid 1px #ccc;
</span><span class="cx">                 padding: 0.2rem;
</span><span class="lines">@@ -137,7 +128,7 @@
</span><span class="cx">                 font-weight: normal;
</span><span class="cx">             }
</span><span class="cx"> 
</span><del>-            .commits-viewer-spinner {
</del><ins>+            #spinner-container {
</ins><span class="cx">                 margin-top: 2rem;
</span><span class="cx">                 text-align: center;
</span><span class="cx">             }
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv3modelscommitlogjs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v3/models/commit-log.js (214201 => 214202)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v3/models/commit-log.js        2017-03-21 01:13:50 UTC (rev 214201)
+++ trunk/Websites/perf.webkit.org/public/v3/models/commit-log.js        2017-03-21 01:19:39 UTC (rev 214202)
</span><span class="lines">@@ -49,70 +49,46 @@
</span><span class="cx">         if (this == previousCommit)
</span><span class="cx">             previousCommit = null;
</span><span class="cx"> 
</span><del>-        var repository = this._repository;
</del><ins>+        const repository = this._repository;
</ins><span class="cx">         if (!previousCommit)
</span><del>-            return {from: null, to: this.revision(), repository: repository, label: this.label(), url: this.url()};
</del><ins>+            return {repository: repository, label: this.label(), url: this.url()};
</ins><span class="cx"> 
</span><del>-        var to = this.revision();
-        var from = previousCommit.revision();
-        var label = null;
-        if (parseInt(to) == to) { // e.g. r12345.
-            from = (parseInt(from) + 1).toString();
</del><ins>+        const to = this.revision();
+        const from = previousCommit.revision();
+        let fromRevisionForURL = from;
+        let label = null;
+        if (parseInt(from) == from) { // e.g. r12345.
+            fromRevisionForURL = (parseInt(from) + 1).toString;
</ins><span class="cx">             label = `r${from}-r${this.revision()}`;
</span><del>-        } else if (to.length == 40) { // e.g. git hash
</del><ins>+        } else if (to.length == 40) // e.g. git hash
</ins><span class="cx">             label = `${from.substring(0, 8)}..${to.substring(0, 8)}`;
</span><del>-        } else
</del><ins>+        else
</ins><span class="cx">             label = `${from} - ${to}`;
</span><span class="cx"> 
</span><del>-        return {from: from, to: to, repository: repository, label: label, url: repository.urlForRevisionRange(from, to)};
</del><ins>+        return {repository: repository, label: label, url: repository.urlForRevisionRange(from, to)};
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    static fetchBetweenRevisions(repository, from, to)
</del><ins>+    static fetchBetweenRevisions(repository, precedingRevision, lastRevision)
</ins><span class="cx">     {
</span><del>-        var params = [];
-        if (from &amp;&amp; to) {
-            params.push(['from', from]);
-            params.push(['to', to]);
-        }
-
-        var url = '../api/commits/' + repository.id() + '/?' + params.map(function (keyValue) {
-            return encodeURIComponent(keyValue[0]) + '=' + encodeURIComponent(keyValue[1]);
-        }).join('&amp;');
-
-
-        var cachedLogs = this._cachedCommitLogs(repository, from, to);
-        if (cachedLogs)
-            return new Promise(function (resolve) { resolve(cachedLogs); });
-
-        var self = this;
-        return RemoteAPI.getJSONWithStatus(url).then(function (data) {
-            var commits = data['commits'].map(function (rawData) {
-                rawData.repository = repository;
-                return CommitLog.ensureSingleton(rawData.id, rawData);
-            });
-            self._cacheCommitLogs(repository, from, to, commits);
-            return commits;
-        });
</del><ins>+        // FIXME: The cache should be smarter about fetching a range within an already fetched range, etc...
+        // FIXME: We should evict some entires from the cache in cachedFetch.
+        return this.cachedFetch(`/api/commits/${repository.id()}/`, {precedingRevision, lastRevision})
+            .then((data) =&gt; this._constructFromRawData(repository, data));
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    static _cachedCommitLogs(repository, from, to)
</del><ins>+    static fetchForSingleRevision(repository, revision)
</ins><span class="cx">     {
</span><del>-        if (!this._caches)
-            return null;
-        var cache = this._caches[repository.id()];
-        if (!cache)
-            return null;
-        // FIXME: Make each commit know of its parent, then we can do a better caching. 
-        return cache[from + '|' + to];
</del><ins>+        return this.cachedFetch(`/api/commits/${repository.id()}/${revision}`).then((data) =&gt; {
+            return this._constructFromRawData(repository, data);
+        });
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    static _cacheCommitLogs(repository, from, to, logs)
</del><ins>+    static _constructFromRawData(repository, data)
</ins><span class="cx">     {
</span><del>-        if (!this._caches)
-            this._caches = {};
-        if (!this._caches[repository.id()])
-            this._caches[repository.id()] = {};
-        this._caches[repository.id()][from + '|' + to] = logs;
</del><ins>+        return data['commits'].map((rawData) =&gt; {
+            rawData.repository = repository;
+            return CommitLog.ensureSingleton(rawData.id, rawData);
+        });
</ins><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv3modelsdatamodeljs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v3/models/data-model.js (214201 => 214202)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v3/models/data-model.js        2017-03-21 01:13:50 UTC (rev 214201)
+++ trunk/Websites/perf.webkit.org/public/v3/models/data-model.js        2017-03-21 01:19:39 UTC (rev 214202)
</span><span class="lines">@@ -62,10 +62,10 @@
</span><span class="cx"> 
</span><span class="cx">     static cachedFetch(path, params, noCache)
</span><span class="cx">     {
</span><del>-        var query = [];
</del><ins>+        const query = [];
</ins><span class="cx">         if (params) {
</span><del>-            for (var key in params)
-                query.push(key + '=' + parseInt(params[key]));
</del><ins>+            for (let key in params)
+                query.push(key + '=' + escape(params[key]));
</ins><span class="cx">         }
</span><span class="cx">         if (query.length)
</span><span class="cx">             path += '?' + query.join('&amp;');
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgpublicv3remotejs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/public/v3/remote.js (214201 => 214202)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/public/v3/remote.js        2017-03-21 01:13:50 UTC (rev 214201)
+++ trunk/Websites/perf.webkit.org/public/v3/remote.js        2017-03-21 01:19:39 UTC (rev 214202)
</span><span class="lines">@@ -12,7 +12,7 @@
</span><span class="cx">             function onload() {
</span><span class="cx">                 Instrumentation.endMeasuringTime('Remote', 'sendHttpRequest');
</span><span class="cx">                 if (xhr.status != 200)
</span><del>-                    return resject(xhr.status);
</del><ins>+                    return reject(xhr.status);
</ins><span class="cx">                 resolve({statusCode: xhr.status, responseText: xhr.responseText});
</span><span class="cx">             };
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkWebsitesperfwebkitorgservertestsapicommitstestsjsfromrev214201trunkWebsitesperfwebkitorgservertestsapicommitsjs"></a>
<div class="copfile"><h4>Copied: trunk/Websites/perf.webkit.org/server-tests/api-commits-tests.js (from rev 214201, trunk/Websites/perf.webkit.org/server-tests/api-commits.js) (0 => 214202)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/server-tests/api-commits-tests.js                                (rev 0)
+++ trunk/Websites/perf.webkit.org/server-tests/api-commits-tests.js        2017-03-21 01:19:39 UTC (rev 214202)
</span><span class="lines">@@ -0,0 +1,572 @@
</span><ins>+'use strict';
+
+const assert = require('assert');
+
+const TestServer = require('./resources/test-server.js');
+const addSlaveForReport = require('./resources/common-operations.js').addSlaveForReport;
+const prepareServerTest = require('./resources/common-operations.js').prepareServerTest;
+
+describe(&quot;/api/commits/&quot;, function () {
+    prepareServerTest(this);
+
+    const subversionCommits = {
+        &quot;slaveName&quot;: &quot;someSlave&quot;,
+        &quot;slavePassword&quot;: &quot;somePassword&quot;,
+        &quot;commits&quot;: [
+            {
+                &quot;repository&quot;: &quot;WebKit&quot;,
+                &quot;revision&quot;: &quot;210948&quot;,
+                &quot;time&quot;: &quot;2017-01-20T02:52:34.577Z&quot;,
+                &quot;author&quot;: {&quot;name&quot;: &quot;Zalan Bujtas&quot;, &quot;account&quot;: &quot;zalan@apple.com&quot;},
+                &quot;message&quot;: &quot;a message&quot;,
+            },
+            {
+                &quot;repository&quot;: &quot;WebKit&quot;,
+                &quot;revision&quot;: &quot;210949&quot;,
+                &quot;time&quot;: &quot;2017-01-20T03:23:50.645Z&quot;,
+                &quot;author&quot;: {&quot;name&quot;: &quot;Chris Dumez&quot;, &quot;account&quot;: &quot;cdumez@apple.com&quot;},
+                &quot;message&quot;: &quot;some message&quot;,
+            },
+            {
+                &quot;repository&quot;: &quot;WebKit&quot;,
+                &quot;previousCommit&quot;: &quot;210949&quot;,
+                &quot;revision&quot;: &quot;210950&quot;,
+                &quot;time&quot;: &quot;2017-01-20T03:49:37.887Z&quot;,
+                &quot;author&quot;: {&quot;name&quot;: &quot;Commit Queue&quot;, &quot;account&quot;: &quot;commit-queue@webkit.org&quot;},
+                &quot;message&quot;: &quot;another message&quot;,
+            }
+        ]
+    }
+
+    const systemVersionCommits = {
+        &quot;slaveName&quot;: &quot;someSlave&quot;,
+        &quot;slavePassword&quot;: &quot;somePassword&quot;,
+        &quot;commits&quot;: [
+            {
+                &quot;repository&quot;: &quot;OSX&quot;,
+                &quot;revision&quot;: &quot;16D32&quot;,
+                &quot;order&quot;: 6
+            },
+            {
+                &quot;repository&quot;: &quot;OSX&quot;,
+                &quot;revision&quot;: &quot;16C68&quot;,
+                &quot;order&quot;: 5
+            },
+            {
+                &quot;repository&quot;: &quot;OSX&quot;,
+                &quot;revision&quot;: &quot;16C67&quot;,
+                &quot;order&quot;: 4
+            },
+            {
+                &quot;repository&quot;: &quot;OSX&quot;,
+                &quot;revision&quot;: &quot;16B2657&quot;,
+                &quot;order&quot;: 3
+            },
+            {
+                &quot;repository&quot;: &quot;OSX&quot;,
+                &quot;revision&quot;: &quot;16B2555&quot;,
+                &quot;order&quot;: 2
+            },
+            {
+                &quot;repository&quot;: &quot;OSX&quot;,
+                &quot;revision&quot;: &quot;16A323&quot;,
+                &quot;order&quot;: 1
+            }
+        ]
+    }
+
+    const notYetReportedCommit = {
+        revision: '210951',
+        time: '2017-01-20T03:56:20.045Z'
+    }
+
+    function assertCommitIsSameAsOneSubmitted(commit, submitted)
+    {
+        assert.equal(commit['revision'], submitted['revision']);
+        assert.equal(new Date(commit['time']).toISOString(), submitted['time']);
+        assert.equal(commit['message'], submitted['message']);
+        assert.equal(commit['authorName'], submitted['author']['name']);
+        assert.equal(commit['authorEmail'], submitted['author']['account']);
+        if(submitted['previousCommit']) {
+            assert.ok(commit['previousCommit']);
+        } else {
+            assert.equal(commit['previousCommit'], null);
+        }
+    }
+
+    describe('/api/commits/&lt;repository&gt;/', () =&gt; {
+        it(&quot;should return RepositoryNotFound when there are no matching repository&quot;, () =&gt; {
+            return TestServer.remoteAPI().getJSON('/api/commits/WebKit').then((response) =&gt; {
+                assert.equal(response['status'], 'RepositoryNotFound');
+            });
+        });
+
+        it(&quot;should return an empty result when there are no reported commits&quot;, () =&gt; {
+            const db = TestServer.database();
+            return Promise.all([
+                db.insert('repositories', {'id': 1, 'name': 'WebKit'}),
+                db.insert('commits', {'repository': 1, 'revision': '210950', 'time': '2017-01-20T03:49:37.887Z'})
+            ]).then(() =&gt; {
+                return TestServer.remoteAPI().getJSON('/api/commits/WebKit');
+            }).then((response) =&gt; {
+                assert.equal(response['status'], 'OK');
+                assert.deepEqual(response['commits'], []);
+            });
+        });
+
+        it(&quot;should return the list of all commits for a given repository&quot;, () =&gt; {
+            return addSlaveForReport(subversionCommits).then(() =&gt; {
+                return TestServer.remoteAPI().postJSON('/api/report-commits/', subversionCommits);
+            }).then(function (response) {
+                assert.equal(response['status'], 'OK');
+                return TestServer.remoteAPI().getJSON('/api/commits/WebKit/');
+            }).then(function (result) {
+                assert.equal(result['status'], 'OK');
+
+                const commits = result['commits'];
+                assert.equal(commits.length, 3);
+                const submittedCommits = subversionCommits['commits'];
+                assertCommitIsSameAsOneSubmitted(commits[0], submittedCommits[0]);
+                assertCommitIsSameAsOneSubmitted(commits[1], submittedCommits[1]);
+                assertCommitIsSameAsOneSubmitted(commits[2], submittedCommits[2]);
+                assert.equal(commits[2]['previousCommit'], commits[1]['id']);
+            });
+        });
+
+        it(&quot;should return the list of ordered commits for a given repository&quot;, () =&gt; {
+            return addSlaveForReport(subversionCommits).then(() =&gt; {
+                return TestServer.remoteAPI().postJSON('/api/report-commits/', systemVersionCommits);
+            }).then(function (response) {
+                assert.equal(response['status'], 'OK');
+                return TestServer.remoteAPI().getJSON('/api/commits/OSX/');
+            }).then(function (result) {
+                assert.equal(result['status'], 'OK');
+                const commits = result['commits'];
+                const submittedCommits = systemVersionCommits['commits'];
+                assert.equal(commits.length, submittedCommits.length);
+                assert.equal(commits[0]['revision'], submittedCommits[5]['revision']);
+                assert.equal(commits[1]['revision'], submittedCommits[4]['revision']);
+                assert.equal(commits[2]['revision'], submittedCommits[3]['revision']);
+                assert.equal(commits[3]['revision'], submittedCommits[2]['revision']);
+                assert.equal(commits[4]['revision'], submittedCommits[1]['revision']);
+                assert.equal(commits[5]['revision'], submittedCommits[0]['revision']);
+            });
+        });
+    });
+
+    describe('/api/commits/&lt;repository&gt;/oldest', () =&gt; {
+        it(&quot;should return RepositoryNotFound when there are no matching repository&quot;, () =&gt; {
+            return TestServer.remoteAPI().getJSON('/api/commits/WebKit/oldest').then((response) =&gt; {
+                assert.equal(response['status'], 'RepositoryNotFound');
+            });
+        });
+
+        it(&quot;should return an empty results when there are no commits&quot;, () =&gt; {
+            return TestServer.database().insert('repositories', {'id': 1, 'name': 'WebKit'}).then(() =&gt; {
+                return TestServer.remoteAPI().getJSON('/api/commits/WebKit/oldest');
+            }).then((response) =&gt; {
+                assert.equal(response['status'], 'OK');
+                assert.deepEqual(response['commits'], []);
+            });
+        });
+
+        it(&quot;should return the oldest commit&quot;, () =&gt; {
+            const remote = TestServer.remoteAPI();
+            return addSlaveForReport(subversionCommits).then(() =&gt; {
+                return remote.postJSONWithStatus('/api/report-commits/', subversionCommits);
+            }).then(() =&gt; {
+                return remote.getJSON('/api/commits/WebKit/oldest');
+            }).then(function (result) {
+                assert.equal(result['status'], 'OK');
+                assert.equal(result['commits'].length, 1);
+                assertCommitIsSameAsOneSubmitted(result['commits'][0], subversionCommits['commits'][0]);
+            });
+        });
+
+        it(&quot;should return the oldest commit based on 'commit_order' when 'commit_time' is missing&quot;, () =&gt; {
+            const remote = TestServer.remoteAPI();
+            return addSlaveForReport(systemVersionCommits).then(() =&gt; {
+                return remote.postJSONWithStatus('/api/report-commits/', systemVersionCommits);
+            }).then(() =&gt; {
+                return remote.getJSON('/api/commits/OSX/oldest');
+            }).then(function (result) {
+                assert.equal(result['status'], 'OK');
+                assert.equal(result['commits'].length, 1);
+                assert.equal(result['commits'][0]['revision'], systemVersionCommits['commits'][5]['revision']);
+            });
+        });
+    });
+
+    describe('/api/commits/&lt;repository&gt;/latest', () =&gt; {
+        it(&quot;should return RepositoryNotFound when there are no matching repository&quot;, () =&gt; {
+            return TestServer.remoteAPI().getJSON('/api/commits/WebKit/latest').then((response) =&gt; {
+                assert.equal(response['status'], 'RepositoryNotFound');
+            });
+        });
+
+        it(&quot;should return an empty results when there are no commits&quot;, () =&gt; {
+            return TestServer.database().insert('repositories', {'id': 1, 'name': 'WebKit'}).then(() =&gt; {
+                return TestServer.remoteAPI().getJSON('/api/commits/WebKit/latest');
+            }).then((response) =&gt; {
+                assert.equal(response['status'], 'OK');
+                assert.deepEqual(response['commits'], []);
+            });
+        });
+
+        it(&quot;should return the oldest commit&quot;, () =&gt; {
+            const remote = TestServer.remoteAPI();
+            return addSlaveForReport(subversionCommits).then(() =&gt; {
+                return remote.postJSONWithStatus('/api/report-commits/', subversionCommits);
+            }).then(() =&gt; {
+                return remote.getJSON('/api/commits/WebKit/latest');
+            }).then(function (result) {
+                assert.equal(result['status'], 'OK');
+                assert.equal(result['commits'].length, 1);
+                assertCommitIsSameAsOneSubmitted(result['commits'][0], subversionCommits['commits'].slice().pop());
+            });
+        });
+
+        it(&quot;should return the latest commit based on 'commit_order' when 'commit_time' is missing&quot;, () =&gt; {
+            const remote = TestServer.remoteAPI();
+            return addSlaveForReport(systemVersionCommits).then(() =&gt; {
+                return remote.postJSONWithStatus('/api/report-commits/', systemVersionCommits);
+            }).then(() =&gt; {
+                return remote.getJSON('/api/commits/OSX/latest');
+            }).then(function (result) {
+                assert.equal(result['status'], 'OK');
+                assert.equal(result['commits'].length, 1);
+                assert.equal(result['commits'][0]['revision'], systemVersionCommits['commits'][0]['revision']);
+            });
+        });
+    });
+
+    describe('/api/commits/&lt;repository&gt;/last-reported', () =&gt; {
+        it(&quot;should return RepositoryNotFound when there are no matching repository&quot;, () =&gt; {
+            return TestServer.remoteAPI().getJSON('/api/commits/WebKit/last-reported').then((response) =&gt; {
+                assert.equal(response['status'], 'RepositoryNotFound');
+            });
+        });
+
+        it(&quot;should return an empty result when there are no reported commits&quot;, () =&gt; {
+            const db = TestServer.database();
+            return Promise.all([
+                db.insert('repositories', {'id': 1, 'name': 'WebKit'}),
+                db.insert('commits', {'repository': 1, 'revision': '210950', 'time': '2017-01-20T03:49:37.887Z'})
+            ]).then(() =&gt; {
+                return TestServer.remoteAPI().getJSON('/api/commits/WebKit/last-reported');
+            }).then((response) =&gt; {
+                assert.equal(response['status'], 'OK');
+                assert.deepEqual(response['commits'], []);
+            });
+        });
+
+        it(&quot;should return an empty results when there are no reported commits&quot;, () =&gt; {
+            return TestServer.database().insert('repositories', {'id': 1, 'name': 'WebKit'}).then(() =&gt; {
+                return TestServer.remoteAPI().getJSON('/api/commits/WebKit/last-reported');
+            }).then((response) =&gt; {
+                assert.equal(response['status'], 'OK');
+                assert.deepEqual(response['commits'], []);
+            });
+        });
+
+        it(&quot;should return the oldest reported commit&quot;, () =&gt; {
+            const db = TestServer.database();
+            const remote = TestServer.remoteAPI();
+            return Promise.all([
+                addSlaveForReport(subversionCommits),
+                db.insert('repositories', {'id': 1, 'name': 'WebKit'}),
+                db.insert('commits', {'repository': 1, 'revision': notYetReportedCommit.revision, 'time': notYetReportedCommit.time}),
+            ]).then(() =&gt; {
+                return remote.postJSONWithStatus('/api/report-commits/', subversionCommits);
+            }).then(() =&gt; {
+                return remote.getJSON('/api/commits/WebKit/last-reported');
+            }).then(function (result) {
+                assert.equal(result['status'], 'OK');
+                assert.equal(result['commits'].length, 1);
+                assertCommitIsSameAsOneSubmitted(result['commits'][0], subversionCommits['commits'].slice().pop());
+            });
+        });
+
+        it(&quot;should return the last reported commit based on 'commit_order' when 'commit_time' is missing&quot;, () =&gt; {
+            const remote = TestServer.remoteAPI();
+            return addSlaveForReport(systemVersionCommits).then(() =&gt; {
+                return remote.postJSONWithStatus('/api/report-commits/', systemVersionCommits);
+            }).then(() =&gt; {
+                return remote.getJSON('/api/commits/OSX/last-reported');
+            }).then(function (result) {
+                assert.equal(result['status'], 'OK');
+                assert.equal(result['commits'].length, 1);
+                assert.equal(result['commits'][0]['revision'], systemVersionCommits['commits'][0]['revision']);
+            });
+        });
+    });
+
+    describe('/api/commits/&lt;repository&gt;/last-reported?from=&lt;start_order&gt;&amp;to=&lt;end_order&gt;', () =&gt; {
+        it(&quot;should return a list of commit in given valid order range&quot;, () =&gt; {
+            const db = TestServer.database();
+            return Promise.all([
+                db.insert('repositories', {'id': 1, 'name': 'OSX'}),
+                db.insert('commits', {'repository': 1, 'revision': 'Sierra16C67', 'order': 367, 'reported': true}),
+                db.insert('commits', {'repository': 1, 'revision': 'Sierra16C68', 'order': 368, 'reported': true}),
+                db.insert('commits', {'repository': 1, 'revision': 'Sierra16C69', 'order': 369, 'reported': false}),
+                db.insert('commits', {'repository': 1, 'revision': 'Sierra16D32', 'order': 432, 'reported': true})
+            ]).then(() =&gt; {
+                return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=367&amp;to=370');
+            }).then((response) =&gt; {
+                assert.equal(response['status'], 'OK');
+                const results = response['commits'];
+                assert.equal(results.length, 1);
+                const commit = results[0];
+                assert.equal(commit.revision, 'Sierra16C68');
+            }).then(() =&gt; {
+                return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=370&amp;to=367');
+            }).then((response) =&gt; {
+                assert.equal(response['status'], 'OK');
+                const results = response['commits'];
+                assert.equal(results.length, 0);
+            }).then(() =&gt; {
+                return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=200&amp;to=299');
+            }).then((response) =&gt; {
+                assert.equal(response['status'], 'OK');
+                const results = response['commits'];
+                assert.equal(results.length, 0);
+            }).then(() =&gt; {
+                return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=369&amp;to=432');
+            }).then((response) =&gt; {
+                assert.equal(response['status'], 'OK');
+                const results = response['commits'];
+                assert.equal(results.length, 1);
+                const commit = results[0];
+                assert.equal(commit.revision, 'Sierra16D32');
+            });
+        });
+    });
+
+    describe('/api/commits/&lt;repository&gt;/&lt;commit&gt;', () =&gt; {
+        it(&quot;should return RepositoryNotFound when there are no matching repository&quot;, () =&gt; {
+            return TestServer.remoteAPI().getJSON('/api/commits/WebKit/210949').then((response) =&gt; {
+                assert.equal(response['status'], 'RepositoryNotFound');
+            });
+        });
+
+        it(&quot;should return UnknownCommit when one of the specified commit does not exist in the database&quot;, () =&gt; {
+            const db = TestServer.database();
+            return Promise.all([
+                db.insert('repositories', {'id': 1, 'name': 'WebKit'}),
+                db.insert('commits', {'repository': 1, 'revision': '210950', 'time': '2017-01-20T03:49:37.887Z'})
+            ]).then(() =&gt; {
+                return TestServer.remoteAPI().getJSON('/api/commits/WebKit/210949');
+            }).then((response) =&gt; {
+                assert.equal(response['status'], 'UnknownCommit');
+            });
+        });
+
+        it(&quot;should return the commit even if it had not been reported&quot;, () =&gt; {
+            const db = TestServer.database();
+            return Promise.all([
+                db.insert('repositories', {'id': 1, 'name': 'WebKit'}),
+                db.insert('commits', {'repository': 1, 'revision': '210950', 'time': '2017-01-20T03:49:37.887Z'})
+            ]).then(() =&gt; {
+                return TestServer.remoteAPI().getJSON('/api/commits/WebKit/210950');
+            }).then((result) =&gt; {
+                assert.equal(result['status'], 'OK');
+                assert.equal(result['commits'].length, 1);
+                assertCommitIsSameAsOneSubmitted(result['commits'][0], {
+                    previousCommit: null,
+                    revision: '210950',
+                    time: '2017-01-20T03:49:37.887Z',
+                    author: {name: null, account: null},
+                    message: null,
+                });
+            });
+        });
+
+        it(&quot;should return the full result for a reported commit&quot;, () =&gt; {
+            const remote = TestServer.remoteAPI();
+            return addSlaveForReport(subversionCommits).then(() =&gt; {
+                return remote.postJSONWithStatus('/api/report-commits/', subversionCommits);
+            }).then(() =&gt; {
+                return remote.getJSON('/api/commits/WebKit/210949');
+            }).then((result) =&gt; {
+                assert.equal(result['status'], 'OK');
+                assert.deepEqual(result['commits'].length, 1);
+                assertCommitIsSameAsOneSubmitted(result['commits'][0], subversionCommits['commits'][1]);
+            });
+        });
+
+    });
+
+    describe('/api/commits/&lt;repository&gt;/?precedingRevision=&lt;commit-1&gt;&amp;lastRevision=&lt;commit-2&gt;', () =&gt; {
+        it(&quot;should return RepositoryNotFound when there are no matching repository&quot;, () =&gt; {
+            return TestServer.remoteAPI().getJSON('/api/commits/WebKit/?from=210900&amp;to=211000').then((response) =&gt; {
+                assert.equal(response['status'], 'RepositoryNotFound');
+            });
+        });
+
+        it(&quot;should return UnknownCommit when one of the specified commit does not exist in the database&quot;, () =&gt; {
+            const db = TestServer.database();
+            return Promise.all([
+                db.insert('repositories', {'id': 1, 'name': 'WebKit'}),
+                db.insert('commits', {'repository': 1, 'revision': '210950', 'time': '2017-01-20T03:49:37.887Z'})
+            ]).then(() =&gt; {
+                return TestServer.remoteAPI().getJSON('/api/commits/WebKit/?precedingRevision=210900&amp;lastRevision=211000');
+            }).then((response) =&gt; {
+                assert.equal(response['status'], 'UnknownCommit');
+            });
+        });
+
+        it(&quot;should return an empty result when commits in the specified range have not been reported&quot;, () =&gt; {
+            const db = TestServer.database();
+            return Promise.all([
+                db.insert('repositories', {'id': 1, 'name': 'WebKit'}),
+                db.insert('commits', {'repository': 1, 'revision': '210949', 'time': '2017-01-20T03:23:50.645Z'}),
+                db.insert('commits', {'repository': 1, 'revision': '210950', 'time': '2017-01-20T03:49:37.887Z'}),
+            ]).then(() =&gt; {
+                return TestServer.remoteAPI().getJSON('/api/commits/WebKit/?precedingRevision=210949&amp;lastRevision=210950');
+            }).then((response) =&gt; {
+                assert.equal(response['status'], 'OK');
+                assert.deepEqual(response['commits'], []);
+            });
+        });
+
+        it(&quot;should return InvalidCommitRange when the specified range is backwards&quot;, () =&gt; {
+            const db = TestServer.database();
+            return Promise.all([
+                db.insert('repositories', {'id': 1, 'name': 'WebKit'}),
+                db.insert('commits', {'repository': 1, 'revision': '210949', 'time': '2017-01-20T03:23:50.645Z'}),
+                db.insert('commits', {'repository': 1, 'revision': '210950', 'time': '2017-01-20T03:49:37.887Z'}),
+            ]).then(() =&gt; {
+                return TestServer.remoteAPI().getJSON('/api/commits/WebKit/?precedingRevision=210950&amp;lastRevision=210949');
+            }).then((response) =&gt; {
+                assert.equal(response['status'], 'InvalidCommitRange');
+            });
+        });
+
+        it(&quot;should return use the commit order when time is not specified&quot;, () =&gt; {
+            const db = TestServer.database();
+            return Promise.all([
+                db.insert('repositories', {'id': 1, 'name': 'macOS'}),
+                db.insert('commits', {'repository': 1, 'revision': '10.12 16A323', order: 1, 'reported': true}),
+                db.insert('commits', {'repository': 1, 'revision': '10.12 16B2555', order: 2, 'reported': true}),
+                db.insert('commits', {'repository': 1, 'revision': '10.12 16B2657', order: 3, 'reported': true}),
+            ]).then(() =&gt; {
+                return TestServer.remoteAPI().getJSON('/api/commits/macOS/?precedingRevision=10.12%2016A323&amp;lastRevision=10.12%2016B2657');
+            }).then((response) =&gt; {
+                assert.equal(response['status'], 'OK');
+                assert.deepEqual(response['commits'].map((commit) =&gt; commit['revision']), ['10.12 16B2555', '10.12 16B2657']);
+            });
+        });
+
+        it(&quot;should return InconsistentCommits when precedingRevision specifies a time but lastRevision does not&quot;, () =&gt; {
+            const db = TestServer.database();
+            return Promise.all([
+                db.insert('repositories', {'id': 1, 'name': 'macOS'}),
+                db.insert('commits', {'repository': 1, 'revision': '10.12 16A323', time: '2017-01-20T03:23:50.645Z', order: 1, 'reported': true}),
+                db.insert('commits', {'repository': 1, 'revision': '10.12 16B2555', order: 2, 'reported': true}),
+                db.insert('commits', {'repository': 1, 'revision': '10.12 16B2657', order: 3, 'reported': true}),
+            ]).then(() =&gt; {
+                return TestServer.remoteAPI().getJSON('/api/commits/macOS/?precedingRevision=10.12%2016A323&amp;lastRevision=10.12%2016B2657');
+            }).then((response) =&gt; {
+                assert.equal(response['status'], 'InconsistentCommits');
+            });
+        });
+
+        it(&quot;should return InconsistentCommits when precedingRevision does not specify a time has a time but lastRevision does&quot;, () =&gt; {
+            const db = TestServer.database();
+            return Promise.all([
+                db.insert('repositories', {'id': 1, 'name': 'macOS'}),
+                db.insert('commits', {'repository': 1, 'revision': '10.12 16A323', order: 1, 'reported': true}),
+                db.insert('commits', {'repository': 1, 'revision': '10.12 16B2555', order: 2, 'reported': true}),
+                db.insert('commits', {'repository': 1, 'revision': '10.12 16B2657', time: '2017-01-20T03:23:50.645Z', order: 3, 'reported': true}),
+            ]).then(() =&gt; {
+                return TestServer.remoteAPI().getJSON('/api/commits/macOS/?precedingRevision=10.12%2016A323&amp;lastRevision=10.12%2016B2657');
+            }).then((response) =&gt; {
+                assert.equal(response['status'], 'InconsistentCommits');
+            });
+        });
+
+        it(&quot;should return empty results when precedingRevision does not specify a time or an order has a time but lastRevision does&quot;, () =&gt; {
+            const db = TestServer.database();
+            return Promise.all([
+                db.insert('repositories', {'id': 1, 'name': 'macOS'}),
+                db.insert('commits', {'repository': 1, 'revision': '10.12 16A323', 'reported': true}),
+                db.insert('commits', {'repository': 1, 'revision': '10.12 16B2555', order: 2, 'reported': true}),
+                db.insert('commits', {'repository': 1, 'revision': '10.12 16B2657', order: 3, 'reported': true}),
+            ]).then(() =&gt; {
+                return TestServer.remoteAPI().getJSON('/api/commits/macOS/?precedingRevision=10.12%2016A323&amp;lastRevision=10.12%2016B2657');
+            }).then((response) =&gt; {
+                assert.equal(response['status'], 'OK');
+                assert.deepEqual(response['commits'], []);
+            });
+        });
+
+        it(&quot;should return empty results when precedingRevision an order has a time but lastRevision does not&quot;, () =&gt; {
+            const db = TestServer.database();
+            return Promise.all([
+                db.insert('repositories', {'id': 1, 'name': 'macOS'}),
+                db.insert('commits', {'repository': 1, 'revision': '10.12 16A323', order: 1, 'reported': true}),
+                db.insert('commits', {'repository': 1, 'revision': '10.12 16B2555', order: 2, 'reported': true}),
+                db.insert('commits', {'repository': 1, 'revision': '10.12 16B2657', 'reported': true}),
+            ]).then(() =&gt; {
+                return TestServer.remoteAPI().getJSON('/api/commits/macOS/?precedingRevision=10.12%2016A323&amp;lastRevision=10.12%2016B2657');
+            }).then((response) =&gt; {
+                assert.equal(response['status'], 'OK');
+                assert.deepEqual(response['commits'], []);
+            });
+        });
+
+        it(&quot;should return reported commits in the specified range&quot;, () =&gt; {
+            const db = TestServer.database();
+            return Promise.all([
+                db.insert('repositories', {'id': 1, 'name': 'WebKit'}),
+                db.insert('commits', {'repository': 1, 'revision': '210948', 'time': '2017-01-20T02:52:34.577Z', 'reported': true}),
+                db.insert('commits', {'repository': 1, 'revision': '210949', 'time': '2017-01-20T03:23:50.645Z', 'reported': true}),
+                db.insert('commits', {'repository': 1, 'revision': '210950', 'time': '2017-01-20T03:49:37.887Z', 'reported': true}),
+            ]).then(() =&gt; {
+                return TestServer.remoteAPI().getJSON('/api/commits/WebKit/?precedingRevision=210948&amp;lastRevision=210950');
+            }).then((result) =&gt; {
+                assert.equal(result['status'], 'OK');
+                assert.deepEqual(result['commits'].length, 2);
+                assertCommitIsSameAsOneSubmitted(result['commits'][0], {
+                    previousCommit: null,
+                    revision: '210949',
+                    time: '2017-01-20T03:23:50.645Z',
+                    author: {name: null, account: null},
+                    message: null,
+                });
+                assertCommitIsSameAsOneSubmitted(result['commits'][1], {
+                    previousCommit: null,
+                    revision: '210950',
+                    time: '2017-01-20T03:49:37.887Z',
+                    author: {name: null, account: null},
+                    message: null,
+                });
+            });
+        });
+
+        it(&quot;should not include a revision not within the specified range&quot;, () =&gt; {
+            const db = TestServer.database();
+            const remote = TestServer.remoteAPI();
+            return Promise.all([
+                db.insert('repositories', {'id': 1, 'name': 'WebKit'}),
+                db.insert('commits', {'repository': 1, 'revision': '210947', 'time': '2017-01-20T02:38:45.485Z', 'reported': false}),
+                db.insert('commits', {'repository': 1, 'revision': '210948', 'time': '2017-01-20T02:52:34.577Z', 'reported': false}),
+                db.insert('commits', {'repository': 1, 'revision': '210949', 'time': '2017-01-20T03:23:50.645Z', 'reported': false}),
+                db.insert('commits', {'repository': 1, 'revision': '210950', 'time': '2017-01-20T03:49:37.887Z', 'reported': false}),
+            ]).then(() =&gt; {
+                return addSlaveForReport(subversionCommits);
+            }).then(() =&gt; {
+                return remote.postJSONWithStatus('/api/report-commits/', subversionCommits);
+            }).then(() =&gt; {
+                return remote.getJSON('/api/commits/WebKit/?precedingRevision=210947&amp;lastRevision=210949');
+            }).then((result) =&gt; {
+                assert.equal(result['status'], 'OK');
+                assert.deepEqual(result['commits'].length, 2);
+                assertCommitIsSameAsOneSubmitted(result['commits'][0], subversionCommits['commits'][0]);
+                assertCommitIsSameAsOneSubmitted(result['commits'][1], subversionCommits['commits'][1]);
+            });
+        });
+
+    });
+
+});
</ins></span></pre></div>
<a id="trunkWebsitesperfwebkitorgservertestsapicommitsjs"></a>
<div class="delfile"><h4>Deleted: trunk/Websites/perf.webkit.org/server-tests/api-commits.js (214201 => 214202)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/server-tests/api-commits.js        2017-03-21 01:13:50 UTC (rev 214201)
+++ trunk/Websites/perf.webkit.org/server-tests/api-commits.js        2017-03-21 01:19:39 UTC (rev 214202)
</span><span class="lines">@@ -1,476 +0,0 @@
</span><del>-'use strict';
-
-const assert = require('assert');
-
-const TestServer = require('./resources/test-server.js');
-const addSlaveForReport = require('./resources/common-operations.js').addSlaveForReport;
-const prepareServerTest = require('./resources/common-operations.js').prepareServerTest;
-
-describe(&quot;/api/commits/&quot;, function () {
-    prepareServerTest(this);
-
-    const subversionCommits = {
-        &quot;slaveName&quot;: &quot;someSlave&quot;,
-        &quot;slavePassword&quot;: &quot;somePassword&quot;,
-        &quot;commits&quot;: [
-            {
-                &quot;repository&quot;: &quot;WebKit&quot;,
-                &quot;revision&quot;: &quot;210948&quot;,
-                &quot;time&quot;: &quot;2017-01-20T02:52:34.577Z&quot;,
-                &quot;author&quot;: {&quot;name&quot;: &quot;Zalan Bujtas&quot;, &quot;account&quot;: &quot;zalan@apple.com&quot;},
-                &quot;message&quot;: &quot;a message&quot;,
-            },
-            {
-                &quot;repository&quot;: &quot;WebKit&quot;,
-                &quot;revision&quot;: &quot;210949&quot;,
-                &quot;time&quot;: &quot;2017-01-20T03:23:50.645Z&quot;,
-                &quot;author&quot;: {&quot;name&quot;: &quot;Chris Dumez&quot;, &quot;account&quot;: &quot;cdumez@apple.com&quot;},
-                &quot;message&quot;: &quot;some message&quot;,
-            },
-            {
-                &quot;repository&quot;: &quot;WebKit&quot;,
-                &quot;previousCommit&quot;: &quot;210949&quot;,
-                &quot;revision&quot;: &quot;210950&quot;,
-                &quot;time&quot;: &quot;2017-01-20T03:49:37.887Z&quot;,
-                &quot;author&quot;: {&quot;name&quot;: &quot;Commit Queue&quot;, &quot;account&quot;: &quot;commit-queue@webkit.org&quot;},
-                &quot;message&quot;: &quot;another message&quot;,
-            }
-        ]
-    }
-
-    const systemVersionCommits = {
-        &quot;slaveName&quot;: &quot;someSlave&quot;,
-        &quot;slavePassword&quot;: &quot;somePassword&quot;,
-        &quot;commits&quot;: [
-            {
-                &quot;repository&quot;: &quot;OSX&quot;,
-                &quot;revision&quot;: &quot;16D32&quot;,
-                &quot;order&quot;: 6
-            },
-            {
-                &quot;repository&quot;: &quot;OSX&quot;,
-                &quot;revision&quot;: &quot;16C68&quot;,
-                &quot;order&quot;: 5
-            },
-            {
-                &quot;repository&quot;: &quot;OSX&quot;,
-                &quot;revision&quot;: &quot;16C67&quot;,
-                &quot;order&quot;: 4
-            },
-            {
-                &quot;repository&quot;: &quot;OSX&quot;,
-                &quot;revision&quot;: &quot;16B2657&quot;,
-                &quot;order&quot;: 3
-            },
-            {
-                &quot;repository&quot;: &quot;OSX&quot;,
-                &quot;revision&quot;: &quot;16B2555&quot;,
-                &quot;order&quot;: 2
-            },
-            {
-                &quot;repository&quot;: &quot;OSX&quot;,
-                &quot;revision&quot;: &quot;16A323&quot;,
-                &quot;order&quot;: 1
-            }
-        ]
-    }
-
-    const notYetReportedCommit = {
-        revision: '210951',
-        time: '2017-01-20T03:56:20.045Z'
-    }
-
-    function assertCommitIsSameAsOneSubmitted(commit, submitted)
-    {
-        assert.equal(commit['revision'], submitted['revision']);
-        assert.equal(new Date(commit['time']).toISOString(), submitted['time']);
-        assert.equal(commit['message'], submitted['message']);
-        assert.equal(commit['authorName'], submitted['author']['name']);
-        assert.equal(commit['authorEmail'], submitted['author']['account']);
-        if(submitted['previousCommit']) {
-            assert.ok(commit['previousCommit']);
-        } else {
-            assert.equal(commit['previousCommit'], null);
-        }
-    }
-
-    describe('/api/commits/&lt;repository&gt;/', () =&gt; {
-        it(&quot;should return RepositoryNotFound when there are no matching repository&quot;, () =&gt; {
-            return TestServer.remoteAPI().getJSON('/api/commits/WebKit').then((response) =&gt; {
-                assert.equal(response['status'], 'RepositoryNotFound');
-            });
-        });
-
-        it(&quot;should return an empty result when there are no reported commits&quot;, () =&gt; {
-            const db = TestServer.database();
-            return Promise.all([
-                db.insert('repositories', {'id': 1, 'name': 'WebKit'}),
-                db.insert('commits', {'repository': 1, 'revision': '210950', 'time': '2017-01-20T03:49:37.887Z'})
-            ]).then(() =&gt; {
-                return TestServer.remoteAPI().getJSON('/api/commits/WebKit');
-            }).then((response) =&gt; {
-                assert.equal(response['status'], 'OK');
-                assert.deepEqual(response['commits'], []);
-            });
-        });
-
-        it(&quot;should return the list of all commits for a given repository&quot;, () =&gt; {
-            return addSlaveForReport(subversionCommits).then(() =&gt; {
-                return TestServer.remoteAPI().postJSON('/api/report-commits/', subversionCommits);
-            }).then(function (response) {
-                assert.equal(response['status'], 'OK');
-                return TestServer.remoteAPI().getJSON('/api/commits/WebKit/');
-            }).then(function (result) {
-                assert.equal(result['status'], 'OK');
-
-                const commits = result['commits'];
-                assert.equal(commits.length, 3);
-                const submittedCommits = subversionCommits['commits'];
-                assertCommitIsSameAsOneSubmitted(commits[0], submittedCommits[0]);
-                assertCommitIsSameAsOneSubmitted(commits[1], submittedCommits[1]);
-                assertCommitIsSameAsOneSubmitted(commits[2], submittedCommits[2]);
-                assert.equal(commits[2]['previousCommit'], commits[1]['id']);
-            });
-        });
-
-        it(&quot;should return the list of ordered commits for a given repository&quot;, () =&gt; {
-            return addSlaveForReport(subversionCommits).then(() =&gt; {
-                return TestServer.remoteAPI().postJSON('/api/report-commits/', systemVersionCommits);
-            }).then(function (response) {
-                assert.equal(response['status'], 'OK');
-                return TestServer.remoteAPI().getJSON('/api/commits/OSX/');
-            }).then(function (result) {
-                assert.equal(result['status'], 'OK');
-                const commits = result['commits'];
-                const submittedCommits = systemVersionCommits['commits'];
-                assert.equal(commits.length, submittedCommits.length);
-                assert.equal(commits[0]['revision'], submittedCommits[5]['revision']);
-                assert.equal(commits[1]['revision'], submittedCommits[4]['revision']);
-                assert.equal(commits[2]['revision'], submittedCommits[3]['revision']);
-                assert.equal(commits[3]['revision'], submittedCommits[2]['revision']);
-                assert.equal(commits[4]['revision'], submittedCommits[1]['revision']);
-                assert.equal(commits[5]['revision'], submittedCommits[0]['revision']);
-            });
-        });
-    });
-
-    describe('/api/commits/&lt;repository&gt;/oldest', () =&gt; {
-        it(&quot;should return RepositoryNotFound when there are no matching repository&quot;, () =&gt; {
-            return TestServer.remoteAPI().getJSON('/api/commits/WebKit/oldest').then((response) =&gt; {
-                assert.equal(response['status'], 'RepositoryNotFound');
-            });
-        });
-
-        it(&quot;should return an empty results when there are no commits&quot;, () =&gt; {
-            return TestServer.database().insert('repositories', {'id': 1, 'name': 'WebKit'}).then(() =&gt; {
-                return TestServer.remoteAPI().getJSON('/api/commits/WebKit/oldest');
-            }).then((response) =&gt; {
-                assert.equal(response['status'], 'OK');
-                assert.deepEqual(response['commits'], []);
-            });
-        });
-
-        it(&quot;should return the oldest commit&quot;, () =&gt; {
-            const remote = TestServer.remoteAPI();
-            return addSlaveForReport(subversionCommits).then(() =&gt; {
-                return remote.postJSONWithStatus('/api/report-commits/', subversionCommits);
-            }).then(() =&gt; {
-                return remote.getJSON('/api/commits/WebKit/oldest');
-            }).then(function (result) {
-                assert.equal(result['status'], 'OK');
-                assert.equal(result['commits'].length, 1);
-                assertCommitIsSameAsOneSubmitted(result['commits'][0], subversionCommits['commits'][0]);
-            });
-        });
-
-        it(&quot;should return the oldest commit based on 'commit_order' when 'commit_time' is missing&quot;, () =&gt; {
-            const remote = TestServer.remoteAPI();
-            return addSlaveForReport(systemVersionCommits).then(() =&gt; {
-                return remote.postJSONWithStatus('/api/report-commits/', systemVersionCommits);
-            }).then(() =&gt; {
-                return remote.getJSON('/api/commits/OSX/oldest');
-            }).then(function (result) {
-                assert.equal(result['status'], 'OK');
-                assert.equal(result['commits'].length, 1);
-                assert.equal(result['commits'][0]['revision'], systemVersionCommits['commits'][5]['revision']);
-            });
-        });
-    });
-
-    describe('/api/commits/&lt;repository&gt;/latest', () =&gt; {
-        it(&quot;should return RepositoryNotFound when there are no matching repository&quot;, () =&gt; {
-            return TestServer.remoteAPI().getJSON('/api/commits/WebKit/latest').then((response) =&gt; {
-                assert.equal(response['status'], 'RepositoryNotFound');
-            });
-        });
-
-        it(&quot;should return an empty results when there are no commits&quot;, () =&gt; {
-            return TestServer.database().insert('repositories', {'id': 1, 'name': 'WebKit'}).then(() =&gt; {
-                return TestServer.remoteAPI().getJSON('/api/commits/WebKit/latest');
-            }).then((response) =&gt; {
-                assert.equal(response['status'], 'OK');
-                assert.deepEqual(response['commits'], []);
-            });
-        });
-
-        it(&quot;should return the oldest commit&quot;, () =&gt; {
-            const remote = TestServer.remoteAPI();
-            return addSlaveForReport(subversionCommits).then(() =&gt; {
-                return remote.postJSONWithStatus('/api/report-commits/', subversionCommits);
-            }).then(() =&gt; {
-                return remote.getJSON('/api/commits/WebKit/latest');
-            }).then(function (result) {
-                assert.equal(result['status'], 'OK');
-                assert.equal(result['commits'].length, 1);
-                assertCommitIsSameAsOneSubmitted(result['commits'][0], subversionCommits['commits'].slice().pop());
-            });
-        });
-
-        it(&quot;should return the latest commit based on 'commit_order' when 'commit_time' is missing&quot;, () =&gt; {
-            const remote = TestServer.remoteAPI();
-            return addSlaveForReport(systemVersionCommits).then(() =&gt; {
-                return remote.postJSONWithStatus('/api/report-commits/', systemVersionCommits);
-            }).then(() =&gt; {
-                return remote.getJSON('/api/commits/OSX/latest');
-            }).then(function (result) {
-                assert.equal(result['status'], 'OK');
-                assert.equal(result['commits'].length, 1);
-                assert.equal(result['commits'][0]['revision'], systemVersionCommits['commits'][0]['revision']);
-            });
-        });
-    });
-
-    describe('/api/commits/&lt;repository&gt;/last-reported', () =&gt; {
-        it(&quot;should return RepositoryNotFound when there are no matching repository&quot;, () =&gt; {
-            return TestServer.remoteAPI().getJSON('/api/commits/WebKit/last-reported').then((response) =&gt; {
-                assert.equal(response['status'], 'RepositoryNotFound');
-            });
-        });
-
-        it(&quot;should return an empty result when there are no reported commits&quot;, () =&gt; {
-            const db = TestServer.database();
-            return Promise.all([
-                db.insert('repositories', {'id': 1, 'name': 'WebKit'}),
-                db.insert('commits', {'repository': 1, 'revision': '210950', 'time': '2017-01-20T03:49:37.887Z'})
-            ]).then(() =&gt; {
-                return TestServer.remoteAPI().getJSON('/api/commits/WebKit/last-reported');
-            }).then((response) =&gt; {
-                assert.equal(response['status'], 'OK');
-                assert.deepEqual(response['commits'], []);
-            });
-        });
-
-        it(&quot;should return an empty results when there are no reported commits&quot;, () =&gt; {
-            return TestServer.database().insert('repositories', {'id': 1, 'name': 'WebKit'}).then(() =&gt; {
-                return TestServer.remoteAPI().getJSON('/api/commits/WebKit/last-reported');
-            }).then((response) =&gt; {
-                assert.equal(response['status'], 'OK');
-                assert.deepEqual(response['commits'], []);
-            });
-        });
-
-        it(&quot;should return the oldest reported commit&quot;, () =&gt; {
-            const db = TestServer.database();
-            const remote = TestServer.remoteAPI();
-            return Promise.all([
-                addSlaveForReport(subversionCommits),
-                db.insert('repositories', {'id': 1, 'name': 'WebKit'}),
-                db.insert('commits', {'repository': 1, 'revision': notYetReportedCommit.revision, 'time': notYetReportedCommit.time}),
-            ]).then(() =&gt; {
-                return remote.postJSONWithStatus('/api/report-commits/', subversionCommits);
-            }).then(() =&gt; {
-                return remote.getJSON('/api/commits/WebKit/last-reported');
-            }).then(function (result) {
-                assert.equal(result['status'], 'OK');
-                assert.equal(result['commits'].length, 1);
-                assertCommitIsSameAsOneSubmitted(result['commits'][0], subversionCommits['commits'].slice().pop());
-            });
-        });
-
-        it(&quot;should return the last reported commit based on 'commit_order' when 'commit_time' is missing&quot;, () =&gt; {
-            const remote = TestServer.remoteAPI();
-            return addSlaveForReport(systemVersionCommits).then(() =&gt; {
-                return remote.postJSONWithStatus('/api/report-commits/', systemVersionCommits);
-            }).then(() =&gt; {
-                return remote.getJSON('/api/commits/OSX/last-reported');
-            }).then(function (result) {
-                assert.equal(result['status'], 'OK');
-                assert.equal(result['commits'].length, 1);
-                assert.equal(result['commits'][0]['revision'], systemVersionCommits['commits'][0]['revision']);
-            });
-        });
-    });
-
-    describe('/api/commits/&lt;repository&gt;/last-reported?from=&lt;start_order&gt;&amp;to=&lt;end_order&gt;', () =&gt; {
-        it(&quot;should return a list of commit in given valid order range&quot;, () =&gt; {
-            const db = TestServer.database();
-            return Promise.all([
-                db.insert('repositories', {'id': 1, 'name': 'OSX'}),
-                db.insert('commits', {'repository': 1, 'revision': 'Sierra16C67', 'order': 367, 'reported': true}),
-                db.insert('commits', {'repository': 1, 'revision': 'Sierra16C68', 'order': 368, 'reported': true}),
-                db.insert('commits', {'repository': 1, 'revision': 'Sierra16C69', 'order': 369, 'reported': false}),
-                db.insert('commits', {'repository': 1, 'revision': 'Sierra16D32', 'order': 432, 'reported': true})
-            ]).then(() =&gt; {
-                return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=367&amp;to=370');
-            }).then((response) =&gt; {
-                assert.equal(response['status'], 'OK');
-                const results = response['commits'];
-                assert.equal(results.length, 1);
-                const commit = results[0];
-                assert.equal(commit.revision, 'Sierra16C68');
-            }).then(() =&gt; {
-                return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=370&amp;to=367');
-            }).then((response) =&gt; {
-                assert.equal(response['status'], 'OK');
-                const results = response['commits'];
-                assert.equal(results.length, 0);
-            }).then(() =&gt; {
-                return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=200&amp;to=299');
-            }).then((response) =&gt; {
-                assert.equal(response['status'], 'OK');
-                const results = response['commits'];
-                assert.equal(results.length, 0);
-            }).then(() =&gt; {
-                return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=369&amp;to=432');
-            }).then((response) =&gt; {
-                assert.equal(response['status'], 'OK');
-                const results = response['commits'];
-                assert.equal(results.length, 1);
-                const commit = results[0];
-                assert.equal(commit.revision, 'Sierra16D32');
-            });
-        });
-    });
-
-    describe('/api/commits/&lt;repository&gt;/&lt;commit&gt;', () =&gt; {
-        it(&quot;should return RepositoryNotFound when there are no matching repository&quot;, () =&gt; {
-            return TestServer.remoteAPI().getJSON('/api/commits/WebKit/210949').then((response) =&gt; {
-                assert.equal(response['status'], 'RepositoryNotFound');
-            });
-        });
-
-        it(&quot;should return UnknownCommit when one of the specified commit does not exist in the database&quot;, () =&gt; {
-            const db = TestServer.database();
-            return Promise.all([
-                db.insert('repositories', {'id': 1, 'name': 'WebKit'}),
-                db.insert('commits', {'repository': 1, 'revision': '210950', 'time': '2017-01-20T03:49:37.887Z'})
-            ]).then(() =&gt; {
-                return TestServer.remoteAPI().getJSON('/api/commits/WebKit/210949');
-            }).then((response) =&gt; {
-                assert.equal(response['status'], 'UnknownCommit');
-            });
-        });
-
-        it(&quot;should return the commit even if it had not been reported&quot;, () =&gt; {
-            const db = TestServer.database();
-            return Promise.all([
-                db.insert('repositories', {'id': 1, 'name': 'WebKit'}),
-                db.insert('commits', {'repository': 1, 'revision': '210950', 'time': '2017-01-20T03:49:37.887Z'})
-            ]).then(() =&gt; {
-                return TestServer.remoteAPI().getJSON('/api/commits/WebKit/210950');
-            }).then((result) =&gt; {
-                assert.equal(result['status'], 'OK');
-                assert.equal(result['commits'].length, 1);
-                assertCommitIsSameAsOneSubmitted(result['commits'][0], {
-                    previousCommit: null,
-                    revision: '210950',
-                    time: '2017-01-20T03:49:37.887Z',
-                    author: {name: null, account: null},
-                    message: null,
-                });
-            });
-        });
-
-        it(&quot;should return the full result for a reported commit&quot;, () =&gt; {
-            const remote = TestServer.remoteAPI();
-            return addSlaveForReport(subversionCommits).then(() =&gt; {
-                return remote.postJSONWithStatus('/api/report-commits/', subversionCommits);
-            }).then(() =&gt; {
-                return remote.getJSON('/api/commits/WebKit/210949');
-            }).then((result) =&gt; {
-                assert.equal(result['status'], 'OK');
-                assert.deepEqual(result['commits'].length, 1);
-                assertCommitIsSameAsOneSubmitted(result['commits'][0], subversionCommits['commits'][1]);
-            });
-        });
-
-    });
-
-    describe('/api/commits/&lt;repository&gt;/?from=&lt;commit-1&gt;&amp;to=&lt;commit-2&gt;', () =&gt; {
-        it(&quot;should return RepositoryNotFound when there are no matching repository&quot;, () =&gt; {
-            return TestServer.remoteAPI().getJSON('/api/commits/WebKit/?from=210900&amp;to=211000').then((response) =&gt; {
-                assert.equal(response['status'], 'RepositoryNotFound');
-            });
-        });
-
-        it(&quot;should return UnknownCommit when one of the specified commit does not exist in the database&quot;, () =&gt; {
-            const db = TestServer.database();
-            return Promise.all([
-                db.insert('repositories', {'id': 1, 'name': 'WebKit'}),
-                db.insert('commits', {'repository': 1, 'revision': '210950', 'time': '2017-01-20T03:49:37.887Z'})
-            ]).then(() =&gt; {
-                return TestServer.remoteAPI().getJSON('/api/commits/WebKit/?from=210900&amp;to=211000');
-            }).then((response) =&gt; {
-                assert.equal(response['status'], 'UnknownCommit');
-            });
-        });
-
-        it(&quot;should return an empty result when commits in the specified range have not reported&quot;, () =&gt; {
-            const db = TestServer.database();
-            return Promise.all([
-                db.insert('repositories', {'id': 1, 'name': 'WebKit'}),
-                db.insert('commits', {'repository': 1, 'revision': '210949', 'time': '2017-01-20T03:23:50.645Z'}),
-                db.insert('commits', {'repository': 1, 'revision': '210950', 'time': '2017-01-20T03:49:37.887Z'}),
-            ]).then(() =&gt; {
-                return TestServer.remoteAPI().getJSON('/api/commits/WebKit/?from=210949&amp;to=210950');
-            }).then((response) =&gt; {
-                assert.equal(response['status'], 'OK');
-                assert.deepEqual(response['commits'], []);
-            });
-        });
-
-        it(&quot;should return reported commits in the specified range&quot;, () =&gt; {
-            const db = TestServer.database();
-            return Promise.all([
-                db.insert('repositories', {'id': 1, 'name': 'WebKit'}),
-                db.insert('commits', {'repository': 1, 'revision': '210949', 'time': '2017-01-20T03:23:50.645Z', 'reported': true}),
-                db.insert('commits', {'repository': 1, 'revision': '210950', 'time': '2017-01-20T03:49:37.887Z', 'reported': true}),
-            ]).then(() =&gt; {
-                return TestServer.remoteAPI().getJSON('/api/commits/WebKit/?from=210949&amp;to=210950');
-            }).then((result) =&gt; {
-                assert.equal(result['status'], 'OK');
-                assert.deepEqual(result['commits'].length, 2);
-                assertCommitIsSameAsOneSubmitted(result['commits'][0], {
-                    previousCommit: null,
-                    revision: '210949',
-                    time: '2017-01-20T03:23:50.645Z',
-                    author: {name: null, account: null},
-                    message: null,
-                });
-                assertCommitIsSameAsOneSubmitted(result['commits'][1], {
-                    previousCommit: null,
-                    revision: '210950',
-                    time: '2017-01-20T03:49:37.887Z',
-                    author: {name: null, account: null},
-                    message: null,
-                });
-            });
-        });
-
-        it(&quot;should not include a revision not within the specified range&quot;, () =&gt; {
-            const remote = TestServer.remoteAPI();
-            return addSlaveForReport(subversionCommits).then(() =&gt; {
-                return remote.postJSONWithStatus('/api/report-commits/', subversionCommits);
-            }).then(() =&gt; {
-                return remote.getJSON('/api/commits/WebKit/?from=210948&amp;to=210949');
-            }).then((result) =&gt; {
-                assert.equal(result['status'], 'OK');
-                assert.deepEqual(result['commits'].length, 2);
-                assertCommitIsSameAsOneSubmitted(result['commits'][0], subversionCommits['commits'][0]);
-                assertCommitIsSameAsOneSubmitted(result['commits'][1], subversionCommits['commits'][1]);
-            });
-        });
-
-    });
-
-});
</del></span></pre></div>
<a id="trunkWebsitesperfwebkitorgunittestscommitlogtestsjs"></a>
<div class="modfile"><h4>Modified: trunk/Websites/perf.webkit.org/unit-tests/commit-log-tests.js (214201 => 214202)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Websites/perf.webkit.org/unit-tests/commit-log-tests.js        2017-03-21 01:13:50 UTC (rev 214201)
+++ trunk/Websites/perf.webkit.org/unit-tests/commit-log-tests.js        2017-03-21 01:19:39 UTC (rev 214202)
</span><span class="lines">@@ -93,8 +93,6 @@
</span><span class="cx">     describe('diff', function () {
</span><span class="cx">         it('should use label() as the label the previous commit is missing', function () {
</span><span class="cx">             assert.deepEqual(webkitCommit().diff(), {
</span><del>-                from: null,
-                to: '200805',
</del><span class="cx">                 label: 'r200805',
</span><span class="cx">                 url: 'http://trac.webkit.org/changeset/200805',
</span><span class="cx">                 repository: MockModels.webkit
</span><span class="lines">@@ -101,8 +99,6 @@
</span><span class="cx">             });
</span><span class="cx"> 
</span><span class="cx">             assert.deepEqual(gitWebKitCommit().diff(), {
</span><del>-                from: null,
-                to: '6f8b0dbbda95a440503b88db1dd03dad3a7b07fb',
</del><span class="cx">                 label: '6f8b0dbb',
</span><span class="cx">                 url: 'http://trac.webkit.org/changeset/6f8b0dbbda95a440503b88db1dd03dad3a7b07fb',
</span><span class="cx">                 repository: MockModels.webkit,
</span><span class="lines">@@ -109,8 +105,6 @@
</span><span class="cx">             });
</span><span class="cx"> 
</span><span class="cx">             assert.deepEqual(osxCommit().diff(), {
</span><del>-                from: null,
-                to: '10.11.4 15E65',
</del><span class="cx">                 label: '10.11.4 15E65',
</span><span class="cx">                 url: '',
</span><span class="cx">                 repository: MockModels.osx,
</span><span class="lines">@@ -119,9 +113,7 @@
</span><span class="cx"> 
</span><span class="cx">         it('should use increment the old SVN revision by 1', function () {
</span><span class="cx">             assert.deepEqual(webkitCommit().diff(oldWebKitCommit()), {
</span><del>-                from: '200575',
-                to: '200805',
-                label: 'r200575-r200805',
</del><ins>+                label: 'r200574-r200805',
</ins><span class="cx">                 url: '',
</span><span class="cx">                 repository: MockModels.webkit
</span><span class="cx">             });
</span><span class="lines">@@ -129,8 +121,6 @@
</span><span class="cx"> 
</span><span class="cx">         it('should truncate a Git hash at 8th character', function () {
</span><span class="cx">             assert.deepEqual(gitWebKitCommit().diff(oldGitWebKitCommit()), {
</span><del>-                from: 'ffda14e6db0746d10d0f050907e4a7325851e502',
-                to: '6f8b0dbbda95a440503b88db1dd03dad3a7b07fb',
</del><span class="cx">                 label: 'ffda14e6..6f8b0dbb',
</span><span class="cx">                 url: '',
</span><span class="cx">                 repository: MockModels.webkit
</span><span class="lines">@@ -139,8 +129,6 @@
</span><span class="cx"> 
</span><span class="cx">         it('should surround &quot;-&quot; with spaces', function () {
</span><span class="cx">             assert.deepEqual(osxCommit().diff(oldOSXCommit()), {
</span><del>-                from: '10.11.3 15D21',
-                to: '10.11.4 15E65',
</del><span class="cx">                 label: '10.11.3 15D21 - 10.11.4 15E65',
</span><span class="cx">                 url: '',
</span><span class="cx">                 repository: MockModels.osx
</span></span></pre>
</div>
</div>

</body>
</html>