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 'roots_dict': {'rootsExcluding': ['iOS']}
21 'slaveArgument': 'slavename',
22 'buildRequestArgument': 'build_request_id'
26 'test': ['Speedometer'],
27 'arguments': {'test_name': 'speedometer'}
30 'test': ['JetStream'],
31 'arguments': {'test_name': 'jetstream'}
34 'test': ['Dromaeo', 'DOM Core Tests'],
35 'arguments': {'tests': 'dromaeo-dom'}
40 'builder': 'ABTest-iPhone-RunBenchmark-Tests',
41 'arguments': { 'forcescheduler': 'ABTest-iPhone-RunBenchmark-Tests-ForceScheduler' },
42 'slaveList': ['ABTest-iPhone-0'],
45 'builder': 'ABTest-iPad-RunBenchmark-Tests',
46 'arguments': { 'forcescheduler': 'ABTest-iPad-RunBenchmark-Tests-ForceScheduler' },
47 'slaveList': ['ABTest-iPad-0', 'ABTest-iPad-1'],
51 {'type': 'speedometer', 'builder': 'iPhone-bench', 'platform': 'iPhone'},
52 {'type': 'jetstream', 'builder': 'iPhone-bench', 'platform': 'iPhone'},
53 {'type': 'dromaeo-dom', 'builder': 'iPhone-bench', 'platform': 'iPhone'},
55 {'type': 'speedometer', 'builder': 'iPad-bench', 'platform': 'iPad'},
56 {'type': 'jetstream', 'builder': 'iPad-bench', 'platform': 'iPad'},
61 function sampleiOSConfigWithExpansions()
64 "triggerableName": "build-webkit-ios",
68 "webkit-revision": {"root": "WebKit"},
69 "os-version": {"root": "iOS"}
71 "buildRequestArgument": "build-request-id"
75 "test": ["PLT-iPhone"],
76 "arguments": {"test_name": "plt"}
80 "arguments": {"test_name": "plt"}
83 "test": ["Speedometer"],
84 "arguments": {"tests": "speedometer"}
89 "builder": "iPhone AB Tests",
90 "arguments": {"forcescheduler": "force-iphone-ab-tests"}
93 "builder": "iPad AB Tests",
94 "arguments": {"forcescheduler": "force-ipad-ab-tests"}
100 "platforms": ["iPhone", "iOS 10 iPhone"],
101 "types": ["iphone-plt", "speedometer"],
105 "platforms": ["iPad"],
106 "types": ["ipad-plt", "speedometer"],
113 let sampleRootSetData = {
116 'time': 1456955807334,
117 'repository': 'WebKit',
118 'revision': '197463',
122 'time': 1456931874000,
123 'repository': 'Shared',
128 function smallConfiguration()
131 'builder': 'some builder',
132 'platform': 'Some platform',
133 'test': ['Some test'],
135 'buildRequestArgument': 'id'};
138 function smallPendingBuild()
141 'builderName': 'some builder',
147 'codebase': 'WebKit',
156 function smallInProgressBuild()
159 'builderName': 'some builder',
168 'codebase': 'WebKit',
177 function smallFinishedBuild()
180 'builderName': 'some builder',
189 'codebase': 'WebKit',
199 function createSampleBuildRequest(platform, test)
201 assert(platform instanceof Platform);
202 assert(test instanceof Test);
204 let rootSet = RootSet.ensureSingleton('4197', {roots: [
205 {'id': '111127', 'time': 1456955807334, 'repository': MockModels.webkit, 'revision': '197463'},
206 {'id': '111237', 'time': 1456931874000, 'repository': MockModels.sharedRepository, 'revision': '80229'},
207 {'id': '88930', 'time': 0, 'repository': MockModels.ios, 'revision': '13A452'},
210 let request = BuildRequest.ensureSingleton('16733-' + platform.id(), {'rootSet': rootSet, 'status': 'pending', 'platform': platform, 'test': test});
214 function samplePendingBuild(buildRequestId, buildTime, slaveName)
217 'builderName': 'ABTest-iPad-RunBenchmark-Tests',
220 ['build_request_id', buildRequestId || '16733', 'Force Build Form'],
221 ['desired_image', '13A452', 'Force Build Form'],
222 ['owner', '<unknown>', 'Force Build Form'],
223 ['test_name', 'speedometer', 'Force Build Form'],
224 ['reason', 'force build','Force Build Form'],
227 JSON.stringify(sampleRootSetData),
230 ['slavename', slaveName, ''],
231 ['scheduler', 'ABTest-iPad-RunBenchmark-Tests-ForceScheduler', 'Scheduler']
236 'codebase': 'compiler-rt',
242 'submittedAt': buildTime || 1458704983
246 function sampleInProgressBuild(slaveName)
250 'builderName': 'ABTest-iPad-RunBenchmark-Tests',
252 'eta': 0.26548067698460565,
253 'expectations': [['output', 845, 1315.0]],
257 'logs': [['stdio', 'https://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/614/steps/Some%20step/logs/stdio']],
259 'results': [null,[]],
263 'times': [1458718657.581628, null],
266 'eta': 6497.991612434387,
267 'logs': [['stdio','https://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/614/steps/shell/logs/stdio']],
270 ['build_request_id', '16733', 'Force Build Form'],
271 ['buildername', 'ABTest-iPad-RunBenchmark-Tests', 'Builder'],
272 ['buildnumber', 614, 'Build'],
273 ['desired_image', '13A452', 'Force Build Form'],
274 ['owner', '<unknown>', 'Force Build Form'],
275 ['reason', 'force build', 'Force Build Form'],
276 ['roots_dict', JSON.stringify(sampleRootSetData), 'Force Build Form'],
277 ['scheduler', 'ABTest-iPad-RunBenchmark-Tests-ForceScheduler', 'Scheduler'],
278 ['slavename', slaveName || 'ABTest-iPad-0', 'BuildSlave'],
280 'reason': 'A build was forced by \'<unknown>\': force build',
282 'slave': 'ABTest-iPad-0',
283 'sourceStamps': [{'branch': '', 'changes': [], 'codebase': 'compiler-rt', 'hasPatch': false, 'project': '', 'repository': '', 'revision': ''}],
287 'expectations': [['output',2309,2309.0]],
291 'logs': [['stdio', 'https://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/614/steps/shell/logs/stdio']],
292 'name': 'Finished step',
297 'times': [1458718655.419865, 1458718655.453633],
301 'eta': 0.26548067698460565,
302 'expectations': [['output', 845, 1315.0]],
306 'logs': [['stdio', 'https://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/614/steps/Some%20step/logs/stdio']],
308 'results': [null,[]],
312 'times': [1458718657.581628, null],
317 'expectations': [['output', null, null]],
322 'name': 'Some other step',
323 'results': [null, []],
327 'times': [null, null],
332 'times': [1458718655.415821, null]
336 function sampleFinishedBuild(buildRequestId, slaveName)
340 'builderName': 'ABTest-iPad-RunBenchmark-Tests',
343 'logs': [['stdio','https://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/1755/steps/shell/logs/stdio']],
346 ['build_request_id', buildRequestId || '18935', 'Force Build Form'],
347 ['buildername', 'ABTest-iPad-RunBenchmark-Tests', 'Builder'],
348 ['buildnumber', 1755, 'Build'],
349 ['desired_image', '13A452', 'Force Build Form'],
350 ['owner', '<unknown>', 'Force Build Form'],
351 ['reason', 'force build', 'Force Build Form'],
352 ['roots_dict', JSON.stringify(sampleRootSetData), 'Force Build Form'],
353 ['scheduler', 'ABTest-iPad-RunBenchmark-Tests-ForceScheduler', 'Scheduler'],
354 ['slavename', slaveName || 'ABTest-iPad-0', 'BuildSlave'],
356 'reason': 'A build was forced by \'<unknown>\': force build',
358 'slave': 'ABTest-iPad-0',
359 'sourceStamps': [{'branch': '', 'changes': [], 'codebase': 'compiler-rt', 'hasPatch': false, 'project': '', 'repository': '', 'revision': ''}],
363 'expectations': [['output',2309,2309.0]],
367 'logs': [['stdio', 'https://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/614/steps/shell/logs/stdio']],
368 'name': 'Finished step',
373 'times': [1458718655.419865, 1458718655.453633],
378 'expectations': [['output', 845, 1315.0]],
382 'logs': [['stdio', 'https://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/614/steps/Some%20step/logs/stdio']],
384 'results': [null,[]],
388 'times': [1458718657.581628, null],
393 'expectations': [['output', null, null]],
398 'name': 'Some other step',
399 'results': [null, []],
403 'times': [null, null],
408 'times': [1458937478.25837, 1458946147.173785]
412 describe('BuildbotSyncer', function () {
414 let requests = MockRemoteAPI.inject('http://build.webkit.org');
416 describe('_loadConfig', function () {
418 it('should create BuildbotSyncer objects for a configuration that specify all required options', function () {
419 let syncers = BuildbotSyncer._loadConfig(MockRemoteAPI, {'configurations': [smallConfiguration()]});
420 assert.equal(syncers.length, 1);
423 it('should throw when some required options are missing', function () {
424 assert.throws(function () {
425 let config = smallConfiguration();
426 delete config['builder'];
427 BuildbotSyncer._loadConfig(MockRemoteAPI, {'configurations': [config]});
428 }, 'builder should be a required option');
429 assert.throws(function () {
430 let config = smallConfiguration();
431 delete config['platform'];
432 BuildbotSyncer._loadConfig(MockRemoteAPI, {'configurations': [config]});
433 }, 'platform should be a required option');
434 assert.throws(function () {
435 let config = smallConfiguration();
436 delete config['test'];
437 BuildbotSyncer._loadConfig(MockRemoteAPI, {'configurations': [config]});
438 }, 'test should be a required option');
439 assert.throws(function () {
440 let config = smallConfiguration();
441 delete config['arguments'];
442 BuildbotSyncer._loadConfig(MockRemoteAPI, {'configurations': [config]});
444 assert.throws(function () {
445 let config = smallConfiguration();
446 delete config['buildRequestArgument'];
447 BuildbotSyncer._loadConfig(MockRemoteAPI, {'configurations': [config]});
451 it('should throw when a test name is not an array of strings', function () {
452 assert.throws(function () {
453 let config = smallConfiguration();
454 config.test = 'some test';
455 BuildbotSyncer._loadConfig(MockRemoteAPI, {'configurations': [config]});
457 assert.throws(function () {
458 let config = smallConfiguration();
460 BuildbotSyncer._loadConfig(MockRemoteAPI, {'configurations': [config]});
464 it('should throw when arguments is not an object', function () {
465 assert.throws(function () {
466 let config = smallConfiguration();
467 config.arguments = 'hello';
468 BuildbotSyncer._loadConfig(MockRemoteAPI, {'configurations': [config]});
472 it('should throw when arguments\'s values are malformed', function () {
473 assert.throws(function () {
474 let config = smallConfiguration();
475 config.arguments = {'some': {'root': 'some root', 'rootsExcluding': ['other root']}};
476 BuildbotSyncer._loadConfig(RemoteAPI, {'configurations': [config]});
478 assert.throws(function () {
479 let config = smallConfiguration();
480 config.arguments = {'some': {'otherKey': 'some root'}};
481 BuildbotSyncer._loadConfig(RemoteAPI, {'configurations': [config]});
483 assert.throws(function () {
484 let config = smallConfiguration();
485 config.arguments = {'some': {'root': ['a', 'b']}};
486 BuildbotSyncer._loadConfig(RemoteAPI, {'configurations': [config]});
488 assert.throws(function () {
489 let config = smallConfiguration();
490 config.arguments = {'some': {'root': 1}};
491 BuildbotSyncer._loadConfig(RemoteAPI, {'configurations': [config]});
493 assert.throws(function () {
494 let config = smallConfiguration();
495 config.arguments = {'some': {'rootsExcluding': 'a'}};
496 BuildbotSyncer._loadConfig(RemoteAPI, {'configurations': [config]});
498 assert.throws(function () {
499 let config = smallConfiguration();
500 config.arguments = {'some': {'rootsExcluding': [1]}};
501 BuildbotSyncer._loadConfig(RemoteAPI, {'configurations': [config]});
505 it('should create BuildbotSyncer objects for valid configurations', function () {
506 let syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig());
507 assert.equal(syncers.length, 2);
508 assert.ok(syncers[0] instanceof BuildbotSyncer);
509 assert.ok(syncers[1] instanceof BuildbotSyncer);
512 it('should parse builder names correctly', function () {
513 let syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig());
514 assert.equal(syncers[0].builderName(), 'ABTest-iPhone-RunBenchmark-Tests');
515 assert.equal(syncers[1].builderName(), 'ABTest-iPad-RunBenchmark-Tests');
518 it('should parse test configurations correctly', function () {
519 let syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig());
521 let configurations = syncers[0].testConfigurations();
522 assert.equal(configurations.length, 3);
523 assert.equal(configurations[0].platform, MockModels.iphone);
524 assert.equal(configurations[0].test, MockModels.speedometer);
525 assert.equal(configurations[1].platform, MockModels.iphone);
526 assert.equal(configurations[1].test, MockModels.jetstream);
527 assert.equal(configurations[2].platform, MockModels.iphone);
528 assert.equal(configurations[2].test, MockModels.domcore);
530 configurations = syncers[1].testConfigurations();
531 assert.equal(configurations.length, 2);
532 assert.equal(configurations[0].platform, MockModels.ipad);
533 assert.equal(configurations[0].test, MockModels.speedometer);
534 assert.equal(configurations[1].platform, MockModels.ipad);
535 assert.equal(configurations[1].test, MockModels.jetstream);
538 it('should parse test configurations with types and platforms expansions correctly', function () {
539 let syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfigWithExpansions());
541 assert.equal(syncers.length, 2);
543 let configurations = syncers[0].testConfigurations();
544 assert.equal(configurations.length, 4);
545 assert.equal(configurations[0].platform, MockModels.iphone);
546 assert.equal(configurations[0].test, MockModels.iPhonePLT);
547 assert.equal(configurations[1].platform, MockModels.iOS10iPhone);
548 assert.equal(configurations[1].test, MockModels.iPhonePLT);
549 assert.equal(configurations[2].platform, MockModels.iphone);
550 assert.equal(configurations[2].test, MockModels.speedometer);
551 assert.equal(configurations[3].platform, MockModels.iOS10iPhone);
552 assert.equal(configurations[3].test, MockModels.speedometer);
554 configurations = syncers[1].testConfigurations();
555 assert.equal(configurations.length, 2);
556 assert.equal(configurations[0].platform, MockModels.ipad);
557 assert.equal(configurations[0].test, MockModels.iPadPLT);
558 assert.equal(configurations[1].platform, MockModels.ipad);
559 assert.equal(configurations[1].test, MockModels.speedometer);
563 describe('_propertiesForBuildRequest', function () {
564 it('should include all properties specified in a given configuration', function () {
565 let syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig());
566 let properties = syncers[0]._propertiesForBuildRequest(createSampleBuildRequest(MockModels.iphone, MockModels.speedometer));
567 assert.deepEqual(Object.keys(properties), ['desired_image', 'roots_dict', 'test_name', 'forcescheduler', 'build_request_id']);
570 it('should preserve non-parametric property values', function () {
571 let syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig());
572 let properties = syncers[0]._propertiesForBuildRequest(createSampleBuildRequest(MockModels.iphone, MockModels.speedometer));
573 assert.equal(properties['test_name'], 'speedometer');
574 assert.equal(properties['forcescheduler'], 'ABTest-iPhone-RunBenchmark-Tests-ForceScheduler');
576 properties = syncers[1]._propertiesForBuildRequest(createSampleBuildRequest(MockModels.ipad, MockModels.jetstream));
577 assert.equal(properties['test_name'], 'jetstream');
578 assert.equal(properties['forcescheduler'], 'ABTest-iPad-RunBenchmark-Tests-ForceScheduler');
581 it('should resolve "root"', function () {
582 let syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig());
583 let properties = syncers[0]._propertiesForBuildRequest(createSampleBuildRequest(MockModels.iphone, MockModels.speedometer));
584 assert.equal(properties['desired_image'], '13A452');
587 it('should resolve "rootsExcluding"', function () {
588 let syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig());
589 let properties = syncers[0]._propertiesForBuildRequest(createSampleBuildRequest(MockModels.iphone, MockModels.speedometer));
590 assert.equal(properties['roots_dict'], JSON.stringify(sampleRootSetData));
593 it('should set the property for the build request id', function () {
594 let syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig());
595 let request = createSampleBuildRequest(MockModels.iphone, MockModels.speedometer);
596 let properties = syncers[0]._propertiesForBuildRequest(request);
597 assert.equal(properties['build_request_id'], request.id());
601 describe('pullBuildbot', function () {
602 it('should fetch pending builds from the right URL', function () {
603 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
604 assert.equal(syncer.builderName(), 'ABTest-iPad-RunBenchmark-Tests');
605 let expectedURL = '/json/builders/ABTest-iPad-RunBenchmark-Tests/pendingBuilds';
606 assert.equal(syncer.pathForPendingBuildsJSON(), expectedURL);
607 syncer.pullBuildbot();
608 assert.equal(requests.length, 1);
609 assert.equal(requests[0].url, expectedURL);
612 it('should fetch recent builds once pending builds have been fetched', function (done) {
613 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
614 assert.equal(syncer.builderName(), 'ABTest-iPad-RunBenchmark-Tests');
616 syncer.pullBuildbot(1);
617 assert.equal(requests.length, 1);
618 assert.equal(requests[0].url, '/json/builders/ABTest-iPad-RunBenchmark-Tests/pendingBuilds');
619 requests[0].resolve([]);
620 Promise.resolve().then(function () {
621 assert.equal(requests.length, 2);
622 assert.equal(requests[1].url, '/json/builders/ABTest-iPad-RunBenchmark-Tests/builds/?select=-1');
627 it('should fetch the right number of recent builds', function (done) {
628 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
630 syncer.pullBuildbot(3);
631 assert.equal(requests.length, 1);
632 assert.equal(requests[0].url, '/json/builders/ABTest-iPad-RunBenchmark-Tests/pendingBuilds');
633 requests[0].resolve([]);
634 Promise.resolve().then(function () {
635 assert.equal(requests.length, 2);
636 assert.equal(requests[1].url, '/json/builders/ABTest-iPad-RunBenchmark-Tests/builds/?select=-1&select=-2&select=-3');
641 it('should create BuildbotBuildEntry for pending builds', function (done) {
642 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
643 let promise = syncer.pullBuildbot();
644 requests[0].resolve([samplePendingBuild()]);
645 promise.then(function (entries) {
646 assert.equal(entries.length, 1);
647 let entry = entries[0];
648 assert.ok(entry instanceof BuildbotBuildEntry);
649 assert.ok(!entry.buildNumber());
650 assert.ok(!entry.slaveName());
651 assert.equal(entry.buildRequestId(), 16733);
652 assert.ok(entry.isPending());
653 assert.ok(!entry.isInProgress());
654 assert.ok(!entry.hasFinished());
655 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/');
660 it('should create BuildbotBuildEntry for in-progress builds', function (done) {
661 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
663 let promise = syncer.pullBuildbot(1);
664 assert.equal(requests.length, 1);
665 requests[0].resolve([]);
666 Promise.resolve().then(function () {
667 assert.equal(requests.length, 2);
668 requests[1].resolve({[-1]: sampleInProgressBuild()});
671 promise.then(function (entries) {
672 assert.equal(entries.length, 1);
673 let entry = entries[0];
674 assert.ok(entry instanceof BuildbotBuildEntry);
675 assert.equal(entry.buildNumber(), 614);
676 assert.equal(entry.slaveName(), 'ABTest-iPad-0');
677 assert.equal(entry.buildRequestId(), 16733);
678 assert.ok(!entry.isPending());
679 assert.ok(entry.isInProgress());
680 assert.ok(!entry.hasFinished());
681 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/614');
686 it('should create BuildbotBuildEntry for finished builds', function (done) {
687 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
689 let promise = syncer.pullBuildbot(1);
690 assert.equal(requests.length, 1);
691 requests[0].resolve([]);
692 Promise.resolve().then(function () {
693 assert.equal(requests.length, 2);
694 requests[1].resolve({[-1]: sampleFinishedBuild()});
697 promise.then(function (entries) {
698 assert.deepEqual(entries.length, 1);
699 let entry = entries[0];
700 assert.ok(entry instanceof BuildbotBuildEntry);
701 assert.equal(entry.buildNumber(), 1755);
702 assert.equal(entry.slaveName(), 'ABTest-iPad-0');
703 assert.equal(entry.buildRequestId(), 18935);
704 assert.ok(!entry.isPending());
705 assert.ok(!entry.isInProgress());
706 assert.ok(entry.hasFinished());
707 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/1755');
712 it('should create BuildbotBuildEntry for mixed pending, in-progress, finished, and missing builds', function (done) {
713 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
715 let promise = syncer.pullBuildbot(5);
716 assert.equal(requests.length, 1);
718 requests[0].resolve([samplePendingBuild(123)]);
720 Promise.resolve().then(function () {
721 assert.equal(requests.length, 2);
722 requests[1].resolve({[-1]: sampleFinishedBuild(), [-2]: {'error': 'Not available'}, [-4]: sampleInProgressBuild()});
725 promise.then(function (entries) {
726 assert.deepEqual(entries.length, 3);
728 let entry = entries[0];
729 assert.ok(entry instanceof BuildbotBuildEntry);
730 assert.equal(entry.buildNumber(), null);
731 assert.equal(entry.slaveName(), null);
732 assert.equal(entry.buildRequestId(), 123);
733 assert.ok(entry.isPending());
734 assert.ok(!entry.isInProgress());
735 assert.ok(!entry.hasFinished());
736 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/');
739 assert.ok(entry instanceof BuildbotBuildEntry);
740 assert.equal(entry.buildNumber(), 614);
741 assert.equal(entry.slaveName(), 'ABTest-iPad-0');
742 assert.equal(entry.buildRequestId(), 16733);
743 assert.ok(!entry.isPending());
744 assert.ok(entry.isInProgress());
745 assert.ok(!entry.hasFinished());
746 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/614');
749 assert.ok(entry instanceof BuildbotBuildEntry);
750 assert.equal(entry.buildNumber(), 1755);
751 assert.equal(entry.slaveName(), 'ABTest-iPad-0');
752 assert.equal(entry.buildRequestId(), 18935);
753 assert.ok(!entry.isPending());
754 assert.ok(!entry.isInProgress());
755 assert.ok(entry.hasFinished());
756 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/1755');
762 it('should sort BuildbotBuildEntry by order', function (done) {
763 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
765 let promise = syncer.pullBuildbot(5);
766 assert.equal(requests.length, 1);
768 requests[0].resolve([samplePendingBuild(456, 2), samplePendingBuild(123, 1)]);
770 Promise.resolve().then(function () {
771 assert.equal(requests.length, 2);
772 requests[1].resolve({[-3]: sampleFinishedBuild(), [-1]: {'error': 'Not available'}, [-2]: sampleInProgressBuild()});
775 promise.then(function (entries) {
776 assert.deepEqual(entries.length, 4);
778 let entry = entries[0];
779 assert.ok(entry instanceof BuildbotBuildEntry);
780 assert.equal(entry.buildNumber(), null);
781 assert.equal(entry.slaveName(), null);
782 assert.equal(entry.buildRequestId(), 123);
783 assert.ok(entry.isPending());
784 assert.ok(!entry.isInProgress());
785 assert.ok(!entry.hasFinished());
786 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/');
789 assert.ok(entry instanceof BuildbotBuildEntry);
790 assert.equal(entry.buildNumber(), null);
791 assert.equal(entry.slaveName(), null);
792 assert.equal(entry.buildRequestId(), 456);
793 assert.ok(entry.isPending());
794 assert.ok(!entry.isInProgress());
795 assert.ok(!entry.hasFinished());
796 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/');
799 assert.ok(entry instanceof BuildbotBuildEntry);
800 assert.equal(entry.buildNumber(), 614);
801 assert.equal(entry.slaveName(), 'ABTest-iPad-0');
802 assert.equal(entry.buildRequestId(), 16733);
803 assert.ok(!entry.isPending());
804 assert.ok(entry.isInProgress());
805 assert.ok(!entry.hasFinished());
806 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/614');
809 assert.ok(entry instanceof BuildbotBuildEntry);
810 assert.equal(entry.buildNumber(), 1755);
811 assert.equal(entry.slaveName(), 'ABTest-iPad-0');
812 assert.equal(entry.buildRequestId(), 18935);
813 assert.ok(!entry.isPending());
814 assert.ok(!entry.isInProgress());
815 assert.ok(entry.hasFinished());
816 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', function (done) {
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 Promise.resolve().then(function () {
831 assert.equal(requests.length, 2);
832 requests[1].resolve({[-1]: sampleInProgressBuild()});
835 promise.then(function (entries) {
836 assert.equal(entries.length, 1);
838 let entry = entries[0];
839 assert.ok(entry instanceof BuildbotBuildEntry);
840 assert.equal(entry.buildNumber(), 614);
841 assert.equal(entry.slaveName(), 'ABTest-iPad-0');
842 assert.equal(entry.buildRequestId(), 16733);
843 assert.ok(!entry.isPending());
844 assert.ok(entry.isInProgress());
845 assert.ok(!entry.hasFinished());
846 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/614');
852 it('should override BuildbotBuildEntry for pending builds by finished builds', function (done) {
853 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
855 let promise = syncer.pullBuildbot(5);
856 assert.equal(requests.length, 1);
858 requests[0].resolve([samplePendingBuild()]);
860 Promise.resolve().then(function () {
861 assert.equal(requests.length, 2);
862 requests[1].resolve({[-1]: sampleFinishedBuild(16733)});
865 promise.then(function (entries) {
866 assert.equal(entries.length, 1);
868 let entry = entries[0];
869 assert.ok(entry instanceof BuildbotBuildEntry);
870 assert.equal(entry.buildNumber(), 1755);
871 assert.equal(entry.slaveName(), 'ABTest-iPad-0');
872 assert.equal(entry.buildRequestId(), 16733);
873 assert.ok(!entry.isPending());
874 assert.ok(!entry.isInProgress());
875 assert.ok(entry.hasFinished());
876 assert.equal(entry.url(), 'http://build.webkit.org/builders/ABTest-iPad-RunBenchmark-Tests/builds/1755');
883 describe('scheduleRequest', function () {
884 it('should schedule a build request on a specified slave', function (done) {
885 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[0];
887 syncer.scheduleRequest(createSampleBuildRequest(MockModels.iphone, MockModels.speedometer), 'some-slave');
888 Promise.resolve().then(function () {
889 assert.equal(requests.length, 1);
890 assert.equal(requests[0].url, '/builders/ABTest-iPhone-RunBenchmark-Tests/force');
891 assert.equal(requests[0].method, 'POST');
892 assert.deepEqual(requests[0].data, {
893 'build_request_id': '16733-' + MockModels.iphone.id(),
894 'desired_image': '13A452',
895 'forcescheduler': 'ABTest-iPhone-RunBenchmark-Tests-ForceScheduler',
896 'roots_dict': '{"WebKit":{"id":"111127","time":1456955807334,"repository":"WebKit","revision":"197463"},'
897 + '"Shared":{"id":"111237","time":1456931874000,"repository":"Shared","revision":"80229"}}',
898 'slavename': 'some-slave',
899 'test_name': 'speedometer'
906 describe('scheduleRequestInGroupIfAvailable', function () {
908 function pullBuildbotWithAssertion(syncer, pendingBuilds, inProgressAndFinishedBuilds)
910 let promise = syncer.pullBuildbot(5);
911 assert.equal(requests.length, 1);
912 requests[0].resolve(pendingBuilds);
913 return Promise.resolve().then(function () {
914 assert.equal(requests.length, 2);
915 requests[1].resolve(inProgressAndFinishedBuilds);
917 }).then(function () {
922 it('should schedule a build if builder has no builds if slaveList is not specified', function (done) {
923 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, {'configurations': [smallConfiguration()]})[0];
925 pullBuildbotWithAssertion(syncer, [], {}).then(function () {
926 syncer.scheduleRequestInGroupIfAvailable(createSampleBuildRequest(MockModels.somePlatform, MockModels.someTest));
927 }).then(function () {
928 assert.equal(requests.length, 1);
929 assert.equal(requests[0].url, '/builders/some%20builder/force');
930 assert.equal(requests[0].method, 'POST');
931 assert.deepEqual(requests[0].data, {id: '16733-' + MockModels.somePlatform.id()});
936 it('should schedule a build if builder only has finished builds if slaveList is not specified', function (done) {
937 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, {'configurations': [smallConfiguration()]})[0];
939 pullBuildbotWithAssertion(syncer, [], {[-1]: smallFinishedBuild()}).then(function () {
940 syncer.scheduleRequestInGroupIfAvailable(createSampleBuildRequest(MockModels.somePlatform, MockModels.someTest));
941 }).then(function () {
942 assert.equal(requests.length, 1);
943 assert.equal(requests[0].url, '/builders/some%20builder/force');
944 assert.equal(requests[0].method, 'POST');
945 assert.deepEqual(requests[0].data, {id: '16733-' + MockModels.somePlatform.id()});
950 it('should not schedule a build if builder has a pending build if slaveList is not specified', function (done) {
951 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, {'configurations': [smallConfiguration()]})[0];
953 pullBuildbotWithAssertion(syncer, [smallPendingBuild()], {}).then(function () {
954 syncer.scheduleRequestInGroupIfAvailable(createSampleBuildRequest(MockModels.somePlatform, MockModels.someTest));
955 }).then(function () {
956 assert.equal(requests.length, 0);
961 it('should schedule a build if builder does not have pending or completed builds on the matching slave', function (done) {
962 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[0];
964 pullBuildbotWithAssertion(syncer, [], {}).then(function () {
965 syncer.scheduleRequestInGroupIfAvailable(createSampleBuildRequest(MockModels.iphone, MockModels.speedometer));
966 }).then(function () {
967 assert.equal(requests.length, 1);
968 assert.equal(requests[0].url, '/builders/ABTest-iPhone-RunBenchmark-Tests/force');
969 assert.equal(requests[0].method, 'POST');
974 it('should schedule a build if builder only has finished builds on the matching slave', function (done) {
975 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
977 pullBuildbotWithAssertion(syncer, [], {[-1]: sampleFinishedBuild()}).then(function () {
978 syncer.scheduleRequestInGroupIfAvailable(createSampleBuildRequest(MockModels.ipad, MockModels.speedometer));
979 }).then(function () {
980 assert.equal(requests.length, 1);
981 assert.equal(requests[0].url, '/builders/ABTest-iPad-RunBenchmark-Tests/force');
982 assert.equal(requests[0].method, 'POST');
987 it('should not schedule a build if builder has a pending build on the maching slave', function (done) {
988 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
990 pullBuildbotWithAssertion(syncer, [samplePendingBuild()], {}).then(function () {
991 syncer.scheduleRequestInGroupIfAvailable(createSampleBuildRequest(MockModels.ipad, MockModels.speedometer));
992 }).then(function () {
993 assert.equal(requests.length, 0);
998 it('should schedule a build if builder only has a pending build on a non-maching slave', function (done) {
999 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
1001 pullBuildbotWithAssertion(syncer, [samplePendingBuild(1, 1, 'another-slave')], {}).then(function () {
1002 syncer.scheduleRequestInGroupIfAvailable(createSampleBuildRequest(MockModels.ipad, MockModels.speedometer));
1003 }).then(function () {
1004 assert.equal(requests.length, 1);
1009 it('should schedule a build if builder only has an in-progress build on the matching slave', function (done) {
1010 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
1012 pullBuildbotWithAssertion(syncer, [], {[-1]: sampleInProgressBuild()}).then(function () {
1013 syncer.scheduleRequestInGroupIfAvailable(createSampleBuildRequest(MockModels.ipad, MockModels.speedometer));
1014 }).then(function () {
1015 assert.equal(requests.length, 1);
1020 it('should schedule a build if builder has an in-progress build on another slave', function (done) {
1021 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
1023 pullBuildbotWithAssertion(syncer, [], {[-1]: sampleInProgressBuild('other-slave')}).then(function () {
1024 syncer.scheduleRequestInGroupIfAvailable(createSampleBuildRequest(MockModels.ipad, MockModels.speedometer));
1025 }).then(function () {
1026 assert.equal(requests.length, 1);
1031 it('should not schedule a build if the request does not match any configuration', function (done) {
1032 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[0];
1034 pullBuildbotWithAssertion(syncer, [], {}).then(function () {
1035 syncer.scheduleRequestInGroupIfAvailable(createSampleBuildRequest(MockModels.ipad, MockModels.speedometer));
1036 }).then(function () {
1037 assert.equal(requests.length, 0);
1042 it('should not schedule a build if a new request had been submitted to the same slave', function (done) {
1043 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
1045 pullBuildbotWithAssertion(syncer, [], {}).then(function () {
1046 syncer.scheduleRequest(createSampleBuildRequest(MockModels.ipad, MockModels.speedometer), 'ABTest-iPad-0');
1047 syncer.scheduleRequest(createSampleBuildRequest(MockModels.ipad, MockModels.speedometer), 'ABTest-iPad-1');
1048 }).then(function () {
1049 assert.equal(requests.length, 2);
1050 syncer.scheduleRequestInGroupIfAvailable(createSampleBuildRequest(MockModels.ipad, MockModels.speedometer));
1051 }).then(function () {
1052 assert.equal(requests.length, 2);
1057 it('should schedule a build if a new request had been submitted to another slave', function (done) {
1058 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
1060 pullBuildbotWithAssertion(syncer, [], {}).then(function () {
1061 syncer.scheduleRequest(createSampleBuildRequest(MockModels.ipad, MockModels.speedometer), 'ABTest-iPad-0');
1062 }).then(function () {
1063 assert.equal(requests.length, 1);
1064 syncer.scheduleRequestInGroupIfAvailable(createSampleBuildRequest(MockModels.ipad, MockModels.speedometer), 'ABTest-iPad-1');
1065 }).then(function () {
1066 assert.equal(requests.length, 2);
1071 it('should not schedule a build if a new request had been submitted to the same builder without slaveList', function (done) {
1072 let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, {'configurations': [smallConfiguration()]})[0];
1074 pullBuildbotWithAssertion(syncer, [], {}).then(function () {
1075 syncer.scheduleRequest(createSampleBuildRequest(MockModels.somePlatform, MockModels.someTest), null);
1076 }).then(function () {
1077 assert.equal(requests.length, 1);
1078 syncer.scheduleRequestInGroupIfAvailable(createSampleBuildRequest(MockModels.somePlatform, MockModels.someTest));
1079 }).then(function () {
1080 assert.equal(requests.length, 1);