3 let assert = require('assert');
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;
9 let BuildbotBuildEntry = require('../tools/js/buildbot-syncer.js').BuildbotBuildEntry;
10 let BuildbotSyncer = require('../tools/js/buildbot-syncer.js').BuildbotSyncer;
12 function sampleiOSConfig()
18 'desired_image': {'root': 'iOS'},
19 'opensource': {'rootOptions': ['WebKit-SVN', 'WebKit-Git']},
20 'roots_dict': {'rootsExcluding': ['iOS']}
22 'slaveArgument': 'slavename',
23 'buildRequestArgument': 'build_request_id'
27 'test': ['Speedometer'],
28 'arguments': {'test_name': 'speedometer'}
31 'test': ['JetStream'],
32 'arguments': {'test_name': 'jetstream'}
35 'test': ['Dromaeo', 'DOM Core Tests'],
36 'arguments': {'tests': 'dromaeo-dom'}
41 'builder': 'ABTest-iPhone-RunBenchmark-Tests',
42 'arguments': { 'forcescheduler': 'ABTest-iPhone-RunBenchmark-Tests-ForceScheduler' },
43 'slaveList': ['ABTest-iPhone-0'],
46 'builder': 'ABTest-iPad-RunBenchmark-Tests',
47 'arguments': { 'forcescheduler': 'ABTest-iPad-RunBenchmark-Tests-ForceScheduler' },
48 'slaveList': ['ABTest-iPad-0', 'ABTest-iPad-1'],
52 {'type': 'speedometer', 'builder': 'iPhone-bench', 'platform': 'iPhone'},
53 {'type': 'jetstream', 'builder': 'iPhone-bench', 'platform': 'iPhone'},
54 {'type': 'dromaeo-dom', 'builder': 'iPhone-bench', 'platform': 'iPhone'},
56 {'type': 'speedometer', 'builder': 'iPad-bench', 'platform': 'iPad'},
57 {'type': 'jetstream', 'builder': 'iPad-bench', 'platform': 'iPad'},
62 function sampleiOSConfigWithExpansions()
65 "triggerableName": "build-webkit-ios",
69 "webkit-revision": {"root": "WebKit"},
70 "os-version": {"root": "iOS"}
72 "buildRequestArgument": "build-request-id"
76 "test": ["PLT-iPhone"],
77 "arguments": {"test_name": "plt"}
81 "arguments": {"test_name": "plt"}
84 "test": ["Speedometer"],
85 "arguments": {"tests": "speedometer"}
90 "builder": "iPhone AB Tests",
91 "arguments": {"forcescheduler": "force-iphone-ab-tests"}
94 "builder": "iPad AB Tests",
95 "arguments": {"forcescheduler": "force-ipad-ab-tests"}
101 "platforms": ["iPhone", "iOS 10 iPhone"],
102 "types": ["iphone-plt", "speedometer"],
106 "platforms": ["iPad"],
107 "types": ["ipad-plt", "speedometer"],
113 let sampleCommitSetData = {
116 'time': 1456955807334,
117 'repository': 'WebKit',
118 'revision': '197463',
122 'time': 1456931874000,
123 'repository': 'Shared',
128 "time":1456931874000,
129 "repository":"WebKit-Git",
130 "revision":"9abcdef",
134 function smallConfiguration()
137 'builder': 'some builder',
138 'platform': 'Some platform',
139 'test': ['Some test'],
141 'buildRequestArgument': 'id'};
144 function smallPendingBuild()
147 'builderName': 'some builder',
153 'codebase': 'WebKit',
162 function smallInProgressBuild()
165 'builderName': 'some builder',
174 'codebase': 'WebKit',
183 function smallFinishedBuild()
186 'builderName': 'some builder',
195 'codebase': 'WebKit',
205 function createSampleBuildRequest(platform, test)
207 assert(platform instanceof Platform);
208 assert(test instanceof Test);
210 let commitSet = CommitSet.ensureSingleton('4197', {commits: [
211 {'id': '111127', 'time': 1456955807334, 'repository': MockModels.webkit, 'revision': '197463'},
212 {'id': '111237', 'time': 1456931874000, 'repository': MockModels.sharedRepository, 'revision': '80229'},
213 {'id': '111239', 'time': 1456931874000, 'repository': MockModels.webkitGit, 'revision': '9abcdef'},
214 {'id': '88930', 'time': 0, 'repository': MockModels.ios, 'revision': '13A452'},
217 let request = BuildRequest.ensureSingleton('16733-' + platform.id(), {'commitSet': commitSet, 'status': 'pending', 'platform': platform, 'test': test});
221 function samplePendingBuild(buildRequestId, buildTime, slaveName)
224 'builderName': 'ABTest-iPad-RunBenchmark-Tests',
227 ['build_request_id', buildRequestId || '16733', 'Force Build Form'],
228 ['desired_image', '13A452', 'Force Build Form'],
229 ['owner', '<unknown>', 'Force Build Form'],
230 ['test_name', 'speedometer', 'Force Build Form'],
231 ['reason', 'force build','Force Build Form'],
234 JSON.stringify(sampleCommitSetData),
237 ['slavename', slaveName, ''],
238 ['scheduler', 'ABTest-iPad-RunBenchmark-Tests-ForceScheduler', 'Scheduler']
243 'codebase': 'compiler-rt',
249 'submittedAt': buildTime || 1458704983
253 function sampleInProgressBuild(slaveName)
257 'builderName': 'ABTest-iPad-RunBenchmark-Tests',
259 'eta': 0.26548067698460565,
260 'expectations': [['output', 845, 1315.0]],
264 'logs': [['stdio', 'https://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/614/steps/Some%20step/logs/stdio']],
266 'results': [null,[]],
270 'times': [1458718657.581628, null],
273 'eta': 6497.991612434387,
274 'logs': [['stdio','https://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/614/steps/shell/logs/stdio']],
277 ['build_request_id', '16733', 'Force Build Form'],
278 ['buildername', 'ABTest-iPad-RunBenchmark-Tests', 'Builder'],
279 ['buildnumber', 614, 'Build'],
280 ['desired_image', '13A452', 'Force Build Form'],
281 ['owner', '<unknown>', 'Force Build Form'],
282 ['reason', 'force build', 'Force Build Form'],
283 ['roots_dict', JSON.stringify(sampleCommitSetData), 'Force Build Form'],
284 ['scheduler', 'ABTest-iPad-RunBenchmark-Tests-ForceScheduler', 'Scheduler'],
285 ['slavename', slaveName || 'ABTest-iPad-0', 'BuildSlave'],
287 'reason': 'A build was forced by \'<unknown>\': force build',
289 'slave': 'ABTest-iPad-0',
290 'sourceStamps': [{'branch': '', 'changes': [], 'codebase': 'compiler-rt', 'hasPatch': false, 'project': '', 'repository': '', 'revision': ''}],
294 'expectations': [['output',2309,2309.0]],
298 'logs': [['stdio', 'https://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/614/steps/shell/logs/stdio']],
299 'name': 'Finished step',
304 'times': [1458718655.419865, 1458718655.453633],
308 'eta': 0.26548067698460565,
309 'expectations': [['output', 845, 1315.0]],
313 'logs': [['stdio', 'https://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/614/steps/Some%20step/logs/stdio']],
315 'results': [null,[]],
319 'times': [1458718657.581628, null],
324 'expectations': [['output', null, null]],
329 'name': 'Some other step',
330 'results': [null, []],
334 'times': [null, null],
339 'times': [1458718655.415821, null]
343 function sampleFinishedBuild(buildRequestId, slaveName)
347 'builderName': 'ABTest-iPad-RunBenchmark-Tests',
350 'logs': [['stdio','https://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/1755/steps/shell/logs/stdio']],
353 ['build_request_id', buildRequestId || '18935', 'Force Build Form'],
354 ['buildername', 'ABTest-iPad-RunBenchmark-Tests', 'Builder'],
355 ['buildnumber', 1755, 'Build'],
356 ['desired_image', '13A452', 'Force Build Form'],
357 ['owner', '<unknown>', 'Force Build Form'],
358 ['reason', 'force build', 'Force Build Form'],
359 ['roots_dict', JSON.stringify(sampleCommitSetData), 'Force Build Form'],
360 ['scheduler', 'ABTest-iPad-RunBenchmark-Tests-ForceScheduler', 'Scheduler'],
361 ['slavename', slaveName || 'ABTest-iPad-0', 'BuildSlave'],
363 'reason': 'A build was forced by \'<unknown>\': force build',
365 'slave': 'ABTest-iPad-0',
366 'sourceStamps': [{'branch': '', 'changes': [], 'codebase': 'compiler-rt', 'hasPatch': false, 'project': '', 'repository': '', 'revision': ''}],
370 'expectations': [['output',2309,2309.0]],
374 'logs': [['stdio', 'https://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/614/steps/shell/logs/stdio']],
375 'name': 'Finished step',
380 'times': [1458718655.419865, 1458718655.453633],
385 'expectations': [['output', 845, 1315.0]],
389 'logs': [['stdio', 'https://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/614/steps/Some%20step/logs/stdio']],
391 'results': [null,[]],
395 'times': [1458718657.581628, null],
400 'expectations': [['output', null, null]],
405 'name': 'Some other step',
406 'results': [null, []],
410 'times': [null, null],
415 'times': [1458937478.25837, 1458946147.173785]
419 describe('BuildbotSyncer', () => {
421 let requests = MockRemoteAPI.inject('http://build.webkit.org');
423 describe('_loadConfig', () => {
425 it('should create BuildbotSyncer objects for a configuration that specify all required options', () => {
426 let syncers = BuildbotSyncer._loadConfig(MockRemoteAPI, {'configurations': [smallConfiguration()]});
427 assert.equal(syncers.length, 1);
430 it('should throw when some required options are missing', () => {
431 assert.throws(() => {
432 let config = smallConfiguration();
433 delete config['builder'];
434 BuildbotSyncer._loadConfig(MockRemoteAPI, {'configurations': [config]});
435 }, 'builder should be a required option');
436 assert.throws(() => {
437 let config = smallConfiguration();
438 delete config['platform'];
439 BuildbotSyncer._loadConfig(MockRemoteAPI, {'configurations': [config]});
440 }, 'platform should be a required option');
441 assert.throws(() => {
442 let config = smallConfiguration();
443 delete config['test'];
444 BuildbotSyncer._loadConfig(MockRemoteAPI, {'configurations': [config]});
445 }, 'test should be a required option');
446 assert.throws(() => {
447 let config = smallConfiguration();
448 delete config['arguments'];
449 BuildbotSyncer._loadConfig(MockRemoteAPI, {'configurations': [config]});
451 assert.throws(() => {
452 let config = smallConfiguration();
453 delete config['buildRequestArgument'];
454 BuildbotSyncer._loadConfig(MockRemoteAPI, {'configurations': [config]});
458 it('should throw when a test name is not an array of strings', () => {
459 assert.throws(() => {
460 let config = smallConfiguration();
461 config.test = 'some test';
462 BuildbotSyncer._loadConfig(MockRemoteAPI, {'configurations': [config]});
464 assert.throws(() => {
465 let config = smallConfiguration();
467 BuildbotSyncer._loadConfig(MockRemoteAPI, {'configurations': [config]});
471 it('should throw when arguments is not an object', () => {
472 assert.throws(() => {
473 let config = smallConfiguration();
474 config.arguments = 'hello';
475 BuildbotSyncer._loadConfig(MockRemoteAPI, {'configurations': [config]});
479 it('should throw when arguments\'s values are malformed', () => {
480 assert.throws(() => {
481 let config = smallConfiguration();
482 config.arguments = {'some': {'root': 'some root', 'rootsExcluding': ['other root']}};
483 BuildbotSyncer._loadConfig(RemoteAPI, {'configurations': [config]});
485 assert.throws(() => {
486 let config = smallConfiguration();
487 config.arguments = {'some': {'otherKey': 'some root'}};
488 BuildbotSyncer._loadConfig(RemoteAPI, {'configurations': [config]});
490 assert.throws(() => {
491 let config = smallConfiguration();
492 config.arguments = {'some': {'root': ['a', 'b']}};
493 BuildbotSyncer._loadConfig(RemoteAPI, {'configurations': [config]});
495 assert.throws(() => {
496 let config = smallConfiguration();
497 config.arguments = {'some': {'root': 1}};
498 BuildbotSyncer._loadConfig(RemoteAPI, {'configurations': [config]});
500 assert.throws(() => {
501 let config = smallConfiguration();
502 config.arguments = {'some': {'rootsExcluding': 'a'}};
503 BuildbotSyncer._loadConfig(RemoteAPI, {'configurations': [config]});
505 assert.throws(() => {
506 let config = smallConfiguration();
507 config.arguments = {'some': {'rootsExcluding': [1]}};
508 BuildbotSyncer._loadConfig(RemoteAPI, {'configurations': [config]});
512 it('should create BuildbotSyncer objects for valid configurations', () => {
513 let syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig());
514 assert.equal(syncers.length, 2);
515 assert.ok(syncers[0] instanceof BuildbotSyncer);
516 assert.ok(syncers[1] instanceof BuildbotSyncer);
519 it('should parse builder names correctly', () => {
520 let syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig());
521 assert.equal(syncers[0].builderName(), 'ABTest-iPhone-RunBenchmark-Tests');
522 assert.equal(syncers[1].builderName(), 'ABTest-iPad-RunBenchmark-Tests');
525 it('should parse test configurations correctly', () => {
526 let syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig());
528 let configurations = syncers[0].testConfigurations();
529 assert.equal(configurations.length, 3);
530 assert.equal(configurations[0].platform, MockModels.iphone);
531 assert.equal(configurations[0].test, MockModels.speedometer);
532 assert.equal(configurations[1].platform, MockModels.iphone);
533 assert.equal(configurations[1].test, MockModels.jetstream);
534 assert.equal(configurations[2].platform, MockModels.iphone);
535 assert.equal(configurations[2].test, MockModels.domcore);
537 configurations = syncers[1].testConfigurations();
538 assert.equal(configurations.length, 2);
539 assert.equal(configurations[0].platform, MockModels.ipad);
540 assert.equal(configurations[0].test, MockModels.speedometer);
541 assert.equal(configurations[1].platform, MockModels.ipad);
542 assert.equal(configurations[1].test, MockModels.jetstream);
545 it('should parse test configurations with types and platforms expansions correctly', () => {
546 let syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfigWithExpansions());
548 assert.equal(syncers.length, 2);
550 let configurations = syncers[0].testConfigurations();
551 assert.equal(configurations.length, 4);
552 assert.equal(configurations[0].platform, MockModels.iphone);
553 assert.equal(configurations[0].test, MockModels.iPhonePLT);
554 assert.equal(configurations[1].platform, MockModels.iOS10iPhone);
555 assert.equal(configurations[1].test, MockModels.iPhonePLT);
556 assert.equal(configurations[2].platform, MockModels.iphone);
557 assert.equal(configurations[2].test, MockModels.speedometer);
558 assert.equal(configurations[3].platform, MockModels.iOS10iPhone);
559 assert.equal(configurations[3].test, MockModels.speedometer);
561 configurations = syncers[1].testConfigurations();
562 assert.equal(configurations.length, 2);
563 assert.equal(configurations[0].platform, MockModels.ipad);
564 assert.equal(configurations[0].test, MockModels.iPadPLT);
565 assert.equal(configurations[1].platform, MockModels.ipad);
566 assert.equal(configurations[1].test, MockModels.speedometer);
570 describe('_propertiesForBuildRequest', () => {
571 it('should include all properties specified in a given configuration', () => {
572 let syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig());
573 let properties = syncers[0]._propertiesForBuildRequest(createSampleBuildRequest(MockModels.iphone, MockModels.speedometer));
574 assert.deepEqual(Object.keys(properties), ['desired_image', 'opensource', 'roots_dict', 'test_name', 'forcescheduler', 'build_request_id']);
577 it('should preserve non-parametric property values', () => {
578 let syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig());
579 let properties = syncers[0]._propertiesForBuildRequest(createSampleBuildRequest(MockModels.iphone, MockModels.speedometer));
580 assert.equal(properties['test_name'], 'speedometer');
581 assert.equal(properties['forcescheduler'], 'ABTest-iPhone-RunBenchmark-Tests-ForceScheduler');
583 properties = syncers[1]._propertiesForBuildRequest(createSampleBuildRequest(MockModels.ipad, MockModels.jetstream));
584 assert.equal(properties['test_name'], 'jetstream');
585 assert.equal(properties['forcescheduler'], 'ABTest-iPad-RunBenchmark-Tests-ForceScheduler');
588 it('should resolve "root"', () => {
589 let syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig());
590 let properties = syncers[0]._propertiesForBuildRequest(createSampleBuildRequest(MockModels.iphone, MockModels.speedometer));
591 assert.equal(properties['desired_image'], '13A452');
594 it('should resolve "rootOptions"', () => {
595 let syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig());
596 let properties = syncers[0]._propertiesForBuildRequest(createSampleBuildRequest(MockModels.iphone, MockModels.speedometer));
597 assert.equal(properties['roots_dict'], JSON.stringify(sampleCommitSetData));
600 it('should resolve "rootsExcluding"', () => {
601 let syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig());
602 let properties = syncers[0]._propertiesForBuildRequest(createSampleBuildRequest(MockModels.iphone, MockModels.speedometer));
603 assert.equal(properties['roots_dict'], JSON.stringify(sampleCommitSetData));
606 it('should set the property for the build request id', () => {
607 let syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig());
608 let request = createSampleBuildRequest(MockModels.iphone, MockModels.speedometer);
609 let properties = syncers[0]._propertiesForBuildRequest(request);
610 assert.equal(properties['build_request_id'], request.id());
614 describe('pullBuildbot', () => {
615 it('should fetch pending builds from the right URL', () => {
616 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
617 assert.equal(syncer.builderName(), 'ABTest-iPad-RunBenchmark-Tests');
618 let expectedURL = '/json/builders/ABTest-iPad-RunBenchmark-Tests/pendingBuilds';
619 assert.equal(syncer.pathForPendingBuildsJSON(), expectedURL);
620 syncer.pullBuildbot();
621 assert.equal(requests.length, 1);
622 assert.equal(requests[0].url, expectedURL);
625 it('should fetch recent builds once pending builds have been fetched', () => {
626 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
627 assert.equal(syncer.builderName(), 'ABTest-iPad-RunBenchmark-Tests');
629 syncer.pullBuildbot(1);
630 assert.equal(requests.length, 1);
631 assert.equal(requests[0].url, '/json/builders/ABTest-iPad-RunBenchmark-Tests/pendingBuilds');
632 requests[0].resolve([]);
633 return MockRemoteAPI.waitForRequest().then(() => {
634 assert.equal(requests.length, 2);
635 assert.equal(requests[1].url, '/json/builders/ABTest-iPad-RunBenchmark-Tests/builds/?select=-1');
639 it('should fetch the right number of recent builds', () => {
640 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
642 syncer.pullBuildbot(3);
643 assert.equal(requests.length, 1);
644 assert.equal(requests[0].url, '/json/builders/ABTest-iPad-RunBenchmark-Tests/pendingBuilds');
645 requests[0].resolve([]);
646 return MockRemoteAPI.waitForRequest().then(() => {
647 assert.equal(requests.length, 2);
648 assert.equal(requests[1].url, '/json/builders/ABTest-iPad-RunBenchmark-Tests/builds/?select=-1&select=-2&select=-3');
652 it('should create BuildbotBuildEntry for pending builds', () => {
653 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
654 let promise = syncer.pullBuildbot();
655 requests[0].resolve([samplePendingBuild()]);
656 return promise.then((entries) => {
657 assert.equal(entries.length, 1);
658 let entry = entries[0];
659 assert.ok(entry instanceof BuildbotBuildEntry);
660 assert.ok(!entry.buildNumber());
661 assert.ok(!entry.slaveName());
662 assert.equal(entry.buildRequestId(), 16733);
663 assert.ok(entry.isPending());
664 assert.ok(!entry.isInProgress());
665 assert.ok(!entry.hasFinished());
666 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/');
670 it('should create BuildbotBuildEntry for in-progress builds', () => {
671 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
673 let promise = syncer.pullBuildbot(1);
674 assert.equal(requests.length, 1);
675 requests[0].resolve([]);
676 return MockRemoteAPI.waitForRequest().then(() => {
677 assert.equal(requests.length, 2);
678 requests[1].resolve({[-1]: sampleInProgressBuild()});
680 }).then((entries) => {
681 assert.equal(entries.length, 1);
682 let entry = entries[0];
683 assert.ok(entry instanceof BuildbotBuildEntry);
684 assert.equal(entry.buildNumber(), 614);
685 assert.equal(entry.slaveName(), 'ABTest-iPad-0');
686 assert.equal(entry.buildRequestId(), 16733);
687 assert.ok(!entry.isPending());
688 assert.ok(entry.isInProgress());
689 assert.ok(!entry.hasFinished());
690 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/614');
694 it('should create BuildbotBuildEntry for finished builds', () => {
695 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
697 let promise = syncer.pullBuildbot(1);
698 assert.equal(requests.length, 1);
699 requests[0].resolve([]);
700 return MockRemoteAPI.waitForRequest().then(() => {
701 assert.equal(requests.length, 2);
702 requests[1].resolve({[-1]: sampleFinishedBuild()});
704 }).then((entries) => {
705 assert.deepEqual(entries.length, 1);
706 let entry = entries[0];
707 assert.ok(entry instanceof BuildbotBuildEntry);
708 assert.equal(entry.buildNumber(), 1755);
709 assert.equal(entry.slaveName(), 'ABTest-iPad-0');
710 assert.equal(entry.buildRequestId(), 18935);
711 assert.ok(!entry.isPending());
712 assert.ok(!entry.isInProgress());
713 assert.ok(entry.hasFinished());
714 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/1755');
718 it('should create BuildbotBuildEntry for mixed pending, in-progress, finished, and missing builds', () => {
719 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
721 let promise = syncer.pullBuildbot(5);
722 assert.equal(requests.length, 1);
724 requests[0].resolve([samplePendingBuild(123)]);
726 return MockRemoteAPI.waitForRequest().then(() => {
727 assert.equal(requests.length, 2);
728 requests[1].resolve({[-1]: sampleFinishedBuild(), [-2]: {'error': 'Not available'}, [-4]: sampleInProgressBuild()});
730 }).then((entries) => {
731 assert.deepEqual(entries.length, 3);
733 let entry = entries[0];
734 assert.ok(entry instanceof BuildbotBuildEntry);
735 assert.equal(entry.buildNumber(), null);
736 assert.equal(entry.slaveName(), null);
737 assert.equal(entry.buildRequestId(), 123);
738 assert.ok(entry.isPending());
739 assert.ok(!entry.isInProgress());
740 assert.ok(!entry.hasFinished());
741 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/');
744 assert.ok(entry instanceof BuildbotBuildEntry);
745 assert.equal(entry.buildNumber(), 614);
746 assert.equal(entry.slaveName(), 'ABTest-iPad-0');
747 assert.equal(entry.buildRequestId(), 16733);
748 assert.ok(!entry.isPending());
749 assert.ok(entry.isInProgress());
750 assert.ok(!entry.hasFinished());
751 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/614');
754 assert.ok(entry instanceof BuildbotBuildEntry);
755 assert.equal(entry.buildNumber(), 1755);
756 assert.equal(entry.slaveName(), 'ABTest-iPad-0');
757 assert.equal(entry.buildRequestId(), 18935);
758 assert.ok(!entry.isPending());
759 assert.ok(!entry.isInProgress());
760 assert.ok(entry.hasFinished());
761 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/1755');
765 it('should sort BuildbotBuildEntry by order', () => {
766 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
768 let promise = syncer.pullBuildbot(5);
769 assert.equal(requests.length, 1);
771 requests[0].resolve([samplePendingBuild(456, 2), samplePendingBuild(123, 1)]);
773 return MockRemoteAPI.waitForRequest().then(() => {
774 assert.equal(requests.length, 2);
775 requests[1].resolve({[-3]: sampleFinishedBuild(), [-1]: {'error': 'Not available'}, [-2]: sampleInProgressBuild()});
777 }).then((entries) => {
778 assert.deepEqual(entries.length, 4);
780 let entry = entries[0];
781 assert.ok(entry instanceof BuildbotBuildEntry);
782 assert.equal(entry.buildNumber(), null);
783 assert.equal(entry.slaveName(), null);
784 assert.equal(entry.buildRequestId(), 123);
785 assert.ok(entry.isPending());
786 assert.ok(!entry.isInProgress());
787 assert.ok(!entry.hasFinished());
788 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/');
791 assert.ok(entry instanceof BuildbotBuildEntry);
792 assert.equal(entry.buildNumber(), null);
793 assert.equal(entry.slaveName(), null);
794 assert.equal(entry.buildRequestId(), 456);
795 assert.ok(entry.isPending());
796 assert.ok(!entry.isInProgress());
797 assert.ok(!entry.hasFinished());
798 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/');
801 assert.ok(entry instanceof BuildbotBuildEntry);
802 assert.equal(entry.buildNumber(), 614);
803 assert.equal(entry.slaveName(), 'ABTest-iPad-0');
804 assert.equal(entry.buildRequestId(), 16733);
805 assert.ok(!entry.isPending());
806 assert.ok(entry.isInProgress());
807 assert.ok(!entry.hasFinished());
808 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/614');
811 assert.ok(entry instanceof BuildbotBuildEntry);
812 assert.equal(entry.buildNumber(), 1755);
813 assert.equal(entry.slaveName(), 'ABTest-iPad-0');
814 assert.equal(entry.buildRequestId(), 18935);
815 assert.ok(!entry.isPending());
816 assert.ok(!entry.isInProgress());
817 assert.ok(entry.hasFinished());
818 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/1755');
822 it('should override BuildbotBuildEntry for pending builds by in-progress builds', () => {
823 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
825 let promise = syncer.pullBuildbot(5);
826 assert.equal(requests.length, 1);
828 requests[0].resolve([samplePendingBuild()]);
830 return MockRemoteAPI.waitForRequest().then(() => {
831 assert.equal(requests.length, 2);
832 requests[1].resolve({[-1]: sampleInProgressBuild()});
834 }).then((entries) => {
835 assert.equal(entries.length, 1);
837 let entry = entries[0];
838 assert.ok(entry instanceof BuildbotBuildEntry);
839 assert.equal(entry.buildNumber(), 614);
840 assert.equal(entry.slaveName(), 'ABTest-iPad-0');
841 assert.equal(entry.buildRequestId(), 16733);
842 assert.ok(!entry.isPending());
843 assert.ok(entry.isInProgress());
844 assert.ok(!entry.hasFinished());
845 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/614');
849 it('should override BuildbotBuildEntry for pending builds by finished builds', () => {
850 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
852 let promise = syncer.pullBuildbot(5);
853 assert.equal(requests.length, 1);
855 requests[0].resolve([samplePendingBuild()]);
857 return MockRemoteAPI.waitForRequest().then(() => {
858 assert.equal(requests.length, 2);
859 requests[1].resolve({[-1]: sampleFinishedBuild(16733)});
861 }).then((entries) => {
862 assert.equal(entries.length, 1);
864 let entry = entries[0];
865 assert.ok(entry instanceof BuildbotBuildEntry);
866 assert.equal(entry.buildNumber(), 1755);
867 assert.equal(entry.slaveName(), 'ABTest-iPad-0');
868 assert.equal(entry.buildRequestId(), 16733);
869 assert.ok(!entry.isPending());
870 assert.ok(!entry.isInProgress());
871 assert.ok(entry.hasFinished());
872 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/1755');
877 describe('scheduleRequest', () => {
878 it('should schedule a build request on a specified slave', () => {
879 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[0];
881 const waitForRequest = MockRemoteAPI.waitForRequest();
882 syncer.scheduleRequest(createSampleBuildRequest(MockModels.iphone, MockModels.speedometer), 'some-slave');
883 return waitForRequest.then(() => {
884 assert.equal(requests.length, 1);
885 assert.equal(requests[0].url, '/builders/ABTest-iPhone-RunBenchmark-Tests/force');
886 assert.equal(requests[0].method, 'POST');
887 assert.deepEqual(requests[0].data, {
888 'build_request_id': '16733-' + MockModels.iphone.id(),
889 'desired_image': '13A452',
890 "opensource": "9abcdef",
891 'forcescheduler': 'ABTest-iPhone-RunBenchmark-Tests-ForceScheduler',
892 'roots_dict': '{"WebKit":{"id":"111127","time":1456955807334,"repository":"WebKit","revision":"197463"},'
893 + '"Shared":{"id":"111237","time":1456931874000,"repository":"Shared","revision":"80229"},'
894 + '"WebKit-Git":{"id":"111239","time":1456931874000,"repository":"WebKit-Git","revision":"9abcdef"}}',
895 'slavename': 'some-slave',
896 'test_name': 'speedometer'
902 describe('scheduleRequestInGroupIfAvailable', () => {
904 function pullBuildbotWithAssertion(syncer, pendingBuilds, inProgressAndFinishedBuilds)
906 const promise = syncer.pullBuildbot(5);
907 assert.equal(requests.length, 1);
908 requests[0].resolve(pendingBuilds);
909 return MockRemoteAPI.waitForRequest().then(() => {
910 assert.equal(requests.length, 2);
911 requests[1].resolve(inProgressAndFinishedBuilds);
917 it('should schedule a build if builder has no builds if slaveList is not specified', () => {
918 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, {'configurations': [smallConfiguration()]})[0];
920 return pullBuildbotWithAssertion(syncer, [], {}).then(() => {
921 syncer.scheduleRequestInGroupIfAvailable(createSampleBuildRequest(MockModels.somePlatform, MockModels.someTest));
922 assert.equal(requests.length, 1);
923 assert.equal(requests[0].url, '/builders/some%20builder/force');
924 assert.equal(requests[0].method, 'POST');
925 assert.deepEqual(requests[0].data, {id: '16733-' + MockModels.somePlatform.id()});
929 it('should schedule a build if builder only has finished builds if slaveList is not specified', () => {
930 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, {'configurations': [smallConfiguration()]})[0];
932 return pullBuildbotWithAssertion(syncer, [], {[-1]: smallFinishedBuild()}).then(() => {
933 syncer.scheduleRequestInGroupIfAvailable(createSampleBuildRequest(MockModels.somePlatform, MockModels.someTest));
934 assert.equal(requests.length, 1);
935 assert.equal(requests[0].url, '/builders/some%20builder/force');
936 assert.equal(requests[0].method, 'POST');
937 assert.deepEqual(requests[0].data, {id: '16733-' + MockModels.somePlatform.id()});
941 it('should not schedule a build if builder has a pending build if slaveList is not specified', () => {
942 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, {'configurations': [smallConfiguration()]})[0];
944 return pullBuildbotWithAssertion(syncer, [smallPendingBuild()], {}).then(() => {
945 syncer.scheduleRequestInGroupIfAvailable(createSampleBuildRequest(MockModels.somePlatform, MockModels.someTest));
946 assert.equal(requests.length, 0);
950 it('should schedule a build if builder does not have pending or completed builds on the matching slave', () => {
951 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[0];
953 return pullBuildbotWithAssertion(syncer, [], {}).then(() => {
954 syncer.scheduleRequestInGroupIfAvailable(createSampleBuildRequest(MockModels.iphone, MockModels.speedometer));
955 assert.equal(requests.length, 1);
956 assert.equal(requests[0].url, '/builders/ABTest-iPhone-RunBenchmark-Tests/force');
957 assert.equal(requests[0].method, 'POST');
961 it('should schedule a build if builder only has finished builds on the matching slave', () => {
962 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
964 pullBuildbotWithAssertion(syncer, [], {[-1]: sampleFinishedBuild()}).then(() => {
965 syncer.scheduleRequestInGroupIfAvailable(createSampleBuildRequest(MockModels.ipad, MockModels.speedometer));
966 assert.equal(requests.length, 1);
967 assert.equal(requests[0].url, '/builders/ABTest-iPad-RunBenchmark-Tests/force');
968 assert.equal(requests[0].method, 'POST');
972 it('should not schedule a build if builder has a pending build on the maching slave', () => {
973 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
975 pullBuildbotWithAssertion(syncer, [samplePendingBuild()], {}).then(() => {
976 syncer.scheduleRequestInGroupIfAvailable(createSampleBuildRequest(MockModels.ipad, MockModels.speedometer));
977 assert.equal(requests.length, 0);
981 it('should schedule a build if builder only has a pending build on a non-maching slave', () => {
982 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
984 return pullBuildbotWithAssertion(syncer, [samplePendingBuild(1, 1, 'another-slave')], {}).then(() => {
985 syncer.scheduleRequestInGroupIfAvailable(createSampleBuildRequest(MockModels.ipad, MockModels.speedometer));
986 assert.equal(requests.length, 1);
990 it('should schedule a build if builder only has an in-progress build on the matching slave', () => {
991 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
993 return pullBuildbotWithAssertion(syncer, [], {[-1]: sampleInProgressBuild()}).then(() => {
994 syncer.scheduleRequestInGroupIfAvailable(createSampleBuildRequest(MockModels.ipad, MockModels.speedometer));
995 assert.equal(requests.length, 1);
999 it('should schedule a build if builder has an in-progress build on another slave', () => {
1000 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
1002 return pullBuildbotWithAssertion(syncer, [], {[-1]: sampleInProgressBuild('other-slave')}).then(() => {
1003 syncer.scheduleRequestInGroupIfAvailable(createSampleBuildRequest(MockModels.ipad, MockModels.speedometer));
1004 assert.equal(requests.length, 1);
1008 it('should not schedule a build if the request does not match any configuration', () => {
1009 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[0];
1011 return pullBuildbotWithAssertion(syncer, [], {}).then(() => {
1012 syncer.scheduleRequestInGroupIfAvailable(createSampleBuildRequest(MockModels.ipad, MockModels.speedometer));
1013 assert.equal(requests.length, 0);
1017 it('should not schedule a build if a new request had been submitted to the same slave', (done) => {
1018 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
1020 pullBuildbotWithAssertion(syncer, [], {}).then(() => {
1021 syncer.scheduleRequest(createSampleBuildRequest(MockModels.ipad, MockModels.speedometer), 'ABTest-iPad-0');
1022 syncer.scheduleRequest(createSampleBuildRequest(MockModels.ipad, MockModels.speedometer), 'ABTest-iPad-1');
1024 assert.equal(requests.length, 2);
1025 syncer.scheduleRequestInGroupIfAvailable(createSampleBuildRequest(MockModels.ipad, MockModels.speedometer));
1027 assert.equal(requests.length, 2);
1032 it('should schedule a build if a new request had been submitted to another slave', () => {
1033 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
1035 return pullBuildbotWithAssertion(syncer, [], {}).then(() => {
1036 syncer.scheduleRequest(createSampleBuildRequest(MockModels.ipad, MockModels.speedometer), 'ABTest-iPad-0');
1037 assert.equal(requests.length, 1);
1038 syncer.scheduleRequestInGroupIfAvailable(createSampleBuildRequest(MockModels.ipad, MockModels.speedometer), 'ABTest-iPad-1');
1039 assert.equal(requests.length, 2);
1043 it('should not schedule a build if a new request had been submitted to the same builder without slaveList', () => {
1044 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, {'configurations': [smallConfiguration()]})[0];
1046 return pullBuildbotWithAssertion(syncer, [], {}).then(() => {
1047 syncer.scheduleRequest(createSampleBuildRequest(MockModels.somePlatform, MockModels.someTest), null);
1048 assert.equal(requests.length, 1);
1049 syncer.scheduleRequestInGroupIfAvailable(createSampleBuildRequest(MockModels.somePlatform, MockModels.someTest));
1050 assert.equal(requests.length, 1);