Make dependency injection in unit tests more explicit
[WebKit-https.git] / Websites / perf.webkit.org / unit-tests / buildbot-syncer-tests.js
1 'use strict';
2
3 let assert = require('assert');
4
5 require('../tools/js/v3-models.js');
6 let MockRemoteAPI = require('./resources/mock-remote-api.js').MockRemoteAPI;
7 let MockModels = require('./resources/mock-v3-models.js').MockModels;
8
9 let BuildbotBuildEntry = require('../tools/js/buildbot-syncer.js').BuildbotBuildEntry;
10 let BuildbotSyncer = require('../tools/js/buildbot-syncer.js').BuildbotSyncer;
11
12 function sampleiOSConfig()
13 {
14     return {
15         'shared':
16             {
17                 'arguments': {
18                     'desired_image': {'root': 'iOS'},
19                     'roots_dict': {'rootsExcluding': ['iOS']}
20                 },
21                 'slaveArgument': 'slavename',
22                 'buildRequestArgument': 'build_request_id'
23             },
24         'types': {
25             'speedometer': {
26                 'test': ['Speedometer'],
27                 'arguments': {'test_name': 'speedometer'}
28             },
29             'jetstream': {
30                 'test': ['JetStream'],
31                 'arguments': {'test_name': 'jetstream'}
32             },
33             "dromaeo-dom": {
34                 "test": ["Dromaeo", "DOM Core Tests"],
35                 "arguments": {"tests": "dromaeo-dom"}
36             },
37         },
38         'builders': {
39             'iPhone-bench': {
40                 'builder': 'ABTest-iPhone-RunBenchmark-Tests',
41                 'arguments': { 'forcescheduler': 'ABTest-iPhone-RunBenchmark-Tests-ForceScheduler' }
42             },
43             'iPad-bench': {
44                 'builder': 'ABTest-iPad-RunBenchmark-Tests',
45                 'arguments': { 'forcescheduler': 'ABTest-iPad-RunBenchmark-Tests-ForceScheduler' }
46             }
47         },
48         'configurations': [
49             {'type': 'speedometer', 'builder': 'iPhone-bench', 'platform': 'iPhone'},
50             {'type': 'jetstream', 'builder': 'iPhone-bench', 'platform': 'iPhone'},
51             {'type': 'dromaeo-dom', 'builder': 'iPhone-bench', 'platform': 'iPhone'},
52
53             {'type': 'speedometer', 'builder': 'iPad-bench', 'platform': 'iPad'},
54             {'type': 'jetstream', 'builder': 'iPad-bench', 'platform': 'iPad'},
55         ]
56     };
57 }
58
59 let sampleRootSetData = {
60     'WebKit': {
61         'id': '111127',
62         'time': 1456955807334,
63         'repository': 'WebKit',
64         'revision': '197463',
65     },
66     'Shared': {
67         'id': '111237',
68         'time': 1456931874000,
69         'repository': 'Shared',
70         'revision': '80229',
71     }
72 };
73
74 function createSampleBuildRequest()
75 {
76     let rootSet = RootSet.ensureSingleton('4197', {roots: [
77         {'id': '111127', 'time': 1456955807334, 'repository': MockModels.webkit, 'revision': '197463'},
78         {'id': '111237', 'time': 1456931874000, 'repository': MockModels.sharedRepository, 'revision': '80229'},
79         {'id': '88930', 'time': 0, 'repository': MockModels.ios, 'revision': '13A452'},
80     ]});
81
82     let request = BuildRequest.ensureSingleton('16733', {'rootSet': rootSet, 'status': 'pending'});
83     return request;
84 }
85
86 function samplePendingBuild(buildRequestId)
87 {
88     return {
89         'builderName': 'ABTest-iPad-RunBenchmark-Tests',
90         'builds': [],
91         'properties': [
92             ['build_request_id', buildRequestId || '16733', 'Force Build Form'],
93             ['desired_image', '13A452', 'Force Build Form'],
94             ['owner', '<unknown>', 'Force Build Form'],
95             ['test_name', 'speedometer', 'Force Build Form'],
96             ['reason', 'force build','Force Build Form'],
97             [
98                 'roots_dict',
99                 JSON.stringify(sampleRootSetData),
100                 'Force Build Form'
101             ],
102             ['scheduler', 'ABTest-iPad-RunBenchmark-Tests-ForceScheduler', 'Scheduler']
103         ],
104         'source': {
105             'branch': '',
106             'changes': [],
107             'codebase': 'compiler-rt',
108             'hasPatch': false,
109             'project': '',
110             'repository': '',
111             'revision': ''
112         },
113         'submittedAt': 1458704983
114     };
115 }
116
117 function sampleInProgressBuild()
118 {
119     return {
120         'blame': [],
121         'builderName': 'ABTest-iPad-RunBenchmark-Tests',
122         'currentStep': {
123             'eta': 0.26548067698460565,
124             'expectations': [['output', 845, 1315.0]],
125             'hidden': false,
126             'isFinished': false,
127             'isStarted': true,
128             'logs': [['stdio', 'https://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/614/steps/Some%20step/logs/stdio']],
129             'name': 'Some step',
130             'results': [null,[]],
131             'statistics': {},
132             'step_number': 1,
133             'text': [''],
134             'times': [1458718657.581628, null],
135             'urls': {}
136         },
137         'eta': 6497.991612434387,
138         'logs': [['stdio','https://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/614/steps/shell/logs/stdio']],
139         'number': 614,
140         'properties': [
141             ['build_request_id', '16733', 'Force Build Form'],
142             ['buildername', 'ABTest-iPad-RunBenchmark-Tests', 'Builder'],
143             ['buildnumber', 614, 'Build'],
144             ['desired_image', '13A452', 'Force Build Form'],
145             ['owner', '<unknown>', 'Force Build Form'],
146             ['reason', 'force build', 'Force Build Form'],
147             ['roots_dict', JSON.stringify(sampleRootSetData), 'Force Build Form'],
148             ['scheduler', 'ABTest-iPad-RunBenchmark-Tests-ForceScheduler', 'Scheduler'],
149             ['slavename', 'ABTest-iPad-0', 'BuildSlave'],
150         ],
151         'reason': 'A build was forced by \'<unknown>\': force build',
152         'results': null,
153         'slave': 'ABTest-iPad-0',
154         'sourceStamps': [{'branch': '', 'changes': [], 'codebase': 'compiler-rt', 'hasPatch': false, 'project': '', 'repository': '', 'revision': ''}],
155         'steps': [
156             {
157                 'eta': null,
158                 'expectations': [['output',2309,2309.0]],
159                 'hidden': false,
160                 'isFinished': true,
161                 'isStarted': true,
162                 'logs': [['stdio', 'https://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/614/steps/shell/logs/stdio']],
163                 'name': 'Finished step',
164                 'results': [0, []],
165                 'statistics': {},
166                 'step_number': 0,
167                 'text': [''],
168                 'times': [1458718655.419865, 1458718655.453633],
169                 'urls': {}
170             },
171             {
172                 'eta': 0.26548067698460565,
173                 'expectations': [['output', 845, 1315.0]],
174                 'hidden': false,
175                 'isFinished': false,
176                 'isStarted': true,
177                 'logs': [['stdio', 'https://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/614/steps/Some%20step/logs/stdio']],
178                 'name': 'Some step',
179                 'results': [null,[]],
180                 'statistics': {},
181                 'step_number': 1,
182                 'text': [''],
183                 'times': [1458718657.581628, null],
184                 'urls': {}
185             },
186             {
187                 'eta': null,
188                 'expectations': [['output', null, null]],
189                 'hidden': false,
190                 'isFinished': false,
191                 'isStarted': false,
192                 'logs': [],
193                 'name': 'Some other step',
194                 'results': [null, []],
195                 'statistics': {},
196                 'step_number': 2,
197                 'text': [],
198                 'times': [null, null],
199                 'urls': {}
200             },
201         ],
202         'text': [],
203         'times': [1458718655.415821, null]
204     };
205 }
206
207 function sampleFinishedBuild(buildRequestId)
208 {
209     return {
210         'blame': [],
211         'builderName': 'ABTest-iPad-RunBenchmark-Tests',
212         'currentStep': null,
213         'eta': null,
214         'logs': [['stdio','https://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/1755/steps/shell/logs/stdio']],
215         'number': 1755,
216         'properties': [
217             ['build_request_id', buildRequestId || '18935', 'Force Build Form'],
218             ['buildername', 'ABTest-iPad-RunBenchmark-Tests', 'Builder'],
219             ['buildnumber', 1755, 'Build'],
220             ['desired_image', '13A452', 'Force Build Form'],
221             ['owner', '<unknown>', 'Force Build Form'],
222             ['reason', 'force build', 'Force Build Form'],
223             ['roots_dict', JSON.stringify(sampleRootSetData), 'Force Build Form'],
224             ['scheduler', 'ABTest-iPad-RunBenchmark-Tests-ForceScheduler', 'Scheduler'],
225             ['slavename', 'ABTest-iPad-0', 'BuildSlave'],
226         ],
227         'reason': 'A build was forced by \'<unknown>\': force build',
228         'results': 2,
229         'slave': 'ABTest-iPad-0',
230         'sourceStamps': [{'branch': '', 'changes': [], 'codebase': 'compiler-rt', 'hasPatch': false, 'project': '', 'repository': '', 'revision': ''}],
231         'steps': [
232             {
233                 'eta': null,
234                 'expectations': [['output',2309,2309.0]],
235                 'hidden': false,
236                 'isFinished': true,
237                 'isStarted': true,
238                 'logs': [['stdio', 'https://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/614/steps/shell/logs/stdio']],
239                 'name': 'Finished step',
240                 'results': [0, []],
241                 'statistics': {},
242                 'step_number': 0,
243                 'text': [''],
244                 'times': [1458718655.419865, 1458718655.453633],
245                 'urls': {}
246             },
247             {
248                 'eta': null,
249                 'expectations': [['output', 845, 1315.0]],
250                 'hidden': false,
251                 'isFinished': true,
252                 'isStarted': true,
253                 'logs': [['stdio', 'https://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/614/steps/Some%20step/logs/stdio']],
254                 'name': 'Some step',
255                 'results': [null,[]],
256                 'statistics': {},
257                 'step_number': 1,
258                 'text': [''],
259                 'times': [1458718657.581628, null],
260                 'urls': {}
261             },
262             {
263                 'eta': null,
264                 'expectations': [['output', null, null]],
265                 'hidden': false,
266                 'isFinished': true,
267                 'isStarted': true,
268                 'logs': [],
269                 'name': 'Some other step',
270                 'results': [null, []],
271                 'statistics': {},
272                 'step_number': 2,
273                 'text': [],
274                 'times': [null, null],
275                 'urls': {}
276             },
277         ],
278         'text': [],
279         'times': [1458937478.25837, 1458946147.173785]
280     };
281 }
282
283 describe('BuildbotSyncer', function () {
284     MockModels.inject();
285     let requests = MockRemoteAPI.inject();
286
287     describe('_loadConfig', function () {
288
289         function smallConfiguration()
290         {
291             return {
292                 'builder': 'some builder',
293                 'platform': 'some platform',
294                 'test': ['some test'],
295                 'arguments': {},
296                 'buildRequestArgument': 'id'};
297         }
298
299         it('should create BuildbotSyncer objects for a configuration that specify all required options', function () {
300             let syncers = BuildbotSyncer._loadConfig('http://build.webkit.org/', {'configurations': [smallConfiguration()]});
301             assert.equal(syncers.length, 1);
302         });
303
304         it('should throw when some required options are missing', function () {
305             assert.throws(function () {
306                 let config = smallConfiguration();
307                 delete config['builder'];
308                 BuildbotSyncer._loadConfig('http://build.webkit.org/', {'configurations': [config]});
309             }, 'builder should be a required option');
310             assert.throws(function () {
311                 let config = smallConfiguration();
312                 delete config['platform'];
313                 BuildbotSyncer._loadConfig('http://build.webkit.org/', {'configurations': [config]});
314             }, 'platform should be a required option');
315             assert.throws(function () {
316                 let config = smallConfiguration();
317                 delete config['test'];
318                 BuildbotSyncer._loadConfig('http://build.webkit.org/', {'configurations': [config]});
319             }, 'test should be a required option');
320             assert.throws(function () {
321                 let config = smallConfiguration();
322                 delete config['arguments'];
323                 BuildbotSyncer._loadConfig('http://build.webkit.org/', {'configurations': [config]});
324             });
325             assert.throws(function () {
326                 let config = smallConfiguration();
327                 delete config['buildRequestArgument'];
328                 BuildbotSyncer._loadConfig('http://build.webkit.org/', {'configurations': [config]});
329             });
330         });
331
332         it('should throw when a test name is not an array of strings', function () {
333             assert.throws(function () {
334                 let config = smallConfiguration();
335                 config.test = 'some test';
336                 BuildbotSyncer._loadConfig('http://build.webkit.org/', {'configurations': [config]});
337             });
338             assert.throws(function () {
339                 let config = smallConfiguration();
340                 config.test = [1];
341                 BuildbotSyncer._loadConfig('http://build.webkit.org/', {'configurations': [config]});
342             });
343         });
344
345         it('should throw when arguments is not an object', function () {
346             assert.throws(function () {
347                 let config = smallConfiguration();
348                 config.arguments = 'hello';
349                 BuildbotSyncer._loadConfig('http://build.webkit.org/', {'configurations': [config]});
350             });
351         });
352
353         it('should throw when arguments\'s values are malformed', function () {
354             assert.throws(function () {
355                 let config = smallConfiguration();
356                 config.arguments = {'some': {'root': 'some root', 'rootsExcluding': ['other root']}};
357                 BuildbotSyncer._loadConfig('http://build.webkit.org/', {'configurations': [config]});
358             });
359             assert.throws(function () {
360                 let config = smallConfiguration();
361                 config.arguments = {'some': {'otherKey': 'some root'}};
362                 BuildbotSyncer._loadConfig('http://build.webkit.org/', {'configurations': [config]});
363             });
364             assert.throws(function () {
365                 let config = smallConfiguration();
366                 config.arguments = {'some': {'root': ['a', 'b']}};
367                 BuildbotSyncer._loadConfig('http://build.webkit.org/', {'configurations': [config]});
368             });
369             assert.throws(function () {
370                 let config = smallConfiguration();
371                 config.arguments = {'some': {'root': 1}};
372                 BuildbotSyncer._loadConfig('http://build.webkit.org/', {'configurations': [config]});
373             });
374             assert.throws(function () {
375                 let config = smallConfiguration();
376                 config.arguments = {'some': {'rootsExcluding': 'a'}};
377                 BuildbotSyncer._loadConfig('http://build.webkit.org/', {'configurations': [config]});
378             });
379             assert.throws(function () {
380                 let config = smallConfiguration();
381                 config.arguments = {'some': {'rootsExcluding': [1]}};
382                 BuildbotSyncer._loadConfig('http://build.webkit.org/', {'configurations': [config]});
383             });
384         });
385
386         it('should create BuildbotSyncer objects for valid configurations', function () {
387             let syncers = BuildbotSyncer._loadConfig('http://build.webkit.org/', sampleiOSConfig());
388             assert.equal(syncers.length, 5);
389             assert.ok(syncers[0] instanceof BuildbotSyncer);
390             assert.ok(syncers[1] instanceof BuildbotSyncer);
391             assert.ok(syncers[2] instanceof BuildbotSyncer);
392             assert.ok(syncers[3] instanceof BuildbotSyncer);
393             assert.ok(syncers[4] instanceof BuildbotSyncer);
394         });
395
396         it('should parse builder names correctly', function () {
397             let syncers = BuildbotSyncer._loadConfig('http://build.webkit.org/', sampleiOSConfig());
398             assert.equal(syncers[0].builderName(), 'ABTest-iPhone-RunBenchmark-Tests');
399             assert.equal(syncers[1].builderName(), 'ABTest-iPhone-RunBenchmark-Tests');
400             assert.equal(syncers[2].builderName(), 'ABTest-iPhone-RunBenchmark-Tests');
401             assert.equal(syncers[3].builderName(), 'ABTest-iPad-RunBenchmark-Tests');
402             assert.equal(syncers[4].builderName(), 'ABTest-iPad-RunBenchmark-Tests');
403         });
404
405         it('should parse platform names correctly', function () {
406             let syncers = BuildbotSyncer._loadConfig('http://build.webkit.org/', sampleiOSConfig());
407             assert.equal(syncers[0].platformName(), 'iPhone');
408             assert.equal(syncers[1].platformName(), 'iPhone');
409             assert.equal(syncers[2].platformName(), 'iPhone');
410             assert.equal(syncers[3].platformName(), 'iPad');
411             assert.equal(syncers[4].platformName(), 'iPad');
412         });
413
414         it('should parse test names correctly', function () {
415             let syncers = BuildbotSyncer._loadConfig('http://build.webkit.org/', sampleiOSConfig());
416             assert.deepEqual(syncers[0].testPath(), ['Speedometer']);
417             assert.deepEqual(syncers[1].testPath(), ['JetStream']);
418             assert.deepEqual(syncers[2].testPath(), ['Dromaeo', 'DOM Core Tests']);
419             assert.deepEqual(syncers[3].testPath(), ['Speedometer']);
420             assert.deepEqual(syncers[4].testPath(), ['JetStream']);
421         });
422     });
423
424     describe('_propertiesForBuildRequest', function () {
425         it('should include all properties specified in a given configuration', function () {
426             let syncers = BuildbotSyncer._loadConfig('http://build.webkit.org/', sampleiOSConfig());
427             let properties = syncers[0]._propertiesForBuildRequest(createSampleBuildRequest());
428             assert.deepEqual(Object.keys(properties), ['desired_image', 'roots_dict', 'test_name', 'forcescheduler', 'build_request_id']);
429         });
430
431         it('should preserve non-parametric property values', function () {
432             let syncers = BuildbotSyncer._loadConfig('http://build.webkit.org/', sampleiOSConfig());
433             let properties = syncers[0]._propertiesForBuildRequest(createSampleBuildRequest());
434             assert.equal(properties['test_name'], 'speedometer');
435             assert.equal(properties['forcescheduler'], 'ABTest-iPhone-RunBenchmark-Tests-ForceScheduler');
436
437             properties = syncers[1]._propertiesForBuildRequest(createSampleBuildRequest());
438             assert.equal(properties['test_name'], 'jetstream');
439             assert.equal(properties['forcescheduler'], 'ABTest-iPhone-RunBenchmark-Tests-ForceScheduler');
440         });
441
442         it('should resolve "root"', function () {
443             let syncers = BuildbotSyncer._loadConfig('http://build.webkit.org/', sampleiOSConfig());
444             let properties = syncers[0]._propertiesForBuildRequest(createSampleBuildRequest());
445             assert.equal(properties['desired_image'], '13A452');
446         });
447
448         it('should resolve "rootsExcluding"', function () {
449             let syncers = BuildbotSyncer._loadConfig('http://build.webkit.org/', sampleiOSConfig());
450             let properties = syncers[0]._propertiesForBuildRequest(createSampleBuildRequest());
451             assert.equal(properties['roots_dict'], JSON.stringify(sampleRootSetData));
452         });
453
454         it('should set the property for the build request id', function () {
455             let syncers = BuildbotSyncer._loadConfig('http://build.webkit.org/', sampleiOSConfig());
456             let properties = syncers[0]._propertiesForBuildRequest(createSampleBuildRequest());
457             assert.equal(properties['build_request_id'], createSampleBuildRequest().id());
458         });
459     });
460
461     describe('pullBuildbot', function () {
462         it('should fetch pending builds from the right URL', function () {
463             let syncer = BuildbotSyncer._loadConfig('http://build.webkit.org', sampleiOSConfig())[3];
464             assert.equal(syncer.builderName(), 'ABTest-iPad-RunBenchmark-Tests');
465             let expectedURL = 'http://build.webkit.org/json/builders/ABTest-iPad-RunBenchmark-Tests/pendingBuilds';
466             assert.equal(syncer.urlForPendingBuildsJSON(), expectedURL);
467             syncer.pullBuildbot();
468             assert.equal(requests.length, 1);
469             assert.equal(requests[0].url, expectedURL);
470         });
471
472         it('should fetch recent builds once pending builds have been fetched', function (done) {
473             let syncer = BuildbotSyncer._loadConfig('http://build.webkit.org', sampleiOSConfig())[3];
474             assert.equal(syncer.builderName(), 'ABTest-iPad-RunBenchmark-Tests');
475
476             syncer.pullBuildbot(1);
477             assert.equal(requests.length, 1);
478             assert.equal(requests[0].url, 'http://build.webkit.org/json/builders/ABTest-iPad-RunBenchmark-Tests/pendingBuilds');
479             requests[0].resolve([]);
480             Promise.resolve().then(function () {
481                 assert.equal(requests.length, 2);
482                 assert.equal(requests[1].url, 'http://build.webkit.org/json/builders/ABTest-iPad-RunBenchmark-Tests/builds/?select=-1');
483                 done();
484             }).catch(done);
485         });
486
487         it('should fetch the right number of recent builds', function (done) {
488             let syncer = BuildbotSyncer._loadConfig('http://build.webkit.org', sampleiOSConfig())[3];
489
490             syncer.pullBuildbot(3);
491             assert.equal(requests.length, 1);
492             assert.equal(requests[0].url, 'http://build.webkit.org/json/builders/ABTest-iPad-RunBenchmark-Tests/pendingBuilds');
493             requests[0].resolve([]);
494             Promise.resolve().then(function () {
495                 assert.equal(requests.length, 2);
496                 assert.equal(requests[1].url, 'http://build.webkit.org/json/builders/ABTest-iPad-RunBenchmark-Tests/builds/?select=-1&select=-2&select=-3');
497                 done();
498             }).catch(done);
499         });
500
501         it('should create BuildbotBuildEntry for pending builds', function (done) {
502             let syncer = BuildbotSyncer._loadConfig('http://build.webkit.org', sampleiOSConfig())[3];
503             let promise = syncer.pullBuildbot();
504             requests[0].resolve([samplePendingBuild()]);
505             promise.then(function (entries) {
506                 assert.deepEqual(Object.keys(entries), ['16733']);
507                 let entry = entries['16733'];
508                 assert.ok(entry instanceof BuildbotBuildEntry);
509                 assert.ok(!entry.buildNumber());
510                 assert.ok(!entry.slaveName());
511                 assert.equal(entry.buildRequestId(), 16733);
512                 assert.ok(entry.isPending());
513                 assert.ok(!entry.isInProgress());
514                 assert.ok(!entry.hasFinished());
515                 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/');
516                 done();
517             }).catch(done);
518         });
519
520         it('should create BuildbotBuildEntry for in-progress builds', function (done) {
521             let syncer = BuildbotSyncer._loadConfig('http://build.webkit.org', sampleiOSConfig())[3];
522
523             let promise = syncer.pullBuildbot(1);
524             assert.equal(requests.length, 1);
525             requests[0].resolve([]);
526             Promise.resolve().then(function () {
527                 assert.equal(requests.length, 2);
528                 requests[1].resolve({[-1]: sampleInProgressBuild()});
529             }).catch(done);
530
531             promise.then(function (entries) {
532                 assert.deepEqual(Object.keys(entries), ['16733']);
533                 let entry = entries['16733'];
534                 assert.ok(entry instanceof BuildbotBuildEntry);
535                 assert.equal(entry.buildNumber(), 614);
536                 assert.equal(entry.slaveName(), 'ABTest-iPad-0');
537                 assert.equal(entry.buildRequestId(), 16733);
538                 assert.ok(!entry.isPending());
539                 assert.ok(entry.isInProgress());
540                 assert.ok(!entry.hasFinished());
541                 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/614');
542                 done();
543             }).catch(done);
544         });
545
546         it('should create BuildbotBuildEntry for finished builds', function (done) {
547             let syncer = BuildbotSyncer._loadConfig('http://build.webkit.org', sampleiOSConfig())[3];
548
549             let promise = syncer.pullBuildbot(1);
550             assert.equal(requests.length, 1);
551             requests[0].resolve([]);
552             Promise.resolve().then(function () {
553                 assert.equal(requests.length, 2);
554                 requests[1].resolve({[-1]: sampleFinishedBuild()});
555             }).catch(done);
556
557             promise.then(function (entries) {
558                 assert.deepEqual(Object.keys(entries), ['18935']);
559                 let entry = entries['18935'];
560                 assert.ok(entry instanceof BuildbotBuildEntry);
561                 assert.equal(entry.buildNumber(), 1755);
562                 assert.equal(entry.slaveName(), 'ABTest-iPad-0');
563                 assert.equal(entry.buildRequestId(), 18935);
564                 assert.ok(!entry.isPending());
565                 assert.ok(!entry.isInProgress());
566                 assert.ok(entry.hasFinished());
567                 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/1755');
568                 done();
569             }).catch(done);
570         });
571
572         it('should create BuildbotBuildEntry for mixed pending, in-progress, finished, and missing builds', function (done) {
573             let syncer = BuildbotSyncer._loadConfig('http://build.webkit.org', sampleiOSConfig())[3];
574
575             let promise = syncer.pullBuildbot(5);
576             assert.equal(requests.length, 1);
577
578             requests[0].resolve([samplePendingBuild(123, 456)]);
579
580             Promise.resolve().then(function () {
581                 assert.equal(requests.length, 2);
582                 requests[1].resolve({[-1]: sampleFinishedBuild(), [-2]: {'error': 'Not available'}, [-4]: sampleInProgressBuild()});
583             }).catch(done);
584
585             promise.then(function (entries) {
586                 assert.deepEqual(Object.keys(entries), ['123', '16733', '18935']);
587
588                 let entry = entries['123'];
589                 assert.ok(entry instanceof BuildbotBuildEntry);
590                 assert.equal(entry.buildNumber(), null);
591                 assert.equal(entry.slaveName(), null);
592                 assert.equal(entry.buildRequestId(), 123);
593                 assert.ok(entry.isPending());
594                 assert.ok(!entry.isInProgress());
595                 assert.ok(!entry.hasFinished());
596                 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/');
597
598                 entry = entries['16733'];
599                 assert.ok(entry instanceof BuildbotBuildEntry);
600                 assert.equal(entry.buildNumber(), 614);
601                 assert.equal(entry.slaveName(), 'ABTest-iPad-0');
602                 assert.equal(entry.buildRequestId(), 16733);
603                 assert.ok(!entry.isPending());
604                 assert.ok(entry.isInProgress());
605                 assert.ok(!entry.hasFinished());
606                 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/614');
607
608                 entry = entries['18935'];
609                 assert.ok(entry instanceof BuildbotBuildEntry);
610                 assert.equal(entry.buildNumber(), 1755);
611                 assert.equal(entry.slaveName(), 'ABTest-iPad-0');
612                 assert.equal(entry.buildRequestId(), 18935);
613                 assert.ok(!entry.isPending());
614                 assert.ok(!entry.isInProgress());
615                 assert.ok(entry.hasFinished());
616                 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/1755');
617
618                 done();
619             }).catch(done);
620         });
621
622         it('should override BuildbotBuildEntry for pending builds by in-progress builds', function (done) {
623             let syncer = BuildbotSyncer._loadConfig('http://build.webkit.org', sampleiOSConfig())[3];
624
625             let promise = syncer.pullBuildbot(5);
626             assert.equal(requests.length, 1);
627
628             requests[0].resolve([samplePendingBuild()]);
629
630             Promise.resolve().then(function () {
631                 assert.equal(requests.length, 2);
632                 requests[1].resolve({[-1]: sampleInProgressBuild()});
633             }).catch(done);
634
635             promise.then(function (entries) {
636                 assert.deepEqual(Object.keys(entries), ['16733']);
637
638                 let entry = entries['16733'];
639                 assert.ok(entry instanceof BuildbotBuildEntry);
640                 assert.equal(entry.buildNumber(), 614);
641                 assert.equal(entry.slaveName(), 'ABTest-iPad-0');
642                 assert.equal(entry.buildRequestId(), 16733);
643                 assert.ok(!entry.isPending());
644                 assert.ok(entry.isInProgress());
645                 assert.ok(!entry.hasFinished());
646                 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/614');
647
648                 done();
649             }).catch(done);
650         });
651
652         it('should override BuildbotBuildEntry for pending builds by finished builds', function (done) {
653             let syncer = BuildbotSyncer._loadConfig('http://build.webkit.org', sampleiOSConfig())[3];
654
655             let promise = syncer.pullBuildbot(5);
656             assert.equal(requests.length, 1);
657
658             requests[0].resolve([samplePendingBuild()]);
659
660             Promise.resolve().then(function () {
661                 assert.equal(requests.length, 2);
662                 requests[1].resolve({[-1]: sampleFinishedBuild(16733)});
663             }).catch(done);
664
665             promise.then(function (entries) {
666                 assert.deepEqual(Object.keys(entries), ['16733']);
667
668                 let entry = entries['16733'];
669                 assert.ok(entry instanceof BuildbotBuildEntry);
670                 assert.equal(entry.buildNumber(), 1755);
671                 assert.equal(entry.slaveName(), 'ABTest-iPad-0');
672                 assert.equal(entry.buildRequestId(), 16733);
673                 assert.ok(!entry.isPending());
674                 assert.ok(!entry.isInProgress());
675                 assert.ok(entry.hasFinished());
676                 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/1755');
677
678                 done();
679             }).catch(done);
680         });
681
682     });
683 });