Add a bisect button to automatically schedule bisecting A/B tasks.
[WebKit-https.git] / Websites / perf.webkit.org / public / v3 / pages / analysis-task-page.js
index 02cfaa0..5aaf068 100644 (file)
@@ -258,8 +258,10 @@ class AnalysisTaskTestGroupPane extends ComponentBase {
         this._renderCurrentTestGroupLazily = new LazilyEvaluatedFunction(this._renderCurrentTestGroup.bind(this));
         this._testGroupMap = new Map;
         this._testGroups = [];
+        this._bisectingCommitSetByTestGroup = null;
         this._currentTestGroup = null;
         this._showHiddenGroups = false;
+        this._allTestGroupIdSetForCurrentTask = null;
     }
 
     didConstructShadowTree()
@@ -268,6 +270,12 @@ class AnalysisTaskTestGroupPane extends ComponentBase {
         this.part('retry-form').listenToAction('startTesting', (repetitionCount) => {
             this.dispatchAction('retryTestGroup', this._currentTestGroup, repetitionCount);
         });
+        this.part('bisect-form').listenToAction('startTesting', (repetitionCount) => {
+            const bisectingCommitSet = this._bisectingCommitSetByTestGroup.get(this._currentTestGroup);
+            const [oneCommitSet, anotherCommitSet] = this._currentTestGroup.requestedCommitSets();
+            const commitSets = [oneCommitSet, bisectingCommitSet, anotherCommitSet];
+            this.dispatchAction('bisectTestGroup', this._currentTestGroup, commitSets, repetitionCount);
+        });
     }
 
     setTestGroups(testGroups, currentTestGroup, showHiddenGroups)
@@ -277,7 +285,30 @@ class AnalysisTaskTestGroupPane extends ComponentBase {
         this._showHiddenGroups = showHiddenGroups;
         this.part('revision-table').setTestGroup(currentTestGroup);
         this.part('results-viewer').setTestGroup(currentTestGroup);
-        this.enqueueToRender();
+
+        const analysisTask = currentTestGroup.task();
+        const allTestGroupIdsForCurrentTask = TestGroup.findAllByTask(analysisTask.id()).map((testGroup) => testGroup.id());
+        const testGroupChanged = !this._allTestGroupIdSetForCurrentTask
+            || this._allTestGroupIdSetForCurrentTask.size !== allTestGroupIdsForCurrentTask.length
+            || !allTestGroupIdsForCurrentTask.every((testGroupId) => this._allTestGroupIdSetForCurrentTask.has(testGroupId));
+
+        const computedForCurrentTestGroup = this._bisectingCommitSetByTestGroup && this._bisectingCommitSetByTestGroup.has(currentTestGroup);
+
+        if (!testGroupChanged && computedForCurrentTestGroup) {
+            this.enqueueToRender();
+            return;
+        }
+
+        if (testGroupChanged) {
+            this._bisectingCommitSetByTestGroup = new Map;
+            this._allTestGroupIdSetForCurrentTask = new Set(allTestGroupIdsForCurrentTask);
+        }
+
+        analysisTask.commitSetsFromTestGroupsAndMeasurementSet().then(async (availableCommitSets) => {
+            const commitSetClosestToMiddle = await CommitSetRangeBisector.commitSetClosestToMiddleOfAllCommits(currentTestGroup.requestedCommitSets(), availableCommitSets);
+            this._bisectingCommitSetByTestGroup.set(currentTestGroup, commitSetClosestToMiddle);
+            this.enqueueToRender();
+        });
     }
 
     setAnalysisResults(analysisResults, metric)
@@ -337,9 +368,12 @@ class AnalysisTaskTestGroupPane extends ComponentBase {
         if (currentGroup)
             this._testGroupMap.get(currentGroup).listItem.classList.add('selected');
 
-        if (currentGroup)
+        if (currentGroup) {
             this.part('retry-form').setRepetitionCount(currentGroup.repetitionCount());
+            this.part('bisect-form').setRepetitionCount(currentGroup.repetitionCount());
+        }
         this.content('retry-form').style.display = currentGroup ? null : 'none';
+        this.content('bisect-form').style.display = currentGroup && this._bisectingCommitSetByTestGroup.get(currentGroup) ? null : 'none';
 
         const hideButton = this.content('hide-button');
         hideButton.textContent = currentGroup && currentGroup.isHidden() ? 'Unhide' : 'Hide';
@@ -356,6 +390,7 @@ class AnalysisTaskTestGroupPane extends ComponentBase {
                 <test-group-results-viewer id="results-viewer"></test-group-results-viewer>
                 <test-group-revision-table id="revision-table"></test-group-revision-table>
                 <test-group-form id="retry-form">Retry</test-group-form>
+                <test-group-form id="bisect-form">Bisect</test-group-form>
                 <button id="hide-button">Hide</button>
                 <span id="pending-request-cancel-warning">(cancels pending requests)</span>
             </div>`;
@@ -423,7 +458,7 @@ class AnalysisTaskTestGroupPane extends ComponentBase {
                 margin: 0;
             }
 
-            #retry-form {
+            #retry-form, #bisect-form {
                 display: block;
                 margin: 0.5rem;
             }
@@ -509,6 +544,7 @@ class AnalysisTaskPage extends PageWithHeading {
         groupPane.listenToAction('renameTestGroup', (testGroup, newName) => this._updateTestGroupName(testGroup, newName));
         groupPane.listenToAction('toggleTestGroupVisibility', (testGroup) => this._hideCurrentTestGroup(testGroup));
         groupPane.listenToAction('retryTestGroup', (testGroup, repetitionCount) => this._retryCurrentTestGroup(testGroup, repetitionCount));
+        groupPane.listenToAction('bisectTestGroup', (testGroup, commitSets, repetitionCount) => this._bisectCurrentTestGroup(testGroup, commitSets, repetitionCount));
 
         this.part('cause-list').listenToAction('addItem', (repository, revision) => {
             this._associateCommit('cause', repository, revision);
@@ -793,7 +829,25 @@ class AnalysisTaskPage extends PageWithHeading {
             .then(this._didFetchTestGroups.bind(this), function (error) {
             alert('Failed to create a new test group: ' + error);
         });
-        return this._createTestGroupAfterVerifyingCommitSetList(newName, repetitionCount, commitSetMap);
+    }
+
+    async _bisectCurrentTestGroup(testGroup, commitSets, repetitionCount)
+    {
+        console.assert(testGroup.task());
+        const existingTestGroupNames = new Set((this._testGroups || []).map((testGroup) => testGroup.name()));
+
+        for (let i = 1; i < commitSets.length; i++) {
+            const previousCommitSet = commitSets[i - 1];
+            const currentCommitSet = commitSets[i];
+            const testGroupName = CommitSet.createNameWithoutCollision(CommitSet.diff(previousCommitSet, currentCommitSet), existingTestGroupNames);
+            try {
+                const testGroups = await TestGroup.createAndRefetchTestGroups(testGroup.task(), testGroupName, repetitionCount, [previousCommitSet, currentCommitSet]);
+                await this._didFetchTestGroups(testGroups);
+            } catch(error) {
+                alert('Failed to create a new test group: ' + error);
+                break;
+            }
+        }
     }
 
     _createTestGroupAfterVerifyingCommitSetList(testGroupName, repetitionCount, commitSetMap)