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