Add support for builderNameToIDMap in BuildbotSyncer
authoraakash_jain@apple.com <aakash_jain@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 19 Jan 2018 05:42:39 +0000 (05:42 +0000)
committeraakash_jain@apple.com <aakash_jain@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 19 Jan 2018 05:42:39 +0000 (05:42 +0000)
https://bugs.webkit.org/show_bug.cgi?id=176526

Reviewed by Ryosuke Niwa.

* tools/js/buildbot-syncer.js:
(BuildbotSyncer._loadConfig): Added builderNameToIDMap parameter. It would be used later on.
(BuildbotSyncer._resolveBuildersWithPlatforms): Ditto.
(BuildbotSyncer._validateAndMergeConfig): Added builderID as a valid config parameter.
* tools/js/buildbot-triggerable.js:
(BuildbotTriggerable.prototype.getBuilderNameToIDMap): Method to fetch BuilderNameToIDMap from Buildbot 0.9.
(BuildbotTriggerable.prototype.getBuilderNameToIDMapDeprecated): Method to fetch BuilderNameToIDMap from Buildbot 0.8.
(BuildbotTriggerable.prototype.initSyncers): Updated to use getBuilderNameToIDMap.
* unit-tests/buildbot-syncer-tests.js: Updated unit-tests.
* server-tests/resources/mock-data.js:
(MockData.buildbotBuildersURLDeprecated): URL for fetching Builders list.
(MockData.buildbotBuildersURL): Ditto for Buildbot 0.9
(MockData.mockBuildbotBuildersDeprecated): Sample builders data for Buildbot 0.8
(MockData.mockBuildbotBuilders): Ditto for Buildbot 0.9
* server-tests/tools-buildbot-triggerable-tests.js: Added test for getBuilderNameToIDMap. Updated tests to handle
newly added promise for fetching builders list from Buildbot.
* server-tests/tools-sync-buildbot-integration-tests.js: Ditto.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@227184 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Websites/perf.webkit.org/ChangeLog
Websites/perf.webkit.org/server-tests/resources/mock-data.js
Websites/perf.webkit.org/server-tests/tools-buildbot-triggerable-tests.js
Websites/perf.webkit.org/server-tests/tools-sync-buildbot-integration-tests.js
Websites/perf.webkit.org/tools/js/buildbot-syncer.js
Websites/perf.webkit.org/tools/js/buildbot-triggerable.js
Websites/perf.webkit.org/unit-tests/buildbot-syncer-tests.js

index e88c640..fa62b15 100644 (file)
@@ -1,3 +1,28 @@
+2018-01-18  Aakash Jain  <aakash_jain@apple.com>
+
+        Add support for builderNameToIDMap in BuildbotSyncer
+        https://bugs.webkit.org/show_bug.cgi?id=176526
+
+        Reviewed by Ryosuke Niwa.
+
+        * tools/js/buildbot-syncer.js:
+        (BuildbotSyncer._loadConfig): Added builderNameToIDMap parameter. It would be used later on.
+        (BuildbotSyncer._resolveBuildersWithPlatforms): Ditto.
+        (BuildbotSyncer._validateAndMergeConfig): Added builderID as a valid config parameter.
+        * tools/js/buildbot-triggerable.js:
+        (BuildbotTriggerable.prototype.getBuilderNameToIDMap): Method to fetch BuilderNameToIDMap from Buildbot 0.9.
+        (BuildbotTriggerable.prototype.getBuilderNameToIDMapDeprecated): Method to fetch BuilderNameToIDMap from Buildbot 0.8.
+        (BuildbotTriggerable.prototype.initSyncers): Updated to use getBuilderNameToIDMap.
+        * unit-tests/buildbot-syncer-tests.js: Updated unit-tests.
+        * server-tests/resources/mock-data.js:
+        (MockData.buildbotBuildersURLDeprecated): URL for fetching Builders list.
+        (MockData.buildbotBuildersURL): Ditto for Buildbot 0.9
+        (MockData.mockBuildbotBuildersDeprecated): Sample builders data for Buildbot 0.8
+        (MockData.mockBuildbotBuilders): Ditto for Buildbot 0.9
+        * server-tests/tools-buildbot-triggerable-tests.js: Added test for getBuilderNameToIDMap. Updated tests to handle
+        newly added promise for fetching builders list from Buildbot.
+        * server-tests/tools-sync-buildbot-integration-tests.js: Ditto.
+
 2018-01-18  Ryosuke Niwa  <rniwa@webkit.org>
 
         Charts can be empty when values are all identical
