Fix unit test and bug fix for 'pull-os-versions.js' script.
[WebKit.git] / Websites / perf.webkit.org / server-tests / tools-os-build-fetcher-tests.js
1 'use strict';
2
3 const assert = require('assert');
4
5 const OSBuildFetcher = require('../tools/js/os-build-fetcher.js').OSBuildFetcher;
6 const MockRemoteAPI = require('../unit-tests/resources/mock-remote-api.js').MockRemoteAPI;
7 const TestServer = require('./resources/test-server.js');
8 const addSlaveForReport = require('./resources/common-operations.js').addSlaveForReport;
9 const prepareServerTest = require('./resources/common-operations.js').prepareServerTest;
10 const MockSubprocess = require('./resources/mock-subprocess.js').MockSubprocess;
11 const MockLogger = require('./resources/mock-logger.js').MockLogger;
12
13
14 describe('OSBuildFetcher', function() {
15     this.timeout(5000);
16     TestServer.inject();
17
18     beforeEach(function () {
19         MockRemoteAPI.reset('http://build.webkit.org');
20         MockSubprocess.reset();
21     });
22
23     const emptyReport = {
24         'slaveName': 'someSlave',
25         'slavePassword': 'somePassword',
26     };
27
28     const slaveAuth = {
29         'name': 'someSlave',
30         'password': 'somePassword'
31     };
32
33     const subCommitWithWebKit = {
34         'WebKit': {'revision': '141978'}
35     };
36
37     const anotherSubCommitWithWebKit = {
38         'WebKit': {'revision': '141999'}
39     };
40
41     const anotherSubCommitWithWebKitAndJavaScriptCore = {
42         'WebKit': {'revision': '142000'},
43         'JavaScriptCore': {'revision': '142000'}
44     };
45
46     const osxCommit = {
47         'repository': 'OSX',
48         'revision': 'Sierra16D32',
49         'order': 1603003200
50     };
51
52     const anotherOSXCommit = {
53         'repository': 'OSX',
54         'revision': 'Sierra16E32',
55         'order': 1603003200
56     };
57
58
59     const config = {
60         'name': 'OSX',
61         'customCommands': [
62             {
63                 'command': ['list', 'all osx 16Dxx builds'],
64                 'subCommitCommand': ['list', 'subCommit', 'for', 'revision'],
65                 'linesToIgnore': '^\\.*$',
66                 'minRevision': 'Sierra16D0',
67                 'maxRevision': 'Sierra16D999'
68             },
69             {
70                 'command': ['list', 'all osx 16Exx builds'],
71                 'subCommitCommand': ['list', 'subCommit', 'for', 'revision'],
72                 'linesToIgnore': '^\\.*$',
73                 'minRevision': 'Sierra16E0',
74                 'maxRevision': 'Sierra16E999'
75             }
76         ]
77     };
78
79
80     const configWithoutSubCommitCommand = {
81         'name': 'OSX',
82         'customCommands': [
83             {
84                 'command': ['list', 'all osx 16Dxx builds'],
85                 'linesToIgnore': '^\\.*$',
86                 'minRevision': 'Sierra16D0',
87                 'maxRevision': 'Sierra16D999'
88             },
89             {
90                 'command': ['list', 'all osx 16Exx builds'],
91                 'linesToIgnore': '^\\.*$',
92                 'minRevision': 'Sierra16E0',
93                 'maxRevision': 'Sierra16E999'
94             }
95         ]
96     };
97
98     describe('OSBuilderFetcher._computeOrder', () => {
99         it('should calculate the right order for a given valid revision', () => {
100             const fetcher = new OSBuildFetcher();
101             assert.equal(fetcher._computeOrder('Sierra16D32'), 1603003200);
102             assert.equal(fetcher._computeOrder('16D321'), 1603032100);
103             assert.equal(fetcher._computeOrder('16d321'), 1603032100);
104             assert.equal(fetcher._computeOrder('16D321z'), 1603032126);
105             assert.equal(fetcher._computeOrder('16d321Z'), 1603032126);
106             assert.equal(fetcher._computeOrder('10.12.3 16D32'), 1603003200);
107             assert.equal(fetcher._computeOrder('10.12.3 Sierra16D32'), 1603003200);
108         });
109
110         it('should throw assertion error when given a invalid revision', () => {
111             const fetcher = new OSBuildFetcher();
112             assert.throws(() => fetcher._computeOrder('invalid'), (error) => error.name == 'AssertionError');
113             assert.throws(() => fetcher._computeOrder(''), (error) => error.name == 'AssertionError');
114             assert.throws(() => fetcher._computeOrder('16'), (error) => error.name == 'AssertionError');
115             assert.throws(() => fetcher._computeOrder('16D'), (error) => error.name == 'AssertionError');
116             assert.throws(() => fetcher._computeOrder('123'), (error) => error.name == 'AssertionError');
117             assert.throws(() => fetcher._computeOrder('D123'), (error) => error.name == 'AssertionError');
118             assert.throws(() => fetcher._computeOrder('123z'), (error) => error.name == 'AssertionError');
119             assert.throws(() => fetcher._computeOrder('16[163'), (error) => error.name == 'AssertionError');
120             assert.throws(() => fetcher._computeOrder('16D163['), (error) => error.name == 'AssertionError');
121         })
122     });
123
124     describe('OSBuilderFetcher._commitsForAvailableBuilds', () => {
125         it('should only return commits whose orders are higher than specified order', () => {
126             const logger = new MockLogger;
127             const fetchter = new OSBuildFetcher(null, null, null, MockSubprocess, logger);
128             const waitingForInvocationPromise = MockSubprocess.waitingForInvocation();
129             const fetchCommitsPromise = fetchter._commitsForAvailableBuilds('OSX', ['list', 'build1'], '^\\.*$', 1604000000);
130
131             return waitingForInvocationPromise.then(() => {
132                 assert.equal(MockSubprocess.invocations.length, 1);
133                 assert.deepEqual(MockSubprocess.invocations[0].command, ['list', 'build1']);
134                 MockSubprocess.invocations[0].resolve('16D321\n16E321z\n\n16F321');
135                 return fetchCommitsPromise;
136             }).then((results) => {
137                 assert.equal(results.length, 2);
138                 assert.deepEqual(results[0], {repository: 'OSX', order: 1604032126, revision: '16E321z'});
139                 assert.deepEqual(results[1], {repository: 'OSX', order: 1605032100, revision: '16F321'});
140             });
141         });
142     });
143
144     describe('OSBuildFetcher._addSubCommitsForBuild', () => {
145         it('should add sub-commit info for commits', () => {
146             const logger = new MockLogger;
147             const fetchter = new OSBuildFetcher(null, null, null, MockSubprocess, logger);
148             const waitingForInvocationPromise = MockSubprocess.waitingForInvocation();
149             const addSubCommitPromise = fetchter._addSubCommitsForBuild([osxCommit, anotherOSXCommit], ['subCommit', 'for', 'revision']);
150
151             return waitingForInvocationPromise.then(() => {
152                 assert.equal(MockSubprocess.invocations.length, 1);
153                 assert.deepEqual(MockSubprocess.invocations[0].command, ['subCommit', 'for', 'revision', 'Sierra16D32']);
154                 MockSubprocess.invocations[0].resolve(JSON.stringify(subCommitWithWebKit));
155                 MockSubprocess.reset();
156                 return MockSubprocess.waitingForInvocation();
157             }).then(() => {
158                 assert.equal(MockSubprocess.invocations.length, 1);
159                 assert.deepEqual(MockSubprocess.invocations[0].command, ['subCommit', 'for', 'revision', 'Sierra16E32']);
160                 MockSubprocess.invocations[0].resolve(JSON.stringify(anotherSubCommitWithWebKit));
161                 return addSubCommitPromise;
162             }).then((results) => {
163                 assert.equal(results.length, 2);
164                 assert.equal(results[0]['repository'], osxCommit['repository']);
165                 assert.equal(results[0]['revision'], osxCommit['revision']);
166                 assert.deepEqual(results[0]['subCommits'], subCommitWithWebKit);
167                 assert.equal(results[1]['repository'], anotherOSXCommit['repository']);
168                 assert.equal(results[1]['revision'], anotherOSXCommit['revision']);
169                 assert.deepEqual(results[1]['subCommits'], anotherSubCommitWithWebKit);
170             });
171         });
172
173         it('should fail if the command to get sub-commit info fails', () => {
174             const logger = new MockLogger;
175             const fetchter = new OSBuildFetcher(null, null, null, MockSubprocess, logger);
176             const waitingForInvocationPromise = MockSubprocess.waitingForInvocation();
177             const addSubCommitPromise = fetchter._addSubCommitsForBuild([osxCommit], ['subCommit', 'for', 'revision'])
178
179             return waitingForInvocationPromise.then(() => {
180                 assert.equal(MockSubprocess.invocations.length, 1);
181                 assert.deepEqual(MockSubprocess.invocations[0].command, ['subCommit', 'for', 'revision', 'Sierra16D32']);
182                 MockSubprocess.invocations[0].reject('Failed getting sub-commit');
183
184                 return addSubCommitPromise.then(() => {
185                     assert(false, 'should never be reached');
186                 }, (error_output) => {
187                     assert(error_output);
188                     assert.equal(error_output, 'Failed getting sub-commit');
189                 });
190             });
191         });
192
193
194         it('should fail if entries in sub-commits does not contain revision', () => {
195             const logger = new MockLogger;
196             const fetchter = new OSBuildFetcher(null, null, null, MockSubprocess, logger);
197             const waitingForInvocationPromise = MockSubprocess.waitingForInvocation();
198             const addSubCommitPromise = fetchter._addSubCommitsForBuild([osxCommit], ['subCommit', 'for', 'revision'])
199
200             return waitingForInvocationPromise.then(() => {
201                 assert.equal(MockSubprocess.invocations.length, 1);
202                 assert.deepEqual(MockSubprocess.invocations[0].command, ['subCommit', 'for', 'revision', 'Sierra16D32']);
203                 MockSubprocess.invocations[0].resolve('{"WebKit":{"RandomKey": "RandomValue"}}');
204
205                 return addSubCommitPromise.then(() => {
206                     assert(false, 'should never be reached');
207                 }, (error_output) => {
208                     assert(error_output);
209                     assert.equal(error_output.name, 'AssertionError');
210                 });
211             });
212         })
213     })
214
215     describe('OSBuildFetcher.fetchAndReportNewBuilds', () => {
216
217         beforeEach(function () {
218             TestServer.database().connect({keepAlive: true});
219         });
220
221         afterEach(function () {
222             TestServer.database().disconnect();
223         });
224
225
226         it('should report all build commits with sub-commits', () => {
227             const logger = new MockLogger;
228             const fetchter = new OSBuildFetcher(config, TestServer.remoteAPI(), slaveAuth, MockSubprocess, logger);
229             const db = TestServer.database();
230             let fetchAndReportPromise = null;
231             let fetchAvailableBuildsPromise = null;
232
233             return addSlaveForReport(emptyReport).then(() => {
234                 return Promise.all([
235                     db.insert('repositories', {'id': 10, 'name': 'OSX'}),
236                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16D67', 'order': 1603006700, 'reported': true}),
237                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16D68', 'order': 1603006800, 'reported': true}),
238                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16D69', 'order': 1603006900, 'reported': false}),
239                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16E32', 'order': 1604003200, 'reported': true}),
240                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16E33', 'order': 1604003300, 'reported': true}),
241                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16E33g', 'order': 1604003307, 'reported': true})]);
242             }).then(() => {
243                 return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1603000000&to=1603099900');
244             }).then((result) => {
245                 assert.equal(result['commits'].length, 1);
246                 assert.equal(result['commits'][0]['revision'], 'Sierra16D68');
247                 assert.equal(result['commits'][0]['order'], 1603006800);
248                 return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1604000000&to=1604099900');
249             }).then((result) => {
250                 assert.equal(result['commits'].length, 1);
251                 assert.equal(result['commits'][0]['revision'], 'Sierra16E33g');
252                 assert.equal(result['commits'][0]['order'], 1604003307);
253                 const waitingForInvocationPromise = MockSubprocess.waitingForInvocation();
254                 fetchAvailableBuildsPromise = fetchter._fetchAvailableBuilds();
255                 return waitingForInvocationPromise;
256             }).then(() => {
257                 return MockSubprocess.waitingForInvocation();
258             }).then(() => {
259                 MockSubprocess.invocations.sort((invocation, antoherInvocation) => invocation['command'] > antoherInvocation['command']);
260                 assert.equal(MockSubprocess.invocations.length, 2);
261                 assert.deepEqual(MockSubprocess.invocations[0].command, ['list', 'all osx 16Dxx builds']);
262                 assert.deepEqual(MockSubprocess.invocations[1].command, ['list', 'all osx 16Exx builds']);
263                 MockSubprocess.invocations[0].resolve('\n\nSierra16D68\nSierra16D69\n');
264                 MockSubprocess.invocations[1].resolve('\n\nSierra16E32\nSierra16E33\nSierra16E33h\nSierra16E34');
265                 MockSubprocess.reset();
266                 return MockSubprocess.waitingForInvocation();
267             }).then(() => {
268                 MockSubprocess.invocations.sort((invocation, antoherInvocation) => invocation['command'] > antoherInvocation['command']);
269                 assert.equal(MockSubprocess.invocations.length, 2);
270                 assert.deepEqual(MockSubprocess.invocations[0].command, ['list', 'subCommit', 'for', 'revision', 'Sierra16D69']);
271                 assert.deepEqual(MockSubprocess.invocations[1].command, ['list', 'subCommit', 'for', 'revision', 'Sierra16E33h']);
272
273                 MockSubprocess.invocations[0].resolve(JSON.stringify(subCommitWithWebKit));
274                 MockSubprocess.invocations[1].resolve(JSON.stringify(anotherSubCommitWithWebKit));
275                 MockSubprocess.reset();
276                 return MockSubprocess.waitingForInvocation();
277             }).then(() => {
278                 assert.equal(MockSubprocess.invocations.length, 1);
279                 assert.deepEqual(MockSubprocess.invocations[0].command, ['list', 'subCommit', 'for', 'revision', 'Sierra16E34']);
280                 MockSubprocess.invocations[0].resolve(JSON.stringify(anotherSubCommitWithWebKitAndJavaScriptCore));
281                 return fetchAvailableBuildsPromise;
282             }).then((results) => {
283                 assert.equal(results.length, 3);
284                 MockSubprocess.reset();
285                 fetchAndReportPromise = fetchter.fetchAndReportNewBuilds();
286                 return MockSubprocess.waitingForInvocation();
287             }).then(() => {
288                 return MockSubprocess.waitingForInvocation();
289             }).then(() => {
290                 MockSubprocess.invocations.sort((invocation, antoherInvocation) => invocation['command'] > antoherInvocation['command']);
291                 assert.equal(MockSubprocess.invocations.length, 2);
292                 assert.deepEqual(MockSubprocess.invocations[0].command, ['list', 'all osx 16Dxx builds']);
293                 assert.deepEqual(MockSubprocess.invocations[1].command, ['list', 'all osx 16Exx builds']);
294                 MockSubprocess.invocations[0].resolve('\n\nSierra16D68\nSierra16D69\n');
295                 MockSubprocess.invocations[1].resolve('\n\nSierra16E32\nSierra16E33\nSierra16E33h\nSierra16E34');
296                 MockSubprocess.reset();
297                 return MockSubprocess.waitingForInvocation();
298             }).then(() => {
299                 MockSubprocess.invocations.sort((invocation, antoherInvocation) => invocation['command'] > antoherInvocation['command']);
300                 assert.equal(MockSubprocess.invocations.length, 2);
301                 assert.deepEqual(MockSubprocess.invocations[0].command, ['list', 'subCommit', 'for', 'revision', 'Sierra16D69']);
302                 assert.deepEqual(MockSubprocess.invocations[1].command, ['list', 'subCommit', 'for', 'revision', 'Sierra16E33h']);
303
304                 MockSubprocess.invocations[0].resolve(JSON.stringify(subCommitWithWebKit));
305                 MockSubprocess.invocations[1].resolve(JSON.stringify(anotherSubCommitWithWebKit));
306
307                 MockSubprocess.reset();
308                 return MockSubprocess.waitingForInvocation();
309             }).then(() => {
310                 assert.equal(MockSubprocess.invocations.length, 1);
311                 MockSubprocess.invocations[0].resolve(JSON.stringify(anotherSubCommitWithWebKitAndJavaScriptCore));
312                 assert.deepEqual(MockSubprocess.invocations[0].command, ['list', 'subCommit', 'for', 'revision', 'Sierra16E34']);
313
314                 return fetchAndReportPromise;
315             }).then((result) => {
316                 assert.equal(result['status'], 'OK');
317                 return Promise.all([
318                     db.selectRows('repositories', {'name': 'WebKit'}),
319                     db.selectRows('repositories', {'name': 'JavaScriptCore'}),
320                     db.selectRows('commits', {'revision': 'Sierra16D69'}),
321                     db.selectRows('commits', {'revision': 'Sierra16E33h'}),
322                     db.selectRows('commits', {'revision': 'Sierra16E34'})]);
323             }).then((results) => {
324                 const webkitRepository = results[0];
325                 const jscRepository = results[1];
326                 const osxCommit16D69 = results[2];
327                 const osxCommit16E33h = results[3];
328                 const osxCommit16E34 = results[4];
329
330                 assert.equal(webkitRepository.length, 1);
331                 assert.equal(webkitRepository[0]['owner'], 10);
332                 assert.equal(jscRepository.length, 1)
333                 assert.equal(jscRepository[0]['owner'], 10);
334
335                 assert.equal(osxCommit16D69.length, 1);
336                 assert.equal(osxCommit16D69[0]['repository'], 10);
337                 assert.equal(osxCommit16D69[0]['order'], 1603006900);
338
339                 assert.equal(osxCommit16E33h.length, 1);
340                 assert.equal(osxCommit16E33h[0]['repository'], 10);
341                 assert.equal(osxCommit16E33h[0]['order'], 1604003308);
342
343                 assert.equal(osxCommit16E34.length, 1);
344                 assert.equal(osxCommit16E34[0]['repository'], 10);
345                 assert.equal(osxCommit16E34[0]['order'], 1604003400);
346
347                 return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1603000000&to=1603099900');
348             }).then((result) => {
349                 assert.equal(result['commits'].length, 1);
350                 assert.equal(result['commits'][0]['revision'], 'Sierra16D69');
351                 assert.equal(result['commits'][0]['order'], 1603006900);
352
353                 return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1604000000&to=1604099900');
354             }).then((result) => {
355                 assert.equal(result['commits'].length, 1);
356                 assert.equal(result['commits'][0]['revision'], 'Sierra16E34');
357                 assert.equal(result['commits'][0]['order'], 1604003400);
358             });
359         });
360
361         it('should report commits without sub-commits if "subCommitCommand" is not specified in config', () => {
362             const logger = new MockLogger;
363             const fetchter = new OSBuildFetcher(configWithoutSubCommitCommand, TestServer.remoteAPI(), slaveAuth, MockSubprocess, logger);
364             const db = TestServer.database();
365             let fetchAndReportPromise = null;
366             let fetchAvailableBuildsPromise = null;
367
368             return addSlaveForReport(emptyReport).then(() => {
369                 return Promise.all([
370                     db.insert('repositories', {'id': 10, 'name': 'OSX'}),
371                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16D67', 'order': 1603006700, 'reported': true}),
372                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16D68', 'order': 1603006800, 'reported': true}),
373                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16D69', 'order': 1603006900, 'reported': false}),
374                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16E32', 'order': 1604003200, 'reported': true}),
375                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16E33', 'order': 1604003300, 'reported': true}),
376                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16E33g', 'order': 1604003307, 'reported': true})]);
377             }).then(() => {
378                 return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1603000000&to=1603099900');
379             }).then((result) => {
380                 assert.equal(result['commits'].length, 1);
381                 assert.equal(result['commits'][0]['revision'], 'Sierra16D68');
382                 assert.equal(result['commits'][0]['order'], 1603006800);
383
384                 return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1604000000&to=1604099900');
385             }).then((result) => {
386                 assert.equal(result['commits'].length, 1);
387                 assert.equal(result['commits'][0]['revision'], 'Sierra16E33g');
388                 assert.equal(result['commits'][0]['order'], 1604003307);
389                 const waitingForInvocationPromise = MockSubprocess.waitingForInvocation();
390                 fetchAndReportPromise = fetchter.fetchAndReportNewBuilds();
391                 return waitingForInvocationPromise;
392             }).then(() => {
393                 return MockSubprocess.waitingForInvocation();
394             }).then(() => {
395                 MockSubprocess.invocations.sort((invocation, antoherInvocation) => invocation['command'] > antoherInvocation['command']);
396                 assert.equal(MockSubprocess.invocations.length, 2);
397                 assert.deepEqual(MockSubprocess.invocations[0].command, ['list', 'all osx 16Dxx builds']);
398                 assert.deepEqual(MockSubprocess.invocations[1].command, ['list', 'all osx 16Exx builds']);
399                 MockSubprocess.invocations[0].resolve('\n\nSierra16D68\nSierra16D69\n');
400                 MockSubprocess.invocations[1].resolve('\n\nSierra16E32\nSierra16E33\nSierra16E33h\nSierra16E34');
401                 return fetchAndReportPromise;
402             }).then((result) => {
403                 assert.equal(result['status'], 'OK');
404                 return Promise.all([
405                     db.selectRows('repositories', {'name': 'WebKit'}),
406                     db.selectRows('repositories', {'name': 'JavaScriptCore'}),
407                     db.selectRows('commits', {'revision': 'Sierra16D69'}),
408                     db.selectRows('commits', {'revision': 'Sierra16E33h'}),
409                     db.selectRows('commits', {'revision': 'Sierra16E34'})]);
410             }).then((results) => {
411                 const webkitRepository = results[0];
412                 const jscRepository = results[1];
413                 const osxCommit16D69 = results[2];
414                 const osxCommit16E33h = results[3];
415                 const osxCommit16E34 = results[4];
416
417                 assert.equal(webkitRepository.length, 0);
418                 assert.equal(jscRepository.length, 0)
419
420                 assert.equal(osxCommit16D69.length, 1);
421                 assert.equal(osxCommit16D69[0]['repository'], 10);
422                 assert.equal(osxCommit16D69[0]['order'], 1603006900);
423
424                 assert.equal(osxCommit16E33h.length, 1);
425                 assert.equal(osxCommit16E33h[0]['repository'], 10);
426                 assert.equal(osxCommit16E33h[0]['order'], 1604003308);
427
428                 assert.equal(osxCommit16E34.length, 1);
429                 assert.equal(osxCommit16E34[0]['repository'], 10);
430                 assert.equal(osxCommit16E34[0]['order'], 1604003400);
431
432                 return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1603000000&to=1603099900');
433             }).then((result) => {
434                 assert.equal(result['commits'].length, 1);
435                 assert.equal(result['commits'][0]['revision'], 'Sierra16D69');
436                 assert.equal(result['commits'][0]['order'], 1603006900);
437
438                 return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1604000000&to=1604099900');
439             }).then((result) => {
440                 assert.equal(result['commits'].length, 1);
441                 assert.equal(result['commits'][0]['revision'], 'Sierra16E34');
442                 assert.equal(result['commits'][0]['order'], 1604003400);
443             });
444         });
445
446         it('should stop reporting if any custom command fails', () => {
447             const logger = new MockLogger;
448             const fetchter = new OSBuildFetcher(config, TestServer.remoteAPI(), slaveAuth, MockSubprocess, logger);
449             const db = TestServer.database();
450             let fetchAndReportPromise = null;
451
452             return addSlaveForReport(emptyReport).then(() => {
453                 return Promise.all([
454                     db.insert('repositories', {'id': 10, 'name': 'OSX'}),
455                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16D67', 'order': 1603006700, 'reported': true}),
456                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16D68', 'order': 1603006800, 'reported': true}),
457                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16D69', 'order': 1603006900, 'reported': false}),
458                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16E32', 'order': 1604003200, 'reported': true}),
459                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16E33', 'order': 1604003300, 'reported': true}),
460                     db.insert('commits', {'repository': 10, 'revision': 'Sierra16E33g', 'order': 1604003307, 'reported': true})]);
461             }).then(() => {
462                 return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1603000000&to=1603099900');
463             }).then((result) => {
464                 assert.equal(result['commits'].length, 1);
465                 assert.equal(result['commits'][0]['revision'], 'Sierra16D68');
466                 assert.equal(result['commits'][0]['order'], 1603006800);
467
468                 return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1604000000&to=1604099900');
469             }).then((result) => {
470                 assert.equal(result['commits'].length, 1);
471                 assert.equal(result['commits'][0]['revision'], 'Sierra16E33g');
472                 assert.equal(result['commits'][0]['order'], 1604003307);
473
474                 const waitingForInvocationPromise = MockSubprocess.waitingForInvocation();
475                 fetchAndReportPromise = fetchter.fetchAndReportNewBuilds();
476                 return waitingForInvocationPromise;
477             }).then(() => {
478                 return MockSubprocess.waitingForInvocation();
479             }).then(() => {
480                 MockSubprocess.invocations.sort((invocation, antoherInvocation) => invocation['command'] > antoherInvocation['command']);
481                 assert.equal(MockSubprocess.invocations.length, 2);
482                 assert.deepEqual(MockSubprocess.invocations[0].command, ['list', 'all osx 16Dxx builds']);
483                 assert.deepEqual(MockSubprocess.invocations[1].command, ['list', 'all osx 16Exx builds']);
484                 MockSubprocess.invocations[0].resolve('\n\nSierra16D68\nSierra16D69\n');
485                 MockSubprocess.invocations[1].resolve('\n\nSierra16E32\nSierra16E33\nSierra16E33h\nSierra16E34');
486                 MockSubprocess.reset();
487                 return MockSubprocess.waitingForInvocation();
488             }).then(() => {
489                 MockSubprocess.invocations.sort((invocation, antoherInvocation) => invocation['command'] > antoherInvocation['command']);
490                 assert.equal(MockSubprocess.invocations.length, 2);
491                 assert.deepEqual(MockSubprocess.invocations[0].command, ['list', 'subCommit', 'for', 'revision', 'Sierra16D69']);
492                 assert.deepEqual(MockSubprocess.invocations[1].command, ['list', 'subCommit', 'for', 'revision', 'Sierra16E33h']);
493
494                 MockSubprocess.invocations[0].resolve(JSON.stringify(subCommitWithWebKit));
495                 MockSubprocess.invocations[1].resolve(JSON.stringify(anotherSubCommitWithWebKit));
496                 MockSubprocess.reset();
497                 return MockSubprocess.waitingForInvocation();
498             }).then(() => {
499                 assert.equal(MockSubprocess.invocations.length, 1);
500                 assert.deepEqual(MockSubprocess.invocations[0].command, ['list', 'subCommit', 'for', 'revision', 'Sierra16E34']);
501                 MockSubprocess.invocations[0].reject('Command failed');
502
503                 return fetchAndReportPromise.then(() => {
504                     assert(false, 'should never be reached');
505                 }, (error_output) => {
506                     assert(error_output);
507                     assert.equal(error_output, 'Command failed');
508                 });
509             }).then(() => {
510                 return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1603000000&to=1603099900');
511             }).then((result) => {
512                 assert.equal(result['commits'].length, 1);
513                 assert.equal(result['commits'][0]['revision'], 'Sierra16D68');
514                 assert.equal(result['commits'][0]['order'], 1603006800);
515
516                 return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1604000000&to=1604099900');
517             }).then((result) => {
518                 assert.equal(result['commits'].length, 1);
519                 assert.equal(result['commits'][0]['revision'], 'Sierra16E33g');
520                 assert.equal(result['commits'][0]['order'], 1604003307);
521             });
522         })
523     })
524 });