Add a bisect button to automatically schedule bisecting A/B tasks.
[WebKit-https.git] / Websites / perf.webkit.org / unit-tests / commit-set-range-bisector-tests.js
1 'use strict';
2
3 const assert = require('assert');
4
5 require('../tools/js/v3-models.js');
6 const MockModels = require('./resources/mock-v3-models.js').MockModels;
7 const MockRemoteAPI = require('./resources/mock-remote-api.js').MockRemoteAPI;
8
9 describe('CommitSetRangeBisector', () => {
10
11     function makeCommit(id, repository, revision, time, order)
12     {
13         return CommitLog.ensureSingleton(id, {
14             repository,
15             revision,
16             ownsCommits: false,
17             time,
18             order
19         });
20     }
21
22     function sortedCommitSets()
23     {
24         return [
25             CommitSet.ensureSingleton(1, {
26                 revisionItems: [
27                     { commit: makeCommit(1, MockModels.webkit, 'webkit-commit-1', 10), requiresBuild: false },
28                     { commit: makeCommit(11, MockModels.osx, 'osx-commit-1', 1), requiresBuild: false }
29                 ],
30                 customRoots: []}),
31             CommitSet.ensureSingleton(2, {
32                 revisionItems: [
33                     { commit: makeCommit(1, MockModels.webkit, 'webkit-commit-1', 10), requiresBuild: false },
34                     { commit: makeCommit(12, MockModels.osx, 'osx-commit-2', 21), requiresBuild: false }
35                 ],
36                 customRoots: []}),
37             CommitSet.ensureSingleton(3, {
38                 revisionItems: [
39                     { commit: makeCommit(2, MockModels.webkit, 'webkit-commit-2', 20), requiresBuild: false },
40                     { commit: makeCommit(12, MockModels.osx, 'osx-commit-2', 21), requiresBuild: false }
41                 ],
42                 customRoots: []}),
43             CommitSet.ensureSingleton(4, {
44                 revisionItems: [
45                     { commit: makeCommit(3, MockModels.webkit, 'webkit-commit-3', 30), requiresBuild: false },
46                     { commit: makeCommit(13, MockModels.osx, 'osx-commit-3', 31), requiresBuild: false }
47                 ],
48                 customRoots: []}),
49             CommitSet.ensureSingleton(5, {
50                 revisionItems: [
51                     { commit: makeCommit(6, MockModels.webkit, 'webkit-commit-6', 60), requiresBuild: false },
52                     { commit: makeCommit(13, MockModels.osx, 'osx-commit-3', 31), requiresBuild: false }
53                 ],
54                 customRoots: []}),
55         ];
56     }
57
58     function sortedCommitSetsWithoutTimeOrOrder()
59     {
60         return [
61             CommitSet.ensureSingleton(6, {
62                 revisionItems: [
63                     { commit: makeCommit(101, MockModels.webkit, 'webkit-commit-101', 0), requiresBuild: false },
64                     { commit: makeCommit(111, MockModels.osx, 'osx-commit-111', 0), requiresBuild: false }
65                 ],
66                 customRoots: []}),
67             CommitSet.ensureSingleton(7, {
68                 revisionItems: [
69                     { commit: makeCommit(101, MockModels.webkit, 'webkit-commit-101', 0), requiresBuild: false },
70                     { commit: makeCommit(112, MockModels.osx, 'osx-commit-112', 0), requiresBuild: false }
71                 ],
72                 customRoots: []}),
73             CommitSet.ensureSingleton(8, {
74                 revisionItems: [
75                     { commit: makeCommit(102, MockModels.webkit, 'webkit-commit-102', 0), requiresBuild: false },
76                     { commit: makeCommit(112, MockModels.osx, 'osx-commit-112', 0), requiresBuild: false }
77                 ],
78                 customRoots: []}),
79             CommitSet.ensureSingleton(9, {
80                 revisionItems: [
81                     { commit: makeCommit(103, MockModels.webkit, 'webkit-commit-103', 0), requiresBuild: false },
82                     { commit: makeCommit(113, MockModels.osx, 'osx-commit-113', 0), requiresBuild: false }
83                 ],
84                 customRoots: []}),
85             CommitSet.ensureSingleton(10, {
86                 revisionItems: [
87                     { commit: makeCommit(106, MockModels.webkit, 'webkit-commit-106', 0), requiresBuild: false },
88                     { commit: makeCommit(113, MockModels.osx, 'osx-commit-113', 0), requiresBuild: false }
89                 ],
90                 customRoots: []}),
91         ];
92     }
93
94     function commitSetsWithSomeCommitsOnlyHaveOrder()
95     {
96         return [
97             CommitSet.ensureSingleton(11, {
98                 revisionItems: [
99                     { commit: makeCommit(1, MockModels.webkit, 'webkit-commit-1', 10), requiresBuild: false },
100                     { commit: makeCommit(11, MockModels.osx, 'osx-commit-1', 1), requiresBuild: false },
101                     { commit: makeCommit(201, MockModels.ownerRepository, 'owner-commit-1', 0, 1), requiresBuild: false }
102                 ],
103                 customRoots: []}),
104             CommitSet.ensureSingleton(12, {
105                 revisionItems: [
106                     { commit: makeCommit(1, MockModels.webkit, 'webkit-commit-1', 10), requiresBuild: false },
107                     { commit: makeCommit(12, MockModels.osx, 'osx-commit-2', 21), requiresBuild: false },
108                     { commit: makeCommit(202, MockModels.ownerRepository, 'owner-commit-2', 0, 2), requiresBuild: false }
109                 ],
110                 customRoots: []}),
111             CommitSet.ensureSingleton(13, {
112                 revisionItems: [
113                     { commit: makeCommit(2, MockModels.webkit, 'webkit-commit-2', 20), requiresBuild: false },
114                     { commit: makeCommit(12, MockModels.osx, 'osx-commit-2', 21), requiresBuild: false },
115                     { commit: makeCommit(202, MockModels.ownerRepository, 'owner-commit-2', 0, 2), requiresBuild: false }
116                 ],
117                 customRoots: []}),
118             CommitSet.ensureSingleton(14, {
119                 revisionItems: [
120                     { commit: makeCommit(3, MockModels.webkit, 'webkit-commit-3', 30), requiresBuild: false },
121                     { commit: makeCommit(13, MockModels.osx, 'osx-commit-3', 31), requiresBuild: false },
122                     { commit: makeCommit(202, MockModels.ownerRepository, 'owner-commit-2', 0, 2), requiresBuild: false }
123                 ],
124                 customRoots: []}),
125             CommitSet.ensureSingleton(15, {
126                 revisionItems: [
127                     { commit: makeCommit(3, MockModels.webkit, 'webkit-commit-3', 30), requiresBuild: false },
128                     { commit: makeCommit(13, MockModels.osx, 'osx-commit-3', 31), requiresBuild: false },
129                     { commit: makeCommit(203, MockModels.ownerRepository, 'owner-commit-3', 0, 3), requiresBuild: false }
130                 ],
131                 customRoots: []}),
132             CommitSet.ensureSingleton(16, {
133                 revisionItems: [
134                     { commit: makeCommit(6, MockModels.webkit, 'webkit-commit-6', 60), requiresBuild: false },
135                     { commit: makeCommit(13, MockModels.osx, 'osx-commit-3', 31), requiresBuild: false },
136                     { commit: makeCommit(203, MockModels.ownerRepository, 'owner-commit-3', 0, 3), requiresBuild: false }
137                 ],
138                 customRoots: []}),
139         ];
140     }
141
142     function commitSetsWithSomeHaveOwnedCommits()
143     {
144         return [
145             CommitSet.ensureSingleton(11, {
146                 revisionItems: [
147                     { commit: makeCommit(1, MockModels.webkit, 'webkit-commit-1', 10), requiresBuild: false },
148                     { commit: makeCommit(11, MockModels.osx, 'osx-commit-1', 1), requiresBuild: false },
149                     { commit: makeCommit(201, MockModels.ownerRepository, 'owner-commit-1', 0, 1), requiresBuild: false },
150                 ],
151                 customRoots: []}),
152             CommitSet.ensureSingleton(12, {
153                 revisionItems: [
154                     { commit: makeCommit(1, MockModels.webkit, 'webkit-commit-1', 10), requiresBuild: false },
155                     { commit: makeCommit(12, MockModels.osx, 'osx-commit-2', 21), requiresBuild: false },
156                     { commit: makeCommit(202, MockModels.ownerRepository, 'owner-commit-2', 0, 2), requiresBuild: false }
157                 ],
158                 customRoots: []}),
159             CommitSet.ensureSingleton(13, {
160                 revisionItems: [
161                     { commit: makeCommit(2, MockModels.webkit, 'webkit-commit-2', 20), requiresBuild: false },
162                     { commit: makeCommit(12, MockModels.osx, 'osx-commit-2', 21), requiresBuild: false },
163                     { commit: makeCommit(202, MockModels.ownerRepository, 'owner-commit-2', 0, 2), requiresBuild: false }
164                 ],
165                 customRoots: []}),
166             CommitSet.ensureSingleton(14, {
167                 revisionItems: [
168                     { commit: makeCommit(3, MockModels.webkit, 'webkit-commit-3', 30), requiresBuild: false },
169                     { commit: makeCommit(13, MockModels.osx, 'osx-commit-3', 31), requiresBuild: false },
170                     { commit: makeCommit(202, MockModels.ownerRepository, 'owner-commit-2', 0, 2), requiresBuild: false },
171                     { commit: makeCommit(302, MockModels.ownedRepository, 'owned-commit-2', 0, 2), requiresBuild: false }
172                 ],
173                 customRoots: []}),
174             CommitSet.ensureSingleton(15, {
175                 revisionItems: [
176                     { commit: makeCommit(3, MockModels.webkit, 'webkit-commit-3', 30), requiresBuild: false },
177                     { commit: makeCommit(13, MockModels.osx, 'osx-commit-3', 31), requiresBuild: false },
178                     { commit: makeCommit(203, MockModels.ownerRepository, 'owner-commit-3', 0, 3), requiresBuild: false }
179                 ],
180                 customRoots: []}),
181             CommitSet.ensureSingleton(16, {
182                 revisionItems: [
183                     { commit: makeCommit(6, MockModels.webkit, 'webkit-commit-6', 60), requiresBuild: false },
184                     { commit: makeCommit(13, MockModels.osx, 'osx-commit-3', 31), requiresBuild: false },
185                     { commit: makeCommit(203, MockModels.ownerRepository, 'owner-commit-3', 0, 3), requiresBuild: false }
186                 ],
187                 customRoots: []}),
188         ];
189     }
190
191     function commitSetsWithSomeCommitsNotMonotonicallyIncrease()
192     {
193         return [
194             CommitSet.ensureSingleton(17, {
195                 revisionItems: [
196                     { commit: makeCommit(1, MockModels.webkit, 'webkit-commit-1', 10), requiresBuild: false },
197                     { commit: makeCommit(12, MockModels.osx, 'osx-commit-2', 0, 2), requiresBuild: false }
198                 ],
199                 customRoots: []}),
200             CommitSet.ensureSingleton(18, {
201                 revisionItems: [
202                     { commit: makeCommit(1, MockModels.webkit, 'webkit-commit-1', 10), requiresBuild: false },
203                     { commit: makeCommit(11, MockModels.osx, 'osx-commit-1', 0, 1), requiresBuild: false }
204                 ],
205                 customRoots: []}),
206             CommitSet.ensureSingleton(19, {
207                 revisionItems: [
208                     { commit: makeCommit(2, MockModels.webkit, 'webkit-commit-2', 20), requiresBuild: false },
209                     { commit: makeCommit(11, MockModels.osx, 'osx-commit-1', 0, 1), requiresBuild: false }
210                 ],
211                 customRoots: []}),
212             CommitSet.ensureSingleton(20, {
213                 revisionItems: [
214                     { commit: makeCommit(2, MockModels.webkit, 'webkit-commit-2', 20), requiresBuild: false },
215                     { commit: makeCommit(12, MockModels.osx, 'osx-commit-2', 0, 2), requiresBuild: false }
216                 ],
217                 customRoots: []}),
218             CommitSet.ensureSingleton(21, {
219                 revisionItems: [
220                     { commit: makeCommit(3, MockModels.webkit, 'webkit-commit-3', 30), requiresBuild: false },
221                     { commit: makeCommit(13, MockModels.osx, 'osx-commit-3', 0, 3), requiresBuild: false }
222                 ],
223                 customRoots: []}),
224             CommitSet.ensureSingleton(22, {
225                 revisionItems: [
226                     { commit: makeCommit(6, MockModels.webkit, 'webkit-commit-6', 60), requiresBuild: false },
227                     { commit: makeCommit(11, MockModels.osx, 'osx-commit-1', 0, 1), requiresBuild: false }
228                 ],
229                 customRoots: []}),
230         ];
231     }
232
233     function createRoot()
234     {
235         return UploadedFile.ensureSingleton(456, {'createdAt': new Date('2017-05-01T21:03:27Z'), 'filename': 'root.dat', 'extension': '.dat', 'author': 'some user',
236             size: 16452234, sha256: '03eed7a8494ab8794c44b7d4308e55448fc56f4d6c175809ba968f78f656d58d'});
237     }
238
239     function commitSetWithRoot()
240     {
241         return CommitSet.ensureSingleton(15, {
242             revisionItems: [{ commit: makeCommit(1, MockModels.webkit, 'webkit-commit-1', 1), requiresBuild: false }],
243             customRoots: [createRoot()]
244         });
245     }
246
247     function commitSet()
248     {
249         return CommitSet.ensureSingleton(16, {
250             revisionItems: [{ commit: makeCommit(1, MockModels.webkit, 'webkit-commit-1', 1), requiresBuild: false }],
251             customRoots: []
252         });
253     }
254
255     function commitSetsWithNoCommonRepository() {
256         return [
257             CommitSet.ensureSingleton(1, {
258                 revisionItems: [
259                     { commit: makeCommit(1, MockModels.webkit, 'webkit-commit-1', 1), requiresBuild: false },
260                     { commit: makeCommit(11, MockModels.osx, 'osx-commit-1', 1), requiresBuild: false }
261                 ],
262                 customRoots: []}),
263             CommitSet.ensureSingleton(2, {
264                 revisionItems: [
265                     { commit: makeCommit(2, MockModels.webkit, 'webkit-commit-1', 1), requiresBuild: false },
266                     { commit: makeCommit(31, MockModels.ios, 'ios-commit-1', 1), requiresBuild: false },
267                 ],
268                 customRoots: []})
269         ];
270     }
271
272     describe('commitSetClosestToMiddleOfAllCommits', () => {
273         MockModels.inject();
274         const requests = MockRemoteAPI.inject();
275
276         it('should return "null" if no common repository found', async () => {
277             const middleCommitSet = await CommitSetRangeBisector.commitSetClosestToMiddleOfAllCommits(commitSetsWithNoCommonRepository().splice(0, 2));
278             assert.equal(middleCommitSet, null);
279         });
280
281         it('should return "null" to bisect commit set with root', async () => {
282             const middleCommitSet = await CommitSetRangeBisector.commitSetClosestToMiddleOfAllCommits([commitSet(), commitSetWithRoot()], [commitSet(), commitSetWithRoot()]);
283             assert.equal(middleCommitSet, null);
284         });
285
286         it('should return "null" if no repository with time or order is found', async () => {
287             const allCommitSets = sortedCommitSetsWithoutTimeOrOrder();
288             const startCommitSet = allCommitSets[0];
289             const endCommitSet = allCommitSets[allCommitSets.length - 1];
290             const middleCommitSet = await CommitSetRangeBisector.commitSetClosestToMiddleOfAllCommits([startCommitSet, endCommitSet], allCommitSets);
291             assert.equal(middleCommitSet, null);
292         });
293
294         it('should throw exception when failed to fetch commit log', async () => {
295             const allCommitSets = sortedCommitSets();
296             const startCommitSet = allCommitSets[0];
297             const endCommitSet = allCommitSets[allCommitSets.length - 1];
298             const promise = CommitSetRangeBisector.commitSetClosestToMiddleOfAllCommits([startCommitSet, endCommitSet], allCommitSets);
299             const rejectReason = '404';
300             requests[0].reject(rejectReason);
301             let exceptionRaised = false;
302             try {
303                 await promise;
304             } catch (error) {
305                 exceptionRaised = true;
306                 assert.equal(error, rejectReason);
307             }
308             assert.ok(exceptionRaised);
309         });
310
311         it('should return "null" if no commit set is found other than the commit sets that define the range', async () => {
312             const allCommitSets = sortedCommitSets();
313             const startCommitSet = allCommitSets[0];
314             const endCommitSet = allCommitSets[allCommitSets.length - 1];
315             const webkitId = MockModels.webkit.id();
316             const osxId = MockModels.osx.id();
317             const promise = CommitSetRangeBisector.commitSetClosestToMiddleOfAllCommits([startCommitSet, endCommitSet], [startCommitSet, endCommitSet]);
318             assert.equal(requests.length, 2);
319             assert.equal(requests[0].url, '/api/commits/9/?precedingRevision=osx-commit-1&lastRevision=osx-commit-3');
320             assert.equal(requests[1].url, '/api/commits/11/?precedingRevision=webkit-commit-1&lastRevision=webkit-commit-6');
321             requests[0].resolve({
322                 'commits': [
323                     {
324                         repository: osxId,
325                         id: 12,
326                         revision: 'osx-commit-2',
327                         ownsCommits: false,
328                         time: 21
329                     },
330                     {
331                         repository: osxId,
332                         id: 13,
333                         revision: 'osx-commit-3',
334                         ownsCommits: false,
335                         time: 31
336                     }
337                 ]
338             });
339             requests[1].resolve({
340                 'commits': [
341                     {
342                         repository: webkitId,
343                         id: 2,
344                         revision: 'webkit-commit-2',
345                         ownsCommits: false,
346                         time: 20
347                     },
348                     {
349                         repository: webkitId,
350                         id: 3,
351                         revision: 'webkit-commit-3',
352                         ownsCommits: false,
353                         time: 30
354                     },
355                     {
356                         repository: webkitId,
357                         id: 4,
358                         revision: 'webkit-commit-4',
359                         ownsCommits: false,
360                         time: 40
361                     },
362                     {
363                         repository: webkitId,
364                         id: 5,
365                         revision: 'webkit-commit-5',
366                         ownsCommits: false,
367                         time: 50
368                     },
369                     {
370                         repository: webkitId,
371                         id: 6,
372                         revision: 'webkit-commit-6',
373                         ownsCommits: false,
374                         time: 60
375                     },
376                 ]
377             });
378
379             assert.equal(await promise, null);
380         });
381
382         it('should return bisecting commit set point closest to the middle of revision range', async () => {
383             const allCommitSets = sortedCommitSets();
384             const startCommitSet = allCommitSets[0];
385             const endCommitSet = allCommitSets[allCommitSets.length - 1];
386             const promise = CommitSetRangeBisector.commitSetClosestToMiddleOfAllCommits([startCommitSet, endCommitSet], allCommitSets);
387             assert.equal(requests.length, 2);
388             const osxFetchRequest = requests.find((fetch_request) => fetch_request.url === '/api/commits/9/?precedingRevision=osx-commit-1&lastRevision=osx-commit-3');
389             const webkitFetchRequest = requests.find((fetch_request) => fetch_request.url === '/api/commits/11/?precedingRevision=webkit-commit-1&lastRevision=webkit-commit-6');
390             const webkitId = MockModels.webkit.id();
391             const osxId = MockModels.osx.id();
392             webkitFetchRequest.resolve({
393                 'commits': [
394                     {
395                         repository: webkitId,
396                         id: 2,
397                         revision: 'webkit-commit-2',
398                         ownsCommits: false,
399                         time: 20
400                     },
401                     {
402                         repository: webkitId,
403                         id: 3,
404                         revision: 'webkit-commit-3',
405                         ownsCommits: false,
406                         time: 30
407                     },
408                     {
409                         repository: webkitId,
410                         id: 4,
411                         revision: 'webkit-commit-4',
412                         ownsCommits: false,
413                         time: 40
414                     },
415                     {
416                         repository: webkitId,
417                         id: 5,
418                         revision: 'webkit-commit-5',
419                         ownsCommits: false,
420                         time: 50
421                     },
422                     {
423                         repository: webkitId,
424                         id: 6,
425                         revision: 'webkit-commit-6',
426                         ownsCommits: false,
427                         time: 60
428                     },
429                 ]
430             });
431             osxFetchRequest.resolve({
432                 'commits': [
433                     {
434                         repository: osxId,
435                         id: 12,
436                         revision: 'osx-commit-2',
437                         ownsCommits: false,
438                         time: 21
439                     },
440                     {
441                         repository: osxId,
442                         id: 13,
443                         revision: 'osx-commit-3',
444                         ownsCommits: false,
445                         time: 31
446                     }
447                 ]
448             });
449
450             assert.equal(await promise, allCommitSets[3]);
451         });
452
453         it('should return same bisection point even when two commit sets from original commit set have reverse order', async () => {
454             const allCommitSets = sortedCommitSets();
455             const startCommitSet = allCommitSets[0];
456             const endCommitSet = allCommitSets[allCommitSets.length - 1];
457             const promise = CommitSetRangeBisector.commitSetClosestToMiddleOfAllCommits([endCommitSet, startCommitSet], allCommitSets);
458             const webkitId = MockModels.webkit.id();
459             const osxId = MockModels.osx.id();
460             requests[0].resolve({
461                 'commits': [
462                     {
463                         repository: webkitId,
464                         id: 2,
465                         revision: 'webkit-commit-2',
466                         ownsCommits: false,
467                         time: 20
468                     },
469                     {
470                         repository: webkitId,
471                         id: 3,
472                         revision: 'webkit-commit-3',
473                         ownsCommits: false,
474                         time: 30
475                     },
476                     {
477                         repository: webkitId,
478                         id: 4,
479                         revision: 'webkit-commit-4',
480                         ownsCommits: false,
481                         time: 40
482                     },
483                     {
484                         repository: webkitId,
485                         id: 5,
486                         revision: 'webkit-commit-5',
487                         ownsCommits: false,
488                         time: 50
489                     },
490                     {
491                         repository: webkitId,
492                         id: 6,
493                         revision: 'webkit-commit-6',
494                         ownsCommits: false,
495                         time: 60
496                     },
497                 ]
498             });
499             requests[1].resolve({
500                 'commits': [
501                     {
502                         repository: osxId,
503                         id: 12,
504                         revision: 'osx-commit-2',
505                         ownsCommits: false,
506                         time: 21
507                     },
508                     {
509                         repository: osxId,
510                         id: 13,
511                         revision: 'osx-commit-3',
512                         ownsCommits: false,
513                         time: 31
514                     }
515                 ]
516             });
517
518             assert.equal(await promise, allCommitSets[3]);
519         });
520
521         it('should use commits with order as fallback when multiple commit sets found for the commit that is closest to the middle of commits with time', async () => {
522             const allCommitSets = commitSetsWithSomeCommitsOnlyHaveOrder();
523             const startCommitSet = allCommitSets[0];
524             const endCommitSet = allCommitSets[allCommitSets.length - 1];
525             const promise = CommitSetRangeBisector.commitSetClosestToMiddleOfAllCommits([startCommitSet, endCommitSet], allCommitSets);
526             const webkitId = MockModels.webkit.id();
527             const osxId = MockModels.osx.id();
528             const ownerRepositoryId = MockModels.ownerRepository.id();
529
530             assert.equal(requests.length, 3);
531             assert.equal(requests[0].url, '/api/commits/9/?precedingRevision=osx-commit-1&lastRevision=osx-commit-3');
532             assert.equal(requests[1].url, '/api/commits/111/?precedingRevision=owner-commit-1&lastRevision=owner-commit-3');
533             assert.equal(requests[2].url, '/api/commits/11/?precedingRevision=webkit-commit-1&lastRevision=webkit-commit-6');
534
535             requests[0].resolve({
536                 'commits': [
537                     {
538                         repository: osxId,
539                         id: 12,
540                         revision: 'osx-commit-2',
541                         ownsCommits: false,
542                         time: 21
543                     },
544                     {
545                         repository: osxId,
546                         id: 13,
547                         revision: 'osx-commit-3',
548                         ownsCommits: false,
549                         time: 31
550                     }
551                 ]
552             });
553
554             requests[1].resolve({
555                 'commits': [
556                     {
557                         repository: ownerRepositoryId,
558                         id: 202,
559                         revision: 'owner-commit-2',
560                         ownsCommits: false,
561                         time: 0,
562                         order: 2
563                     },
564                     {
565                         repository: ownerRepositoryId,
566                         id: 203,
567                         revision: 'owner-commit-3',
568                         ownsCommits: false,
569                         time: 0,
570                         order: 3
571                     }
572                 ]
573             });
574
575             requests[2].resolve({
576                 'commits': [
577                     {
578                         repository: webkitId,
579                         id: 2,
580                         revision: 'webkit-commit-2',
581                         ownsCommits: false,
582                         time: 20
583                     },
584                     {
585                         repository: webkitId,
586                         id: 3,
587                         revision: 'webkit-commit-3',
588                         ownsCommits: false,
589                         time: 30
590                     },
591                     {
592                         repository: webkitId,
593                         id: 4,
594                         revision: 'webkit-commit-4',
595                         ownsCommits: false,
596                         time: 40
597                     },
598                     {
599                         repository: webkitId,
600                         id: 5,
601                         revision: 'webkit-commit-5',
602                         ownsCommits: false,
603                         time: 50
604                     },
605                     {
606                         repository: webkitId,
607                         id: 6,
608                         revision: 'webkit-commit-6',
609                         ownsCommits: false,
610                         time: 60
611                     },
612                 ]
613             });
614             assert.equal(await promise, allCommitSets[3]);
615         });
616
617         it('should filter out commit set with owned commit', async () => {
618             const allCommitSets = commitSetsWithSomeHaveOwnedCommits();
619             const startCommitSet = allCommitSets[0];
620             const endCommitSet = allCommitSets[allCommitSets.length - 1];
621             const promise = CommitSetRangeBisector.commitSetClosestToMiddleOfAllCommits([startCommitSet, endCommitSet], allCommitSets);
622             const webkitId = MockModels.webkit.id();
623             const osxId = MockModels.osx.id();
624             const ownerRepositoryId = MockModels.ownerRepository.id();
625
626             assert.equal(requests.length, 3);
627             assert.equal(requests[0].url, '/api/commits/9/?precedingRevision=osx-commit-1&lastRevision=osx-commit-3');
628             assert.equal(requests[1].url, '/api/commits/111/?precedingRevision=owner-commit-1&lastRevision=owner-commit-3');
629             assert.equal(requests[2].url, '/api/commits/11/?precedingRevision=webkit-commit-1&lastRevision=webkit-commit-6');
630
631             requests[0].resolve({
632                 'commits': [
633                     {
634                         repository: osxId,
635                         id: 12,
636                         revision: 'osx-commit-2',
637                         ownsCommits: false,
638                         time: 21
639                     },
640                     {
641                         repository: osxId,
642                         id: 13,
643                         revision: 'osx-commit-3',
644                         ownsCommits: false,
645                         time: 31
646                     }
647                 ]
648             });
649
650             requests[1].resolve({
651                 'commits': [
652                     {
653                         repository: ownerRepositoryId,
654                         id: 202,
655                         revision: 'owner-commit-2',
656                         ownsCommits: true,
657                         time: 0,
658                         order: 2
659                     },
660                     {
661                         repository: ownerRepositoryId,
662                         id: 203,
663                         revision: 'owner-commit-3',
664                         ownsCommits: true,
665                         time: 0,
666                         order: 3
667                     }
668                 ]
669             });
670
671             requests[2].resolve({
672                 'commits': [
673                     {
674                         repository: webkitId,
675                         id: 2,
676                         revision: 'webkit-commit-2',
677                         ownsCommits: false,
678                         time: 20
679                     },
680                     {
681                         repository: webkitId,
682                         id: 3,
683                         revision: 'webkit-commit-3',
684                         ownsCommits: false,
685                         time: 30
686                     },
687                     {
688                         repository: webkitId,
689                         id: 4,
690                         revision: 'webkit-commit-4',
691                         ownsCommits: false,
692                         time: 40
693                     },
694                     {
695                         repository: webkitId,
696                         id: 5,
697                         revision: 'webkit-commit-5',
698                         ownsCommits: false,
699                         time: 50
700                     },
701                     {
702                         repository: webkitId,
703                         id: 6,
704                         revision: 'webkit-commit-6',
705                         ownsCommits: false,
706                         time: 60
707                     },
708                 ]
709             });
710             assert.equal(await promise, allCommitSets[4]);
711         });
712
713         it('should still work even some commits do not monotonically increasing', async () => {
714             const allCommitSets = commitSetsWithSomeCommitsNotMonotonicallyIncrease();
715             const startCommitSet = allCommitSets[0];
716             const endCommitSet = allCommitSets[allCommitSets.length - 1];
717             const promise = CommitSetRangeBisector.commitSetClosestToMiddleOfAllCommits([startCommitSet, endCommitSet], allCommitSets);
718             const webkitId = MockModels.webkit.id();
719             const osxId = MockModels.osx.id();
720
721             assert.equal(requests.length, 2);
722             assert.equal(requests[0].url, '/api/commits/9/?precedingRevision=osx-commit-1&lastRevision=osx-commit-2');
723             assert.equal(requests[1].url, '/api/commits/11/?precedingRevision=webkit-commit-1&lastRevision=webkit-commit-6');
724
725             requests[0].resolve({
726                 'commits': [
727                     {
728                         repository: osxId,
729                         id: 12,
730                         revision: 'osx-commit-2',
731                         ownsCommits: false,
732                         time: 0,
733                         order: 2
734                     }
735                 ]
736             });
737             requests[1].resolve({
738                 'commits': [
739                     {
740                         repository: webkitId,
741                         id: 2,
742                         revision: 'webkit-commit-2',
743                         ownsCommits: false,
744                         time: 20
745                     },
746                     {
747                         repository: webkitId,
748                         id: 3,
749                         revision: 'webkit-commit-3',
750                         ownsCommits: false,
751                         time: 30
752                     },
753                     {
754                         repository: webkitId,
755                         id: 4,
756                         revision: 'webkit-commit-4',
757                         ownsCommits: false,
758                         time: 40
759                     },
760                     {
761                         repository: webkitId,
762                         id: 5,
763                         revision: 'webkit-commit-5',
764                         ownsCommits: false,
765                         time: 50
766                     },
767                     {
768                         repository: webkitId,
769                         id: 6,
770                         revision: 'webkit-commit-6',
771                         ownsCommits: false,
772                         time: 60
773                     },
774                 ]
775             });
776
777             assert.equal(await promise, allCommitSets[3]);
778         });
779     });
780 });