index 73b7b37..be1c734 100644 (file)
@@ -17,6 +17,8 @@ MockData = {
     jscRepositoryId() { return 222; },
     gitWebkitRepositoryId() { return 111; },
     sharedRepositoryId() { return 14; },
+    buildbotBuildersURLDeprecated() {return '/json/builders'},
+    buildbotBuildersURL() {return '/api/v2/builders'},
     addMockConfiguration: function (db)
     {
         return Promise.all([
@@ -220,6 +222,60 @@ MockData = {
             ]
         }
     },
+    mockBuildbotBuildersDeprecated: function ()
+    {
+        return {
+            "some builder": {
+                "slaves": [ "some-slave-1" ]
+            },
+            "some-builder-1": {
+                "slaves": [ "some-slave-2" ]
+            },
+            "some builder 2": {
+                "slaves": [ "some-slave-3" ]
+            },
+            "other builder": {
+                "slaves": [ "some-slave-4" ]
+            },
+            "some tester": {
+                "slaves": [ "some-slave-5" ]
+            },
+            "another tester": {
+                "slaves": [ "some-slave-6" ]
+            }
+        }
+    },
+    mockBuildbotBuilders: function ()
+    {
+        return {
+            "builders": [
+                {
+                    "builderid": 1,
+                    "name": "some builder",
+                },
+                {
+                    "builderid": 2,
+                    "name": "some-builder-1",
+                },
+                {
+                    "builderid": 3,
+                    "name": "some builder 2",
+                },
+                {
+                    "builderid": 4,
+                    "name": "other builder",
+                },
+                {
+                    "builderid": 5,
+                    "name": "some tester",
+                },
+                {
+                    "builderid": 6,
+                    "name": "another tester",
+                }
+            ]
+        }
+    },
     pendingBuild(options)
     {
         options = options || {};
index df42bea..f331fbe 100644 (file)
@@ -36,6 +36,8 @@ describe('BuildbotTriggerable', function () {
                 const slaveInfo = {name: 'sync-slave', password: 'password'};
                 const triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
                 syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
+                assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURLDeprecated(), MockData.mockBuildbotBuildersDeprecated());
+                MockRemoteAPI.reset();
                 return MockRemoteAPI.waitForRequest();
             }).then(() => {
                 assert.equal(BuildRequest.all().length, 4);
@@ -90,6 +92,8 @@ describe('BuildbotTriggerable', function () {
                 let slaveInfo = {name: 'sync-slave', password: 'password'};
                 let triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
                 syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
+                assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURLDeprecated(), MockData.mockBuildbotBuildersDeprecated());
+                MockRemoteAPI.reset();
                 return MockRemoteAPI.waitForRequest();
             }).then(() => {
                 assert.equal(MockRemoteAPI.requests[0].method, 'GET');
@@ -138,6 +142,8 @@ describe('BuildbotTriggerable', function () {
                 const slaveInfo = {name: 'sync-slave', password: 'password'};
                 const triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
                 syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
+                assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURLDeprecated(), MockData.mockBuildbotBuildersDeprecated());
+                MockRemoteAPI.reset();
                 return MockRemoteAPI.waitForRequest();
             }).then(() => {
                 assert.equal(MockRemoteAPI.requests.length, 2);
@@ -217,6 +223,8 @@ describe('BuildbotTriggerable', function () {
                 let slaveInfo = {name: 'sync-slave', password: 'password'};
                 let triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
                 syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
+                assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURLDeprecated(), MockData.mockBuildbotBuildersDeprecated());
+                MockRemoteAPI.reset();
                 return MockRemoteAPI.waitForRequest();
             }).then(() => {
                 assert.equal(MockRemoteAPI.requests.length, 2);
@@ -289,6 +297,8 @@ describe('BuildbotTriggerable', function () {
                 const slaveInfo = {name: 'sync-slave', password: 'password'};
                 const triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
                 syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
+                assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURLDeprecated(), MockData.mockBuildbotBuildersDeprecated());
+                MockRemoteAPI.reset();
                 return MockRemoteAPI.waitForRequest();
             }).then(() => {
                 assert.equal(MockRemoteAPI.requests.length, 2);
@@ -368,6 +378,8 @@ describe('BuildbotTriggerable', function () {
                 let slaveInfo = {name: 'sync-slave', password: 'password'};
                 let triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
                 syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
+                assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURLDeprecated(), MockData.mockBuildbotBuildersDeprecated());
+                MockRemoteAPI.reset();
                 return MockRemoteAPI.waitForRequest();
             }).then(() => {
                 assert.equal(MockRemoteAPI.requests.length, 1);
@@ -431,6 +443,8 @@ describe('BuildbotTriggerable', function () {
                 const slaveInfo = {name: 'sync-slave', password: 'password'};
                 const triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
                 syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
+                assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURLDeprecated(), MockData.mockBuildbotBuildersDeprecated());
+                MockRemoteAPI.reset();
                 return MockRemoteAPI.waitForRequest();
             }).then(() => {
                 assert.equal(MockRemoteAPI.requests.length, 2);
@@ -529,6 +543,8 @@ describe('BuildbotTriggerable', function () {
                 const slaveInfo = {name: 'sync-slave', password: 'password'};
                 const triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
                 syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
+                assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURLDeprecated(), MockData.mockBuildbotBuildersDeprecated());
+                MockRemoteAPI.reset();
                 return MockRemoteAPI.waitForRequest();
             }).then(() => {
                 assert.equal(MockRemoteAPI.requests.length, 2);
@@ -630,6 +646,8 @@ describe('BuildbotTriggerable', function () {
                 const slaveInfo = {name: 'sync-slave', password: 'password'};
                 const triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
                 syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
+                assertRequestAndResolve(requests[0], 'GET', MockData.buildbotBuildersURLDeprecated(), MockData.mockBuildbotBuildersDeprecated());
+                MockRemoteAPI.reset();
                 return MockRemoteAPI.waitForRequest();
             }).then(() => {
                 assert.equal(requests.length, 1);
@@ -705,6 +723,8 @@ describe('BuildbotTriggerable', function () {
                 const slaveInfo = {name: 'sync-slave', password: 'password'};
                 triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
                 syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
+                assertRequestAndResolve(requests[0], 'GET', MockData.buildbotBuildersURLDeprecated(), MockData.mockBuildbotBuildersDeprecated());
+                MockRemoteAPI.reset();
                 return MockRemoteAPI.waitForRequest();
             }).then(() => {
                 assert.equal(requests.length, 1);
@@ -732,23 +752,24 @@ describe('BuildbotTriggerable', function () {
                 return syncPromise;
             }).then(() => {
                 syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
-                return MockRemoteAPI.waitForRequest();
-            }).then(() => {
-                assert.equal(requests.length, 6);
-                assertRequestAndResolve(requests[5], 'GET', '/json/builders/some-builder-1/pendingBuilds', []);
+                assertRequestAndResolve(requests[5], 'GET', MockData.buildbotBuildersURLDeprecated(), MockData.mockBuildbotBuildersDeprecated());
                 return MockRemoteAPI.waitForRequest();
             }).then(() => {
                 assert.equal(requests.length, 7);
-                assertRequestAndResolve(requests[6], 'GET', '/json/builders/some-builder-1/builds/?select=-1&select=-2',
-                    {[-1]: MockData.runningBuild({buildRequestId: 701}), [-2]: MockData.runningBuild({buildRequestId: 700})});
+                assertRequestAndResolve(requests[6], 'GET', '/json/builders/some-builder-1/pendingBuilds', []);
                 return MockRemoteAPI.waitForRequest();
             }).then(() => {
                 assert.equal(requests.length, 8);
-                assertRequestAndResolve(requests[7], 'GET', '/json/builders/some-builder-1/pendingBuilds', []);
+                assertRequestAndResolve(requests[7], 'GET', '/json/builders/some-builder-1/builds/?select=-1&select=-2',
+                    {[-1]: MockData.runningBuild({buildRequestId: 701}), [-2]: MockData.runningBuild({buildRequestId: 700})});
                 return MockRemoteAPI.waitForRequest();
             }).then(() => {
                 assert.equal(requests.length, 9);
-                assertRequestAndResolve(requests[8], 'GET', '/json/builders/some-builder-1/builds/?select=-1&select=-2',
+                assertRequestAndResolve(requests[8], 'GET', '/json/builders/some-builder-1/pendingBuilds', []);
+                return MockRemoteAPI.waitForRequest();
+            }).then(() => {
+                assert.equal(requests.length, 10);
+                assertRequestAndResolve(requests[9], 'GET', '/json/builders/some-builder-1/builds/?select=-1&select=-2',
                     {[-1]: MockData.runningBuild({buildRequestId: 701}), [-2]: MockData.runningBuild({buildRequestId: 700})});
                 return syncPromise;
             });
@@ -765,6 +786,8 @@ describe('BuildbotTriggerable', function () {
                 const slaveInfo = {name: 'sync-slave', password: 'password'};
                 const triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
                 syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
+                assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURLDeprecated(), MockData.mockBuildbotBuildersDeprecated());
+                MockRemoteAPI.reset();
                 return MockRemoteAPI.waitForRequest();
             }).then(() => {
                 assert.equal(MockRemoteAPI.requests.length, 1);
@@ -832,6 +855,8 @@ describe('BuildbotTriggerable', function () {
                 const slaveInfo = {name: 'sync-slave', password: 'password'};
                 const triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
                 syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
+                assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURLDeprecated(), MockData.mockBuildbotBuildersDeprecated());
+                MockRemoteAPI.reset();
                 return MockRemoteAPI.waitForRequest();
             }).then(() => {
                 assert.equal(MockRemoteAPI.requests.length, 1);
@@ -887,6 +912,8 @@ describe('BuildbotTriggerable', function () {
                 const slaveInfo = {name: 'sync-slave', password: 'password'};
                 const triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
                 syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
+                assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURLDeprecated(), MockData.mockBuildbotBuildersDeprecated());
+                MockRemoteAPI.reset();
                 return MockRemoteAPI.waitForRequest();
             }).then(() => {
                 assert.equal(MockRemoteAPI.requests.length, 1);
@@ -972,7 +999,9 @@ describe('BuildbotTriggerable', function () {
                 const logger = new MockLogger;
                 const slaveInfo = {name: 'sync-slave', password: 'password'};
                 const buildbotTriggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
-                return buildbotTriggerable.initSyncers().then(() => buildbotTriggerable.updateTriggerable());
+                const triggerablePromise = buildbotTriggerable.initSyncers().then(() => buildbotTriggerable.updateTriggerable());
+                assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURLDeprecated(), MockData.mockBuildbotBuildersDeprecated());
+                return triggerablePromise;
             }).then(() => refetchManifest()).then(() => {
                 assert.equal(Triggerable.all().length, 1);
 
@@ -997,7 +1026,9 @@ describe('BuildbotTriggerable', function () {
                 const logger = new MockLogger;
                 const slaveInfo = {name: 'sync-slave', password: 'password'};
                 const buildbotTriggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
-                return buildbotTriggerable.initSyncers().then(() => buildbotTriggerable.updateTriggerable());
+                const triggerablePromise = buildbotTriggerable.initSyncers().then(() => buildbotTriggerable.updateTriggerable());
+                assertRequestAndResolve(MockRemoteAPI.requests[1], 'GET', MockData.buildbotBuildersURLDeprecated(), MockData.mockBuildbotBuildersDeprecated());
+                return triggerablePromise;
             }).then(() => refetchManifest()).then(() => {
                 assert.equal(Triggerable.all().length, 1);
                 const groups = TriggerableRepositoryGroup.sortByName(Triggerable.all()[0].repositoryGroups());
@@ -1010,4 +1041,23 @@ describe('BuildbotTriggerable', function () {
         });
     });
 
+    describe('getBuilderNameToIDMap', () => {
+
+        it('should get Builder Name to ID Map', () => {
+            const config = MockData.mockTestSyncConfigWithSingleBuilder();
+            const logger = new MockLogger;
+            const slaveInfo = {name: 'sync-slave', password: 'password'};
+            const buildbotTriggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
+            const getBuilderNameToIDMapPromise = buildbotTriggerable.getBuilderNameToIDMap();
+            assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURL(), MockData.mockBuildbotBuilders());
+            getBuilderNameToIDMapPromise.then((builderNameToIDMap) => {
+                assert.equal(builderNameToIDMap["some builder"], 1)
+                assert.equal(builderNameToIDMap["some-builder-1"], 2)
+                assert.equal(builderNameToIDMap["some builder 2"], 3)
+                assert.equal(builderNameToIDMap["other builder"], 4)
+                assert.equal(builderNameToIDMap["some tester"], 5)
+                assert.equal(builderNameToIDMap["another tester"], 6)
+            });
+        });
+    });
 });
index dd4bfb9..1a5755a 100644 (file)
@@ -139,6 +139,13 @@ const configWithTwoTesters = {
     ]
 };
 
+function assertAndResolveRequest(request, method, url, contentToResolve)
+{
+    assert.equal(request.method, method);
+    assert.equal(request.url, url);
+    request.resolve(contentToResolve);
+}
+
 function createTriggerable(config = configWithOneTesterTwoBuilders)
 {
     let triggerable;
@@ -146,7 +153,10 @@ function createTriggerable(config = configWithOneTesterTwoBuilders)
         return Manifest.fetch();
     }).then(() => {
         triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, {name: 'sync-slave', password: 'password'}, new MockLogger);
-        return triggerable.initSyncers().then(() => triggerable.updateTriggerable());
+        const syncPromise = triggerable.initSyncers().then(() => triggerable.updateTriggerable());
+        assertAndResolveRequest(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURLDeprecated(), MockData.mockBuildbotBuildersDeprecated());
+        MockRemoteAPI.reset();
+        return syncPromise;
     }).then(() => Manifest.fetch()).then(() => {
         return new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, {name: 'sync-slave', password: 'password'}, new MockLogger);
     });
@@ -221,12 +231,6 @@ describe('sync-buildbot', function () {
         MockRemoteAPI.reset('http://build.webkit.org');
     });
 
-    function assertAndResolveRequest(request, method, url, contentToResolve)
-    {
-        assert.equal(request.method, method);
-        assert.equal(request.url, url);
-        request.resolve(contentToResolve);
-    }
 
     it('should not schedule on another builder if the build was scheduled on one builder before', () => {
         const requests = MockRemoteAPI.requests;
@@ -245,6 +249,8 @@ describe('sync-buildbot', function () {
             taskId = firstTestGroup.task().id();
             anotherTaskId = secondTestGroup.task().id();
             syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
+            assertAndResolveRequest(requests[0], 'GET', MockData.buildbotBuildersURLDeprecated(), MockData.mockBuildbotBuildersDeprecated());
+            MockRemoteAPI.reset();
             return MockRemoteAPI.waitForRequest();
         }).then(() => {
             assert.equal(requests.length, 2);
@@ -357,6 +363,8 @@ describe('sync-buildbot', function () {
             assert.deepEqual(otherCommitSet.allRootFiles(), []);
 
             syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
+            assertAndResolveRequest(requests[0], 'GET', MockData.buildbotBuildersURLDeprecated(), MockData.mockBuildbotBuildersDeprecated());
+            MockRemoteAPI.reset();
             return MockRemoteAPI.waitForRequest();
         }).then(() => {
             assert.equal(requests.length, 3);
@@ -468,6 +476,8 @@ describe('sync-buildbot', function () {
 
             MockRemoteAPI.reset();
             syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
+            assertAndResolveRequest(requests[0], 'GET', MockData.buildbotBuildersURLDeprecated(), MockData.mockBuildbotBuildersDeprecated());
+            MockRemoteAPI.reset();
             return MockRemoteAPI.waitForRequest();
         }).then(() => {
             assert.equal(requests.length, 3);
@@ -626,6 +636,8 @@ describe('sync-buildbot', function () {
             assert.deepEqual(otherCommitSet.allRootFiles(), []);
 
             syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
+            assertAndResolveRequest(requests[0], 'GET', MockData.buildbotBuildersURLDeprecated(), MockData.mockBuildbotBuildersDeprecated());
+            MockRemoteAPI.reset();
             return MockRemoteAPI.waitForRequest();
         }).then(() => {
             assert.equal(requests.length, 3);
@@ -737,6 +749,8 @@ describe('sync-buildbot', function () {
 
             MockRemoteAPI.reset();
             syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
+            assertAndResolveRequest(requests[0], 'GET', MockData.buildbotBuildersURLDeprecated(), MockData.mockBuildbotBuildersDeprecated());
+            MockRemoteAPI.reset();
             return MockRemoteAPI.waitForRequest();
         }).then(() => {
             assert.equal(requests.length, 3);
@@ -911,6 +925,8 @@ describe('sync-buildbot', function () {
             assert.equal(otherRoots.length, 1);
             assert.deepEqual(otherRoots[0].filename(), 'root46.dat');
             syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
+            assertAndResolveRequest(requests[0], 'GET', MockData.buildbotBuildersURLDeprecated(), MockData.mockBuildbotBuildersDeprecated());
+            MockRemoteAPI.reset();
             return MockRemoteAPI.waitForRequest();
         }).then(() => {
             assert.equal(requests.length, 3);
@@ -982,6 +998,8 @@ describe('sync-buildbot', function () {
             assert.equal(otherBuildRequest.buildId(), null);
 
             syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
+            assertAndResolveRequest(requests[0], 'GET', MockData.buildbotBuildersURLDeprecated(), MockData.mockBuildbotBuildersDeprecated());
+            MockRemoteAPI.reset();
             return Promise.all([MockRemoteAPI.waitForRequest(), uploadRoot(1, 123)]);
         }).then(() => {
             assert.equal(requests.length, 3);
@@ -1066,6 +1084,8 @@ describe('sync-buildbot', function () {
             assert.equal(otherBuildRequest.buildId(), null);
 
             syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
+            assertAndResolveRequest(requests[0], 'GET', MockData.buildbotBuildersURLDeprecated(), MockData.mockBuildbotBuildersDeprecated());
+            MockRemoteAPI.reset();
             return MockRemoteAPI.waitForRequest();
         }).then(() => {
             assert.equal(requests.length, 3);
@@ -1172,6 +1192,8 @@ describe('sync-buildbot', function () {
             assert.deepEqual(otherCommitSet.allRootFiles(), []);
 
             syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
+            assertAndResolveRequest(requests[0], 'GET', MockData.buildbotBuildersURLDeprecated(), MockData.mockBuildbotBuildersDeprecated());
+            MockRemoteAPI.reset();
             return MockRemoteAPI.waitForRequest();
         }).then(() => {
             assert.equal(requests.length, 3);
@@ -1291,6 +1313,8 @@ describe('sync-buildbot', function () {
 
             MockRemoteAPI.reset();
             syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
+            assertAndResolveRequest(requests[0], 'GET', MockData.buildbotBuildersURLDeprecated(), MockData.mockBuildbotBuildersDeprecated());
+            MockRemoteAPI.reset();
             return MockRemoteAPI.waitForRequest();
         }).then(() => {
             assert.equal(requests.length, 3);
@@ -1467,6 +1491,8 @@ describe('sync-buildbot', function () {
             assert.deepEqual(otherCommitSet.allRootFiles(), []);
 
             syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
+            assertAndResolveRequest(requests[0], 'GET', MockData.buildbotBuildersURLDeprecated(), MockData.mockBuildbotBuildersDeprecated());
+            MockRemoteAPI.reset();
             return MockRemoteAPI.waitForRequest();
         }).then(() => {
             assert.equal(requests.length, 3);
@@ -1627,6 +1653,8 @@ describe('sync-buildbot', function () {
 
             MockRemoteAPI.reset();
             syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
+            assertAndResolveRequest(requests[0], 'GET', MockData.buildbotBuildersURLDeprecated(), MockData.mockBuildbotBuildersDeprecated());
+            MockRemoteAPI.reset();
             return MockRemoteAPI.waitForRequest();
         }).then(() => {
             assert.equal(requests.length, 3);
index 589edec..135e0a9 100644 (file)
@@ -67,6 +67,7 @@ class BuildbotSyncer {
         this._platformPropertyName = commonConfigurations.platformArgument;
         this._buildRequestPropertyName = commonConfigurations.buildRequestArgument;
         this._builderName = object.builder;
+        this._builderID = object.builderID;
         this._slaveList = object.slaveList;
         this._entryList = null;
         this._slavesWithNewRequests = new Set;
@@ -317,8 +318,9 @@ class BuildbotSyncer {
         return revisionSet;
     }
 
-    static _loadConfig(remote, config)
+    static _loadConfig(remote, config, builderNameToIDMap)
     {
+        assert(builderNameToIDMap);
         const types = config['types'] || {};
         const builders = config['builders'] || {};
 
@@ -348,7 +350,7 @@ class BuildbotSyncer {
         }
 
         assert(Array.isArray(config['testConfigurations']), `The test configuration must be an array`);
-        this._resolveBuildersWithPlatforms('test', config['testConfigurations'], builders).forEach((entry, configurationIndex) => {
+        this._resolveBuildersWithPlatforms('test', config['testConfigurations'], builders, builderNameToIDMap).forEach((entry, configurationIndex) => {
             assert(Array.isArray(entry['types']), `The test configuration ${configurationIndex} does not specify "types" as an array`);
             for (const type of entry['types']) {
                 const typeConfig = this._validateAndMergeConfig({}, entry.builderConfig);
@@ -366,7 +368,7 @@ class BuildbotSyncer {
         const buildConfigurations = config['buildConfigurations'];
         if (buildConfigurations) {
             assert(Array.isArray(buildConfigurations), `The test configuration must be an array`);
-            this._resolveBuildersWithPlatforms('test', buildConfigurations, builders).forEach((entry, configurationIndex) => {
+            this._resolveBuildersWithPlatforms('test', buildConfigurations, builders, builderNameToIDMap).forEach((entry, configurationIndex) => {
                 const syncer = ensureBuildbotSyncer(entry.builderConfig);
                 assert(!syncer.isTester(), `The build configuration ${configurationIndex} uses a tester: ${syncer.builderName()}`);
                 syncer.addBuildConfiguration(entry.platform, entry.builderConfig.properties);
@@ -376,7 +378,7 @@ class BuildbotSyncer {
         return Array.from(syncerByBuilder.values());
     }
 
-    static _resolveBuildersWithPlatforms(configurationType, configurationList, builders)
+    static _resolveBuildersWithPlatforms(configurationType, configurationList, builders, builderNameToIDMap)
     {
         const resolvedConfigurations = [];
         let configurationIndex = 0;
@@ -388,6 +390,8 @@ class BuildbotSyncer {
                 const matchingBuilder = builders[builderKey];
                 assert(matchingBuilder, `"${builderKey}" is not a valid builder in the configuration`);
                 assert('builder' in matchingBuilder, `Builder ${builderKey} does not specify a buildbot builder name`);
+                assert(matchingBuilder.builder in builderNameToIDMap, `Builder ${matchingBuilder.builder} not found in Buildbot configuration.`);
+                matchingBuilder['builderID'] = builderNameToIDMap[matchingBuilder.builder];
                 const builderConfig = this._validateAndMergeConfig({}, matchingBuilder);
                 for (const platformName of entry['platforms']) {
                     const platform = Platform.findByName(platformName);
@@ -565,6 +569,10 @@ class BuildbotSyncer {
                 assert.equal(typeof(value), 'string', `${name} should be of string type`);
                 config[name] = value;
                 break;
+            case 'builderID':
+                assert(value, 'builderID should not be undefined.');
+                config[name] = value;
+                break;
             default:
                 assert(false, `Unrecognized parameter "${name}"`);
             }
index 10352aa..8992840 100644 (file)
@@ -27,11 +27,36 @@ class BuildbotTriggerable {
         this._logger = logger || {log: () => { }, error: () => { }};
     }
 
+    // This method handles Buildbot 0.9 data format
+    getBuilderNameToIDMap()
+    {
+        return this._buildbotRemote.getJSON("/api/v2/builders").then((content) => {
+            assert(content.builders instanceof Array);
+
+            const builderNameToIDMap = {};
+            for (const builder of content.builders)
+                builderNameToIDMap[builder.name] = builder.builderid;
+
+            return builderNameToIDMap;
+        });
+    }
+
+    // This method handles Buildbot 0.8 data format
+    getBuilderNameToIDMapDeprecated()
+    {
+        return this._buildbotRemote.getJSON("/json/builders").then((content) => {
+            const builderNameToIDMap = {};
+            for (let builder in content)
+                builderNameToIDMap[builder] = builder;
+
+            return builderNameToIDMap;
+        });
+    }
+
     initSyncers()
     {
-        return new Promise((resolve, reject) => {
-            this._syncers = BuildbotSyncer._loadConfig(this._buildbotRemote, this._config);
-            setTimeout(resolve, 0);
+        return this.getBuilderNameToIDMapDeprecated().then((builderNameToIDMap) => {
+            this._syncers = BuildbotSyncer._loadConfig(this._buildbotRemote, this._config, builderNameToIDMap);
         });
     }
 
index 30bf2cb..0bd0da5 100644 (file)
@@ -143,6 +143,19 @@ function smallConfiguration()
     };
 }
 
+function builderNameToIDMap()
+{
+    return {
+        'some builder' : '100',
+        'ABTest-iPhone-RunBenchmark-Tests': '101',
+        'ABTest-iPad-RunBenchmark-Tests': '102',
+        'ABTest-iOS-Builder': '103',
+        'iPhone AB Tests' : '104',
+        'iPhone 2 AB Tests': '105',
+        'iPad AB Tests': '106'
+    }    
+}
+
 function smallPendingBuild()
 {
     return {
@@ -477,39 +490,39 @@ describe('BuildbotSyncer', () => {
     describe('_loadConfig', () => {
 
         it('should create BuildbotSyncer objects for a configuration that specify all required options', () => {
-            assert.equal(BuildbotSyncer._loadConfig(MockRemoteAPI, smallConfiguration()).length, 1);
+            assert.equal(BuildbotSyncer._loadConfig(MockRemoteAPI, smallConfiguration(), builderNameToIDMap()).length, 1);
         });
 
         it('should throw when some required options are missing', () => {
             assert.throws(() => {
                 const config = smallConfiguration();
                 delete config.builders;
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /"some-builder" is not a valid builder in the configuration/);
             assert.throws(() => {
                 const config = smallConfiguration();
                 delete config.types;
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /"some-test" is not a valid type in the configuration/);
             assert.throws(() => {
                 const config = smallConfiguration();
                 delete config.testConfigurations[0].builders;
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /The test configuration 1 does not specify "builders" as an array/);
             assert.throws(() => {
                 const config = smallConfiguration();
                 delete config.testConfigurations[0].platforms;
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /The test configuration 1 does not specify "platforms" as an array/);
             assert.throws(() => {
                 const config = smallConfiguration();
                 delete config.testConfigurations[0].types;
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /The test configuration 0 does not specify "types" as an array/);
             assert.throws(() => {
                 const config = smallConfiguration();
                 delete config.buildRequestArgument;
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /buildRequestArgument must specify the name of the property used to store the build request ID/);
         });
 
@@ -517,12 +530,12 @@ describe('BuildbotSyncer', () => {
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.testConfigurations[0].types = 'some test';
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /The test configuration 0 does not specify "types" as an array/);
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.testConfigurations[0].types = [1];
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /"1" is not a valid type in the configuration/);
         });
 
@@ -530,12 +543,12 @@ describe('BuildbotSyncer', () => {
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.builders[Object.keys(config.builders)[0]].properties = 'hello';
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Build properties should be a dictionary/);
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.types[Object.keys(config.types)[0]].properties = 'hello';
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Build properties should be a dictionary/);
         });
 
@@ -544,13 +557,13 @@ describe('BuildbotSyncer', () => {
                 const config = smallConfiguration();
                 const firstType = Object.keys(config.types)[0];
                 config.types[firstType].testProperties = {};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Unrecognized parameter "testProperties"/);
             assert.throws(() => {
                 const config = smallConfiguration();
                 const firstBuilder = Object.keys(config.builders)[0];
                 config.builders[firstBuilder].testProperties = {};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Unrecognized parameter "testProperties"/);
         });
 
@@ -559,13 +572,13 @@ describe('BuildbotSyncer', () => {
                 const config = smallConfiguration();
                 const firstType = Object.keys(config.types)[0];
                 config.types[firstType].buildProperties = {};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Unrecognized parameter "buildProperties"/);
             assert.throws(() => {
                 const config = smallConfiguration();
                 const firstBuilder = Object.keys(config.builders)[0];
                 config.builders[firstBuilder].buildProperties = {};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Unrecognized parameter "buildProperties"/);
         });
 
@@ -574,27 +587,27 @@ describe('BuildbotSyncer', () => {
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.types[firstType].properties = 'hello';
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Build properties should be a dictionary/);
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.types[firstType].properties = {'some': {'otherKey': 'some root'}};
-                BuildbotSyncer._loadConfig(RemoteAPI, config);
+                BuildbotSyncer._loadConfig(RemoteAPI, config, builderNameToIDMap());
             }, /Build properties "some" specifies a non-string value of type "object"/);
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.types[firstType].properties = {'some': {'otherKey': 'some root'}};
-                BuildbotSyncer._loadConfig(RemoteAPI, config);
+                BuildbotSyncer._loadConfig(RemoteAPI, config, builderNameToIDMap());
             }, /Build properties "some" specifies a non-string value of type "object"/);
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.types[firstType].properties = {'some': {'revision': 'WebKit'}};
-                BuildbotSyncer._loadConfig(RemoteAPI, config);
+                BuildbotSyncer._loadConfig(RemoteAPI, config, builderNameToIDMap());
             }, /Build properties "some" specifies a non-string value of type "object"/);
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.types[firstType].properties = {'some': 1};
-                BuildbotSyncer._loadConfig(RemoteAPI, config);
+                BuildbotSyncer._loadConfig(RemoteAPI, config, builderNameToIDMap());
             }, / Build properties "some" specifies a non-string value of type "object"/);
         });
 
@@ -603,32 +616,32 @@ describe('BuildbotSyncer', () => {
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.builders[firstBuilder].properties = 'hello';
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Build properties should be a dictionary/);
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.builders[firstBuilder].properties = {'some': {'otherKey': 'some root'}};
-                BuildbotSyncer._loadConfig(RemoteAPI, config);
+                BuildbotSyncer._loadConfig(RemoteAPI, config, builderNameToIDMap());
             }, /Build properties "some" specifies a non-string value of type "object"/);
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.builders[firstBuilder].properties = {'some': {'otherKey': 'some root'}};
-                BuildbotSyncer._loadConfig(RemoteAPI, config);
+                BuildbotSyncer._loadConfig(RemoteAPI, config, builderNameToIDMap());
             }, /Build properties "some" specifies a non-string value of type "object"/);
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.builders[firstBuilder].properties = {'some': {'revision': 'WebKit'}};
-                BuildbotSyncer._loadConfig(RemoteAPI, config);
+                BuildbotSyncer._loadConfig(RemoteAPI, config, builderNameToIDMap());
             }, /Build properties "some" specifies a non-string value of type "object"/);
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.builders[firstBuilder].properties = {'some': 1};
-                BuildbotSyncer._loadConfig(RemoteAPI, config);
+                BuildbotSyncer._loadConfig(RemoteAPI, config, builderNameToIDMap());
             }, /Build properties "some" specifies a non-string value of type "object"/);
         });
 
         it('should create BuildbotSyncer objects for valid configurations', () => {
-            let syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig());
+            let syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig(), builderNameToIDMap());
             assert.equal(syncers.length, 3);
             assert.ok(syncers[0] instanceof BuildbotSyncer);
             assert.ok(syncers[1] instanceof BuildbotSyncer);
@@ -636,14 +649,14 @@ describe('BuildbotSyncer', () => {
         });
 
         it('should parse builder names correctly', () => {
-            let syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig());
+            let syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig(), builderNameToIDMap());
             assert.equal(syncers[0].builderName(), 'ABTest-iPhone-RunBenchmark-Tests');
             assert.equal(syncers[1].builderName(), 'ABTest-iPad-RunBenchmark-Tests');
             assert.equal(syncers[2].builderName(), 'ABTest-iOS-Builder');
         });
 
         it('should parse test configurations with build configurations correctly', () => {
-            let syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig());
+            let syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig(), builderNameToIDMap());
 
             let configurations = syncers[0].testConfigurations();
             assert(syncers[0].isTester());
@@ -679,12 +692,12 @@ describe('BuildbotSyncer', () => {
             assert.throws(() => {
                 const config = sampleiOSConfig();
                 config.buildConfigurations[0].builders = config.testConfigurations[0].builders;
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             });
         });
 
         it('should parse test configurations with types and platforms expansions correctly', () => {
-            const syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfigWithExpansions());
+            const syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfigWithExpansions(), builderNameToIDMap());
 
             assert.equal(syncers.length, 3);
 
@@ -725,12 +738,12 @@ describe('BuildbotSyncer', () => {
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.repositoryGroups = 1;
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /repositoryGroups must specify a dictionary from the name to its definition/);
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.repositoryGroups = 'hello';
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /repositoryGroups must specify a dictionary from the name to its definition/);
         });
 
@@ -738,12 +751,12 @@ describe('BuildbotSyncer', () => {
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.repositoryGroups = {'some-group': {testProperties: {}}};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Repository group "some-group" does not specify a dictionary of repositories/);
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.repositoryGroups = {'some-group': {repositories: 1}, testProperties: {}};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Repository group "some-group" does not specify a dictionary of repositories/);
         });
 
@@ -751,7 +764,7 @@ describe('BuildbotSyncer', () => {
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.repositoryGroups = {'some-group': {repositories: {}, testProperties: {}}};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Repository group "some-group" does not specify any repository/);
         });
 
@@ -759,7 +772,7 @@ describe('BuildbotSyncer', () => {
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.repositoryGroups = {'some-group': {repositories: {'InvalidRepositoryName': {}}}};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /"InvalidRepositoryName" is not a valid repository name/);
         });
 
@@ -767,7 +780,7 @@ describe('BuildbotSyncer', () => {
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.repositoryGroups = {'some-group': {repositories: {'WebKit': 1}}};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /"WebKit" specifies a non-dictionary value/);
         });
 
@@ -775,12 +788,12 @@ describe('BuildbotSyncer', () => {
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.repositoryGroups = {'some-group': {repositories: {'WebKit': {}}, description: 1}};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Repository group "some-group" have an invalid description/);
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.repositoryGroups = {'some-group': {repositories: {'WebKit': {}}, description: [1, 2]}};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Repository group "some-group" have an invalid description/);
         });
 
@@ -788,12 +801,12 @@ describe('BuildbotSyncer', () => {
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.repositoryGroups = {'some-group': {repositories: {'WebKit': {}}, testProperties: 1}};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Repository group "some-group" specifies the test configurations with an invalid type/);
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.repositoryGroups = {'some-group': {repositories: {'WebKit': {}}, testProperties: 'hello'}};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Repository group "some-group" specifies the test configurations with an invalid type/);
         });
 
@@ -801,7 +814,7 @@ describe('BuildbotSyncer', () => {
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.repositoryGroups = {'some-group': {repositories: {'WebKit': {}}, testProperties: {'wk': {revision: 'InvalidRepository'}}}};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Repository group "some-group" an invalid repository "InvalidRepository"/);
         });
 
@@ -809,7 +822,7 @@ describe('BuildbotSyncer', () => {
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.repositoryGroups = {'some-group': {repositories: {'WebKit': {}}, testProperties: {'os': {revision: 'iOS'}}}};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Repository group "some-group" an invalid repository "iOS"/);
             assert.throws(() => {
                 const config = smallConfiguration();
@@ -818,7 +831,7 @@ describe('BuildbotSyncer', () => {
                     testProperties: {'wk': {revision: 'WebKit'}, 'install-roots': {'roots': {}}},
                     buildProperties: {'os': {revision: 'iOS'}},
                     acceptsRoots: true}};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Repository group "some-group" an invalid repository "iOS"/);
         });
 
@@ -830,7 +843,7 @@ describe('BuildbotSyncer', () => {
                     testProperties: {'wk': {revision: 'WebKit'}, 'ios': {revision: 'iOS'}, 'install-roots': {'roots': {}}},
                     buildProperties: {'wk': {revision: 'WebKit'}, 'ios': {revision: 'iOS'}, 'wk-patch': {patch: 'iOS'}},
                     acceptsRoots: true}};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Repository group "some-group" specifies a patch for "iOS" but it does not accept a patch/);
         });
 
@@ -842,7 +855,7 @@ describe('BuildbotSyncer', () => {
                     testProperties: {'wk': {revision: 'WebKit'}, 'install-roots': {'roots': {}}},
                     buildProperties: {'wk-patch': {patch: 'WebKit'}},
                     acceptsRoots: true}};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Repository group "some-group" specifies a patch for "WebKit" but does not specify a revision/);
         });
 
@@ -850,7 +863,7 @@ describe('BuildbotSyncer', () => {
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.repositoryGroups = {'some-group': {'repositories': {'WebKit': {}}, testProperties: {}}};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Repository group "some-group" does not use some of the repositories listed in testing/);
             assert.throws(() => {
                 const config = smallConfiguration();
@@ -859,7 +872,7 @@ describe('BuildbotSyncer', () => {
                     testProperties: {'wk': {revision: 'WebKit'}, 'install-roots': {'roots': {}}},
                     buildProperties: {},
                     acceptsRoots: true}};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Repository group "some-group" does not use some of the repositories listed in building a patch/);
         });
 
@@ -867,12 +880,12 @@ describe('BuildbotSyncer', () => {
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.repositoryGroups = {'some-group': {'repositories': {'WebKit': {}}, 'testProperties': {'webkit': {'revision': 'WebKit'}}, acceptsRoots: 1}};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Repository group "some-group" contains invalid acceptsRoots value:/);
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.repositoryGroups = {'some-group': {'repositories': {'WebKit': {}}, 'testProperties': {'webkit': {'revision': 'WebKit'}}, acceptsRoots: []}};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Repository group "some-group" contains invalid acceptsRoots value:/);
         });
 
@@ -880,12 +893,12 @@ describe('BuildbotSyncer', () => {
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.repositoryGroups = {'some-group': {'repositories': {'WebKit': {acceptsPatch: 1}}, 'testProperties': {'webkit': {'revision': 'WebKit'}}}};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /"WebKit" contains invalid acceptsPatch value:/);
             assert.throws(() => {
                 const config = smallConfiguration();
                 config.repositoryGroups = {'some-group': {'repositories': {'WebKit': {acceptsPatch: []}}, 'testProperties': {'webkit': {'revision': 'WebKit'}}}};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /"WebKit" contains invalid acceptsPatch value:/);
         });
 
@@ -894,7 +907,7 @@ describe('BuildbotSyncer', () => {
                 const config = smallConfiguration();
                 config.repositoryGroups = {'some-group': {'repositories': {'WebKit': {acceptsPatch: true}},
                     'testProperties': {'webkit': {'revision': 'WebKit'}, 'webkit-patch': {'patch': 'WebKit'}}}};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Repository group "some-group" specifies a patch for "WebKit" in the properties for testing/);
         });
 
@@ -906,7 +919,7 @@ describe('BuildbotSyncer', () => {
                     testProperties: {'webkit': {revision: 'WebKit'}, 'install-roots': {'roots': {}}},
                     buildProperties: {'webkit': {revision: 'WebKit'}, 'patch': {patch: 'WebKit'}, 'install-roots': {roots: {}}},
                     acceptsRoots: true}};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Repository group "some-group" specifies roots in the properties for building/);
         });
 
@@ -916,7 +929,7 @@ describe('BuildbotSyncer', () => {
                 config.repositoryGroups = {'some-group': {
                     repositories: {'WebKit': {}},
                     testProperties: {'webkit': {'revision': 'WebKit'}, 'install-roots': {'roots': {}}}}};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Repository group "some-group" specifies roots in a property but it does not accept roots/);
         });
 
@@ -927,7 +940,7 @@ describe('BuildbotSyncer', () => {
                     repositories: {'WebKit': {acceptsPatch: true}},
                     testProperties: {'webkit': {revision: 'WebKit'}},
                     buildProperties: {'webkit': {revision: 'WebKit'}, 'webkit-patch': {patch: 'WebKit'}}}};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Repository group "some-group" specifies the properties for building but does not accept roots in testing/);
         });
 
@@ -939,7 +952,7 @@ describe('BuildbotSyncer', () => {
                     testProperties: {'webkit': {'revision': 'WebKit'}, 'install-roots': {'roots': {}}},
                     buildProperties: {'webkit': {'revision': 'WebKit'}},
                     acceptsRoots: true}};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Repository group "some-group" specifies the properties for building but does not accept any patches/);
         });
 
@@ -951,21 +964,21 @@ describe('BuildbotSyncer', () => {
                     testProperties: {'webkit': {revision: 'WebKit'}},
                     buildProperties: {'webkit': {revision: 'WebKit'}, 'webkit-patch': {patch: 'WebKit'}},
                     acceptsRoots: true}};
-                BuildbotSyncer._loadConfig(MockRemoteAPI, config);
+                BuildbotSyncer._loadConfig(MockRemoteAPI, config, builderNameToIDMap());
             }, /Repository group "some-group" accepts roots but does not specify roots in testProperties/);
         });
     });
 
     describe('_propertiesForBuildRequest', () => {
         it('should include all properties specified in a given configuration', () => {
-            const syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig());
+            const syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig(), builderNameToIDMap());
             const request = createSampleBuildRequest(MockModels.iphone, MockModels.speedometer);
             const properties = syncers[0]._propertiesForBuildRequest(request, [request]);
             assert.deepEqual(Object.keys(properties).sort(), ['build_request_id', 'desired_image', 'forcescheduler', 'opensource', 'test_name']);
         });
 
         it('should preserve non-parametric property values', () => {
-            const syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig());
+            const syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig(), builderNameToIDMap());
             let request = createSampleBuildRequest(MockModels.iphone, MockModels.speedometer);
             let properties = syncers[0]._propertiesForBuildRequest(request, [request]);
             assert.equal(properties['test_name'], 'speedometer');
@@ -978,14 +991,14 @@ describe('BuildbotSyncer', () => {
         });
 
         it('should resolve "root"', () => {
-            const syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig());
+            const syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig(), builderNameToIDMap());
             const request = createSampleBuildRequest(MockModels.iphone, MockModels.speedometer);
             const properties = syncers[0]._propertiesForBuildRequest(request, [request]);
             assert.equal(properties['desired_image'], '13A452');
         });
 
         it('should resolve "revision"', () => {
-            const syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig());
+            const syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig(), builderNameToIDMap());
             const request = createSampleBuildRequest(MockModels.iphone, MockModels.speedometer);
             const properties = syncers[0]._propertiesForBuildRequest(request, [request]);
             assert.equal(properties['opensource'], '197463');
@@ -1009,7 +1022,7 @@ describe('BuildbotSyncer', () => {
                 },
                 'acceptsRoots': true,
             };
-            const syncers = BuildbotSyncer._loadConfig(RemoteAPI, config);
+            const syncers = BuildbotSyncer._loadConfig(RemoteAPI, config, builderNameToIDMap());
             const request = createSampleBuildRequestWithPatch(MockModels.iphone, null, -1);
             const properties = syncers[2]._propertiesForBuildRequest(request, [request]);
             assert.equal(properties['webkit'], '197463');
@@ -1031,7 +1044,7 @@ describe('BuildbotSyncer', () => {
                 },
                 'acceptsRoots': true,
             };
-            const syncers = BuildbotSyncer._loadConfig(RemoteAPI, config);
+            const syncers = BuildbotSyncer._loadConfig(RemoteAPI, config, builderNameToIDMap());
             const requestToBuild = createSampleBuildRequestWithPatch(MockModels.iphone, null, -1);
             const requestToTest = createSampleBuildRequestWithPatch(MockModels.iphone, MockModels.speedometer, 0);
             const otherRequestToTest = createSampleBuildRequest(MockModels.iphone, MockModels.speedometer);
@@ -1075,7 +1088,7 @@ describe('BuildbotSyncer', () => {
                 },
                 'acceptsRoots': true,
             };
-            const syncers = BuildbotSyncer._loadConfig(RemoteAPI, config);
+            const syncers = BuildbotSyncer._loadConfig(RemoteAPI, config, builderNameToIDMap());
             const request = createSampleBuildRequestWithOwnedCommit(MockModels.iphone, null, -1);
             const properties = syncers[2]._propertiesForBuildRequest(request, [request]);
             assert.equal(properties['webkit'], '197463');
@@ -1104,7 +1117,7 @@ describe('BuildbotSyncer', () => {
                 },
                 'acceptsRoots': true,
             };
-            const syncers = BuildbotSyncer._loadConfig(RemoteAPI, config);
+            const syncers = BuildbotSyncer._loadConfig(RemoteAPI, config, builderNameToIDMap());
             const request = createSampleBuildRequestWithOwnedCommitAndPatch(MockModels.iphone, null, -1);
             const properties = syncers[2]._propertiesForBuildRequest(request, [request]);
             assert.equal(properties['webkit'], '197463');
@@ -1115,7 +1128,7 @@ describe('BuildbotSyncer', () => {
         });
 
         it('should set the property for the build request id', () => {
-            const syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig());
+            const syncers = BuildbotSyncer._loadConfig(RemoteAPI, sampleiOSConfig(), builderNameToIDMap());
             const request = createSampleBuildRequest(MockModels.iphone, MockModels.speedometer);
             const properties = syncers[0]._propertiesForBuildRequest(request, [request]);
             assert.equal(properties['build_request_id'], request.id());
@@ -1124,7 +1137,7 @@ describe('BuildbotSyncer', () => {
 
     describe('pullBuildbot', () => {
         it('should fetch pending builds from the right URL', () => {
-            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
+            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig(), builderNameToIDMap())[1];
             assert.equal(syncer.builderName(), 'ABTest-iPad-RunBenchmark-Tests');
             let expectedURL = '/json/builders/ABTest-iPad-RunBenchmark-Tests/pendingBuilds';
             assert.equal(syncer.pathForPendingBuildsJSON(), expectedURL);
@@ -1134,7 +1147,7 @@ describe('BuildbotSyncer', () => {
         });
 
         it('should fetch recent builds once pending builds have been fetched', () => {
-            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
+            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig(), builderNameToIDMap())[1];
             assert.equal(syncer.builderName(), 'ABTest-iPad-RunBenchmark-Tests');
 
             syncer.pullBuildbot(1);
@@ -1148,7 +1161,7 @@ describe('BuildbotSyncer', () => {
         });
 
         it('should fetch the right number of recent builds', () => {
-            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
+            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig(), builderNameToIDMap())[1];
 
             syncer.pullBuildbot(3);
             assert.equal(requests.length, 1);
@@ -1161,7 +1174,7 @@ describe('BuildbotSyncer', () => {
         });
 
         it('should create BuildbotBuildEntry for pending builds', () => {
-            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
+            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig(), builderNameToIDMap())[1];
             let promise = syncer.pullBuildbot();
             requests[0].resolve([samplePendingBuild()]);
             return promise.then((entries) => {
@@ -1179,7 +1192,7 @@ describe('BuildbotSyncer', () => {
         });
 
         it('should create BuildbotBuildEntry for in-progress builds', () => {
-            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
+            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig(), builderNameToIDMap())[1];
 
             let promise = syncer.pullBuildbot(1);
             assert.equal(requests.length, 1);
@@ -1203,7 +1216,7 @@ describe('BuildbotSyncer', () => {
         });
 
         it('should create BuildbotBuildEntry for finished builds', () => {
-            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
+            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig(), builderNameToIDMap())[1];
 
             let promise = syncer.pullBuildbot(1);
             assert.equal(requests.length, 1);
@@ -1227,7 +1240,7 @@ describe('BuildbotSyncer', () => {
         });
 
         it('should create BuildbotBuildEntry for mixed pending, in-progress, finished, and missing builds', () => {
-            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
+            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig(), builderNameToIDMap())[1];
 
             let promise = syncer.pullBuildbot(5);
             assert.equal(requests.length, 1);
@@ -1274,7 +1287,7 @@ describe('BuildbotSyncer', () => {
         });
 
         it('should sort BuildbotBuildEntry by order', () => {
-            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
+            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig(), builderNameToIDMap())[1];
 
             let promise = syncer.pullBuildbot(5);
             assert.equal(requests.length, 1);
@@ -1331,7 +1344,7 @@ describe('BuildbotSyncer', () => {
         });
 
         it('should override BuildbotBuildEntry for pending builds by in-progress builds', () => {
-            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
+            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig(), builderNameToIDMap())[1];
 
             let promise = syncer.pullBuildbot(5);
             assert.equal(requests.length, 1);
@@ -1358,7 +1371,7 @@ describe('BuildbotSyncer', () => {
         });
 
         it('should override BuildbotBuildEntry for pending builds by finished builds', () => {
-            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
+            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig(), builderNameToIDMap())[1];
 
             let promise = syncer.pullBuildbot(5);
             assert.equal(requests.length, 1);
@@ -1387,7 +1400,7 @@ describe('BuildbotSyncer', () => {
 
     describe('scheduleRequest', () => {
         it('should schedule a build request on a specified slave', () => {
-            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[0];
+            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig(), builderNameToIDMap())[0];
 
             const waitForRequest = MockRemoteAPI.waitForRequest();
             const request = createSampleBuildRequest(MockModels.iphone, MockModels.speedometer);
@@ -1424,7 +1437,7 @@ describe('BuildbotSyncer', () => {
         }
 
         it('should schedule a build if builder has no builds if slaveList is not specified', () => {
-            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, smallConfiguration())[0];
+            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, smallConfiguration(), builderNameToIDMap())[0];
 
             return pullBuildbotWithAssertion(syncer, [], {}).then(() => {
                 const request = createSampleBuildRequest(MockModels.somePlatform, MockModels.someTest);
@@ -1437,7 +1450,7 @@ describe('BuildbotSyncer', () => {
         });
 
         it('should schedule a build if builder only has finished builds if slaveList is not specified', () => {
-            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, smallConfiguration())[0];
+            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, smallConfiguration(), builderNameToIDMap())[0];
 
             return pullBuildbotWithAssertion(syncer, [], {[-1]: smallFinishedBuild()}).then(() => {
                 const request = createSampleBuildRequest(MockModels.somePlatform, MockModels.someTest);
@@ -1450,7 +1463,7 @@ describe('BuildbotSyncer', () => {
         });
 
         it('should not schedule a build if builder has a pending build if slaveList is not specified', () => {
-            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, smallConfiguration())[0];
+            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, smallConfiguration(), builderNameToIDMap())[0];
 
             return pullBuildbotWithAssertion(syncer, [smallPendingBuild()], {}).then(() => {
                 syncer.scheduleRequestInGroupIfAvailable(createSampleBuildRequest(MockModels.somePlatform, MockModels.someTest));
@@ -1459,7 +1472,7 @@ describe('BuildbotSyncer', () => {
         });
 
         it('should schedule a build if builder does not have pending or completed builds on the matching slave', () => {
-            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[0];
+            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig(), builderNameToIDMap())[0];
 
             return pullBuildbotWithAssertion(syncer, [], {}).then(() => {
                 const request = createSampleBuildRequest(MockModels.iphone, MockModels.speedometer);
@@ -1471,7 +1484,7 @@ describe('BuildbotSyncer', () => {
         });
 
         it('should schedule a build if builder only has finished builds on the matching slave', () => {
-            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
+            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig(), builderNameToIDMap())[1];
 
             pullBuildbotWithAssertion(syncer, [], {[-1]: sampleFinishedBuild()}).then(() => {
                 const request = createSampleBuildRequest(MockModels.ipad, MockModels.speedometer);
@@ -1483,7 +1496,7 @@ describe('BuildbotSyncer', () => {
         });
 
         it('should not schedule a build if builder has a pending build on the maching slave', () => {
-            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
+            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig(), builderNameToIDMap())[1];
 
             pullBuildbotWithAssertion(syncer, [samplePendingBuild()], {}).then(() => {
                 const request = createSampleBuildRequest(MockModels.ipad, MockModels.speedometer);
@@ -1493,7 +1506,7 @@ describe('BuildbotSyncer', () => {
         });
 
         it('should schedule a build if builder only has a pending build on a non-maching slave', () => {
-            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
+            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig(), builderNameToIDMap())[1];
 
             return pullBuildbotWithAssertion(syncer, [samplePendingBuild(1, 1, 'another-slave')], {}).then(() => {
                 const request = createSampleBuildRequest(MockModels.ipad, MockModels.speedometer);
@@ -1503,7 +1516,7 @@ describe('BuildbotSyncer', () => {
         });
 
         it('should schedule a build if builder only has an in-progress build on the matching slave', () => {
-            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
+            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig(), builderNameToIDMap())[1];
 
             return pullBuildbotWithAssertion(syncer, [], {[-1]: sampleInProgressBuild()}).then(() => {
                 const request = createSampleBuildRequest(MockModels.ipad, MockModels.speedometer);
@@ -1513,7 +1526,7 @@ describe('BuildbotSyncer', () => {
         });
 
         it('should schedule a build if builder has an in-progress build on another slave', () => {
-            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
+            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig(), builderNameToIDMap())[1];
 
             return pullBuildbotWithAssertion(syncer, [], {[-1]: sampleInProgressBuild('other-slave')}).then(() => {
                 const request = createSampleBuildRequest(MockModels.ipad, MockModels.speedometer);
@@ -1523,7 +1536,7 @@ describe('BuildbotSyncer', () => {
         });
 
         it('should not schedule a build if the request does not match any configuration', () => {
-            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[0];
+            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig(), builderNameToIDMap())[0];
 
             return pullBuildbotWithAssertion(syncer, [], {}).then(() => {
                 const request = createSampleBuildRequest(MockModels.ipad, MockModels.speedometer);
@@ -1533,7 +1546,7 @@ describe('BuildbotSyncer', () => {
         });
 
         it('should not schedule a build if a new request had been submitted to the same slave', (done) => {
-            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
+            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig(), builderNameToIDMap())[1];
 
             pullBuildbotWithAssertion(syncer, [], {}).then(() => {
                 let request = createSampleBuildRequest(MockModels.ipad, MockModels.speedometer);
@@ -1551,7 +1564,7 @@ describe('BuildbotSyncer', () => {
         });
 
         it('should schedule a build if a new request had been submitted to another slave', () => {
-            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig())[1];
+            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, sampleiOSConfig(), builderNameToIDMap())[1];
 
             return pullBuildbotWithAssertion(syncer, [], {}).then(() => {
                 let request = createSampleBuildRequest(MockModels.ipad, MockModels.speedometer);
@@ -1564,7 +1577,7 @@ describe('BuildbotSyncer', () => {
         });
 
         it('should not schedule a build if a new request had been submitted to the same builder without slaveList', () => {
-            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, smallConfiguration())[0];
+            let syncer = BuildbotSyncer._loadConfig(MockRemoteAPI, smallConfiguration(), builderNameToIDMap())[0];
 
             return pullBuildbotWithAssertion(syncer, [], {}).then(() => {
                 let request = createSampleBuildRequest(MockModels.somePlatform, MockModels.someTest);