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