Finish encoding/decoding support for DisplayList::SetState
[WebKit-https.git] / Websites / perf.webkit.org / server-tests / privileged-api-create-test-group-tests.js
1 'use strict';
2
3 const assert = require('assert');
4
5 const MockData = require('./resources/mock-data.js');
6 const TestServer = require('./resources/test-server.js');
7 const TemporaryFile = require('./resources/temporary-file.js').TemporaryFile;
8 const addSlaveForReport = require('./resources/common-operations.js').addSlaveForReport;
9 const prepareServerTest = require('./resources/common-operations.js').prepareServerTest;
10
11 function createAnalysisTask(name, webkitRevisions = ["191622", "191623"])
12 {
13     const reportWithRevision = [{
14         "buildTag": "124",
15         "buildTime": "2015-10-27T15:34:51",
16         "revisions": {
17             "WebKit": {
18                 "revision": webkitRevisions[0],
19                 "timestamp": '2015-10-27T11:36:56.878473Z',
20             },
21             "macOS": {
22                 "revision": "15A284",
23             }
24         },
25         "builderName": "someBuilder",
26         "slaveName": "someSlave",
27         "slavePassword": "somePassword",
28         "platform": "some platform",
29         "tests": {
30             "some test": {
31                 "metrics": {
32                     "Time": ["Arithmetic"],
33                 },
34                 "tests": {
35                     "test1": {
36                         "metrics": {"Time": { "current": [11] }},
37                     }
38                 }
39             },
40         }}];
41
42     const anotherReportWithRevision = [{
43         "buildTag": "125",
44         "buildTime": "2015-10-27T17:27:41",
45         "revisions": {
46             "WebKit": {
47                 "revision": webkitRevisions[1],
48                 "timestamp": '2015-10-27T16:38:10.768995Z',
49             },
50             "macOS": {
51                 "revision": "15A284",
52             }
53         },
54         "builderName": "someBuilder",
55         "slaveName": "someSlave",
56         "slavePassword": "somePassword",
57         "platform": "some platform",
58         "tests": {
59             "some test": {
60                 "metrics": {
61                     "Time": ["Arithmetic"],
62                 },
63                 "tests": {
64                     "test1": {
65                         "metrics": {"Time": { "current": [12] }},
66                     }
67                 }
68             },
69         }}];
70
71     const db = TestServer.database();
72     const remote = TestServer.remoteAPI();
73     return addSlaveForReport(reportWithRevision[0]).then(() => {
74         return remote.postJSON('/api/report/', reportWithRevision);
75     }).then(() => {
76         return remote.postJSON('/api/report/', anotherReportWithRevision);
77     }).then((result) => {
78         return Manifest.fetch();
79     }).then(() => {
80         const test = Test.findByPath(['some test', 'test1']);
81         const platform = Platform.findByName('some platform');
82         return db.selectFirstRow('test_configurations', {metric: test.metrics()[0].id(), platform: platform.id()});
83     }).then((configRow) => {
84         return db.selectRows('test_runs', {config: configRow['id']});
85     }).then((testRuns) => {
86         assert.equal(testRuns.length, 2);
87         return PrivilegedAPI.sendRequest('create-analysis-task', {
88             name: name,
89             startRun: testRuns[0]['id'],
90             endRun: testRuns[1]['id'],
91         });
92     }).then((content) => content['taskId']);
93 }
94
95 function addTriggerableAndCreateTask(name, webkitRevisions)
96 {
97     const report = {
98         'slaveName': 'anotherSlave',
99         'slavePassword': 'anotherPassword',
100         'triggerable': 'build-webkit',
101         'configurations': [
102             {test: MockData.someTestId(), platform: MockData.somePlatformId()},
103             {test: MockData.someTestId(), platform: MockData.otherPlatformId()},
104         ],
105         'repositoryGroups': [
106             {name: 'os-only', acceptsRoot: true, repositories: [
107                 {repository: MockData.macosRepositoryId(), acceptsPatch: false},
108             ]},
109             {name: 'webkit-only', acceptsRoot: true, repositories: [
110                 {repository: MockData.webkitRepositoryId(), acceptsPatch: true},
111             ]},
112             {name: 'system-and-webkit', acceptsRoot: true, repositories: [
113                 {repository: MockData.macosRepositoryId(), acceptsPatch: false},
114                 {repository: MockData.webkitRepositoryId(), acceptsPatch: true}
115             ]},
116             {name: 'system-webkit-sjc', acceptsRoot: true, repositories: [
117                 {repository: MockData.macosRepositoryId(), acceptsPatch: false},
118                 {repository: MockData.jscRepositoryId(), acceptsPatch: false},
119                 {repository: MockData.webkitRepositoryId(), acceptsPatch: true}
120             ]},
121         ]
122     };
123     return MockData.addMockData(TestServer.database()).then(() => {
124         return addSlaveForReport(report);
125     }).then(() => {
126         return TestServer.remoteAPI().postJSON('/api/update-triggerable/', report);
127     }).then(() => {
128         return createAnalysisTask(name, webkitRevisions);
129     });
130 }
131
132 describe('/privileged-api/create-test-group', function () {
133     prepareServerTest(this);
134     TemporaryFile.inject();
135
136     it('should return "InvalidName" on an empty request', () => {
137         return PrivilegedAPI.sendRequest('create-test-group', {}).then((content) => {
138             assert(false, 'should never be reached');
139         }, (error) => {
140             assert.equal(error, 'InvalidName');
141         });
142     });
143
144     it('should return "InvalidTask" when task is not specified', () => {
145         return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', commitSets: [[1]]}).then((content) => {
146             assert(false, 'should never be reached');
147         }, (error) => {
148             assert.equal(error, 'InvalidTask');
149         });
150     });
151
152     it('should return "InvalidTask" when task is not a valid integer', () => {
153         return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: 'foo', commitSets: [[1]]}).then((content) => {
154             assert(false, 'should never be reached');
155         }, (error) => {
156             assert.equal(error, 'InvalidTask');
157         });
158     });
159
160     it('should return "InvalidCommitSets" when commit sets are not specified', () => {
161         return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: 1, repetitionCount: 1}).then((content) => {
162             assert(false, 'should never be reached');
163         }, (error) => {
164             assert.equal(error, 'InvalidCommitSets');
165         });
166     });
167
168     it('should return "InvalidCommitSets" when commit sets is empty', () => {
169         return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: 1, repetitionCount: 1, commitSets: {}}).then((content) => {
170             assert(false, 'should never be reached');
171         }, (error) => {
172             assert.equal(error, 'InvalidCommitSets');
173         });
174     });
175
176     it('should return "InvalidTask" when there is no matching task', () => {
177         return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: 1, repetitionCount: 1, commitSets: {'WebKit': []}}).then((content) => {
178             assert(false, 'should never be reached');
179         }, (error) => {
180             assert.equal(error, 'InvalidTask');
181         });
182     });
183
184     it('should return "InvalidRepetitionCount" when repetitionCount is not a valid integer', () => {
185         return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: 1, repetitionCount: 'foo', commitSets: {'WebKit': []}}).then((content) => {
186             assert(false, 'should never be reached');
187         }, (error) => {
188             assert.equal(error, 'InvalidRepetitionCount');
189         });
190     });
191
192     it('should return "InvalidRepetitionCount" when repetitionCount is a negative integer', () => {
193         return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: 1, repetitionCount: -5, commitSets: {'WebKit': []}}).then((content) => {
194             assert(false, 'should never be reached');
195         }, (error) => {
196             assert.equal(error, 'InvalidRepetitionCount');
197         });
198     });
199
200     it('should return "InvalidTask" when there is no matching task', () => {
201         return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: 1, commitSets: {'WebKit': []}}).then((content) => {
202             assert(false, 'should never be reached');
203         }, (error) => {
204             assert.equal(error, 'InvalidTask');
205         });
206     });
207
208     it('should return "TriggerableNotFoundForTask" when there is no matching triggerable', () => {
209         return createAnalysisTask('some task').then((taskId) => {
210             return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, commitSets: {'WebKit': []}}).then((content) => {
211                 assert(false, 'should never be reached');
212             }, (error) => {
213                 assert.equal(error, 'TriggerableNotFoundForTask');
214             });
215         });
216     });
217
218     it('should return "InvalidCommitSets" when each repository specifies zero revisions', () => {
219         return addTriggerableAndCreateTask('some task').then((taskId) => {
220             return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, commitSets: {'WebKit': []}}).then((content) => {
221                 assert(false, 'should never be reached');
222             }, (error) => {
223                 assert.equal(error, 'InvalidCommitSets');
224             });
225         });
226     });
227
228     it('should return "InvalidRevisionSets" when a revision set is empty', () => {
229         return addTriggerableAndCreateTask('some task').then((taskId) => {
230             const webkit = Repository.all().find((repository) => repository.name() == 'WebKit');
231             return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, revisionSets: [{[webkit.id()]: {revision: '191622'}}, {}]}).then((content) => {
232                 assert(false, 'should never be reached');
233             }, (error) => {
234                 assert.equal(error, 'InvalidRevisionSets');
235             });
236         });
237     });
238
239     it('should return "InvalidRevisionSets" when the number of revision sets is less than two', () => {
240         return addTriggerableAndCreateTask('some task').then((taskId) => {
241             const webkit = Repository.all().find((repository) => repository.name() == 'WebKit');
242             return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, revisionSets: [{[webkit.id()]: {revision: '191622'}}]}).then((content) => {
243                 assert(false, 'should never be reached');
244             }, (error) => {
245                 assert.equal(error, 'InvalidRevisionSets');
246             });
247         });
248     });
249
250     it('should return "RepositoryNotFound" when commit sets contains an invalid repository', () => {
251         return addTriggerableAndCreateTask('some task').then((taskId) => {
252             return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, commitSets: {'Foo': []}}).then((content) => {
253                 assert(false, 'should never be reached');
254             }, (error) => {
255                 assert.equal(error, 'RepositoryNotFound');
256             });
257         });
258     });
259
260     it('should return "RevisionNotFound" when commit sets contains an invalid revision', () => {
261         return addTriggerableAndCreateTask('some task').then((taskId) => {
262             return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, commitSets: {'WebKit': ['1']}}).then((content) => {
263                 assert(false, 'should never be reached');
264             }, (error) => {
265                 assert.equal(error, 'RevisionNotFound');
266             });
267         });
268     });
269
270     it('should return "RevisionNotFound" when revision sets contains an invalid revision', () => {
271         return addTriggerableAndCreateTask('some task').then((taskId) => {
272             const webkit = Repository.all().find((repository) => repository.name() == 'WebKit');
273             const revisionSets = [{[webkit.id()]: {revision: '191622'}}, {[webkit.id()]: {revision: '1a'}}];
274             return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, revisionSets}).then((content) => {
275                 assert(false, 'should never be reached');
276             }, (error) => {
277                 assert.equal(error, 'RevisionNotFound');
278             });
279         });
280     });
281
282     it('should return "AmbiguousRevision" when there are multiple commits that match the specified revision string', () => {
283         return addTriggerableAndCreateTask('some task', ['2ceda45d3cd63cde58d0dbf5767714e03d902e43', '2c71a8ddc1f661663ccfd1a29c633ba57e879533']).then((taskId) => {
284             const webkit = Repository.all().find((repository) => repository.name() == 'WebKit');
285             const revisionSets = [{[webkit.id()]: {revision: '2ceda'}}, {[webkit.id()]: {revision: '2c'}}];
286             return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, revisionSets}).then((content) => {
287                 assert(false, 'should never be reached');
288             }, (error) => {
289                 assert.equal(error, 'AmbiguousRevision');
290             });
291         });
292     });
293
294     it('should return "RevisionNotFound" when the end of a Git hash is specified', () => {
295         return addTriggerableAndCreateTask('some task', ['2ceda45d3cd63cde58d0dbf5767714e03d902e43', '5471a8ddc1f661663ccfd1a29c633ba57e879533']).then((taskId) => {
296             const webkit = Repository.all().find((repository) => repository.name() == 'WebKit');
297             const revisionSets = [{[webkit.id()]: {revision: '2ceda45d3cd63cde58d0dbf5767714e03d902e43'}}, {[webkit.id()]: {revision: '57e879533'}}];
298             return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, revisionSets}).then((content) => {
299                 assert(false, 'should never be reached');
300             }, (error) => {
301                 assert.equal(error, 'RevisionNotFound');
302             });
303         });
304     });
305
306     it('should return "InvalidUploadedFile" when revision sets contains an invalid file ID', () => {
307         return addTriggerableAndCreateTask('some task').then((taskId) => {
308             const webkit = Repository.all().find((repository) => repository.name() == 'WebKit');
309             const revisionSets = [{[webkit.id()]: {revision: '191622'}, 'customRoots': ['1']}, {[webkit.id()]: {revision: '1'}}];
310             return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, revisionSets}).then((content) => {
311                 assert(false, 'should never be reached');
312             }, (error) => {
313                 assert.equal(error, 'InvalidUploadedFile');
314             });
315         });
316     });
317
318     it('should return "InvalidRepository" when a revision set uses a repository name instead of a repository id', () => {
319         return addTriggerableAndCreateTask('some task').then((taskId) => {
320             const revisionSets = [{'WebKit': {revision: '191622'}}, {}];
321             return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, revisionSets}).then((content) => {
322                 assert(false, 'should never be reached');
323             }, (error) => {
324                 assert.equal(error, 'InvalidRepository');
325             });
326         });
327     });
328
329     it('should return "InvalidCommitSets" when commit sets contains an inconsistent number of revisions', () => {
330         return addTriggerableAndCreateTask('some task').then((taskId) => {
331             return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, commitSets: {'WebKit': ['191622', '191623'], 'macOS': ['15A284']}}).then((content) => {
332                 assert(false, 'should never be reached');
333             }, (error) => {
334                 assert.equal(error, 'InvalidCommitSets');
335             });
336         });
337     });
338
339     it('should return "DuplicateTestGroupName" when there is already a test group of the same name', () => {
340         return addTriggerableAndCreateTask('some task').then((taskId) => {
341             const commitSets = {'WebKit': ['191622', '191623']};
342             return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, commitSets}).then((content) => {
343                 assert(content['testGroupId']);
344                 return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, commitSets});
345             }).then(() => {
346                 assert(false, 'should never be reached');
347             }, (error) => {
348                 assert.equal(error, 'DuplicateTestGroupName');
349             });
350         });
351     });
352
353     it('should return "InvalidOwnerRevision" when commit ownership is not valid', () => {
354         let taskId;
355         return addTriggerableAndCreateTask('some task').then((id) => taskId = id).then(() => {
356             const webkit = Repository.all().filter((repository) => repository.name() == 'WebKit')[0];
357             const macos = Repository.all().filter((repository) => repository.name() == 'macOS')[0];
358             const ownedJSC = Repository.all().filter((repository) => repository.name() == 'JavaScriptCore' && repository.ownerId())[0];
359             const revisionSets = [{[webkit.id()]: {revision: '191622'}, [macos.id()]: {revision: '15A284'}, [ownedJSC.id()]: {revision: 'owned-jsc-6161', ownerRevision: '191621'}},
360                 {[webkit.id()]: {revision: '191622'}, [macos.id()]: {revision: '15A284'}, [ownedJSC.id()]: {revision: 'owned-jsc-9191', ownerRevision: '191622'}}];
361             return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, repetitionCount: 2, revisionSets});
362         }).then(() => {
363             assert(false, 'should never be reached');
364         }, (error) => {
365             assert.equal(error, 'InvalidOwnerRevision');
366         });
367     });
368
369     it('should return "InvalidCommitOwnership" when commit ownership is not valid', () => {
370         let taskId;
371         return addTriggerableAndCreateTask('some task').then((id) => taskId = id).then(() => {
372             const webkit = Repository.all().filter((repository) => repository.name() == 'WebKit')[0];
373             const macos = Repository.all().filter((repository) => repository.name() == 'macOS')[0];
374             const ownedJSC = Repository.all().filter((repository) => repository.name() == 'JavaScriptCore' && repository.ownerId())[0];
375             const revisionSets = [{[webkit.id()]: {revision: '191622'}, [macos.id()]: {revision: '15A284'}, [ownedJSC.id()]: {revision: 'owned-jsc-6161', ownerRevision: '191622'}},
376                 {[webkit.id()]: {revision: '191622'}, [macos.id()]: {revision: '15A284'}, [ownedJSC.id()]: {revision: 'owned-jsc-9191', ownerRevision: '191622'}}];
377             return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, repetitionCount: 2, revisionSets});
378         }).then(() => {
379             assert(false, 'should never be reached');
380         }, (error) => {
381             assert.equal(error, 'InvalidCommitOwnership');
382         });
383     });
384
385     it('should allow a commit set in which owner of a commit is not in the same set', () => {
386         let taskId;
387         let groupId;
388         return addTriggerableAndCreateTask('some task').then((id) => taskId = id).then(() => {
389             const webkit = Repository.all().filter((repository) => repository.name() == 'WebKit')[0];
390             const macos = Repository.all().filter((repository) => repository.name() == 'macOS')[0];
391             const ownedJSC = Repository.all().filter((repository) => repository.name() == 'JavaScriptCore' && repository.ownerId())[0];
392             const revisionSets = [{[webkit.id()]: {revision: '191622'}, [macos.id()]: {revision: '15A284'}, [ownedJSC.id()]: {revision: 'owned-jsc-6161', ownerRevision: '191622'}},
393                 {[webkit.id()]: {revision: '191622'}, [macos.id()]: {revision: '15A284'}, [ownedJSC.id()]: {revision: 'owned-jsc-9191', ownerRevision: '192736'}}];
394             return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, repetitionCount: 1, revisionSets});
395         }).then((content) => {
396             assert.equal(content['status'], 'OK');
397             groupId = content['testGroupId'];
398             return TestGroup.fetchForTask(taskId, true);
399         }).then((testGroups) => {
400             assert.equal(testGroups.length, 1);
401             const group = testGroups[0];
402             assert.equal(group.id(), groupId);
403             assert.equal(group.repetitionCount(), 1);
404             assert.ok(!group.needsNotification());
405             const requests = group.buildRequests();
406             assert.equal(requests.length, 4);
407             assert(requests[0].isBuild());
408             assert(!requests[0].isTest());
409             assert(requests[1].isBuild());
410             assert(!requests[1].isTest());
411             assert(!requests[2].isBuild());
412             assert(requests[2].isTest());
413             assert(!requests[3].isBuild());
414             assert(requests[3].isTest());
415
416             const macos = Repository.findById(MockData.macosRepositoryId());
417             const webkit = Repository.findById(MockData.webkitRepositoryId());
418             const ownedJSC = Repository.findById(MockData.ownedJSCRepositoryId());
419             const set0 = requests[0].commitSet();
420             const set1 = requests[1].commitSet();
421             assert.deepEqual(Repository.sortByNamePreferringOnesWithURL(set0.repositories()), [ownedJSC, webkit, macos]);
422             assert.deepEqual(Repository.sortByNamePreferringOnesWithURL(set1.repositories()), [ownedJSC, webkit, macos]);
423             assert.equal(set0.revisionForRepository(macos), '15A284');
424             assert.equal(set0.revisionForRepository(webkit), '191622');
425             assert.equal(set0.revisionForRepository(ownedJSC), 'owned-jsc-6161');
426             assert.equal(set1.revisionForRepository(macos), '15A284');
427             assert.equal(set1.revisionForRepository(webkit), '191622');
428             assert.equal(set1.revisionForRepository(ownedJSC), 'owned-jsc-9191');
429         });
430     });
431
432     it('should allow a commit set in which repository of owner commit is not specified at all', () => {
433         let taskId;
434         let groupId;
435         return addTriggerableAndCreateTask('some task').then((id) => taskId = id).then(() => {
436             const webkit = Repository.all().filter((repository) => repository.name() == 'WebKit')[0];
437             const macos = Repository.all().filter((repository) => repository.name() == 'macOS')[0];
438             const jsc = Repository.all().filter((repository) => repository.name() == 'JavaScriptCore' && repository.ownerId())[0];
439             const revisionSets = [{[webkit.id()]: {revision: '191622'}, [macos.id()]: {revision: '15A284'}, [jsc.id()]: {revision: 'owned-jsc-6161', ownerRevision: '191622'}},
440                 {[macos.id()]: {revision: '15A284'}, [jsc.id()]: {revision: 'owned-jsc-9191', ownerRevision: '192736'}}];
441             return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, repetitionCount: 1, revisionSets});
442         }).then((content) => {
443             assert.equal(content['status'], 'OK');
444             groupId = content['testGroupId'];
445             return TestGroup.fetchForTask(taskId, true);
446         }).then((testGroups) => {
447             assert.equal(testGroups.length, 1);
448             const group = testGroups[0];
449             assert.equal(group.id(), groupId);
450             assert.equal(group.repetitionCount(), 1);
451             assert.ok(!group.needsNotification());
452             const requests = group.buildRequests();
453             assert.equal(requests.length, 4);
454             assert(requests[0].isBuild());
455             assert(!requests[0].isTest());
456             assert(requests[1].isBuild());
457             assert(!requests[1].isTest());
458             assert(!requests[2].isBuild());
459             assert(requests[2].isTest());
460             assert(!requests[3].isBuild());
461             assert(requests[3].isTest());
462
463             const macos = Repository.findById(MockData.macosRepositoryId());
464             const webkit = Repository.findById(MockData.webkitRepositoryId());
465             const ownedJSC = Repository.findById(MockData.ownedJSCRepositoryId());
466             const set0 = requests[0].commitSet();
467             const set1 = requests[1].commitSet();
468             assert.deepEqual(Repository.sortByNamePreferringOnesWithURL(set0.repositories()), [ownedJSC, webkit, macos]);
469             assert.deepEqual(Repository.sortByNamePreferringOnesWithURL(set1.repositories()), [ownedJSC, macos]);
470             assert.equal(set0.revisionForRepository(macos), '15A284');
471             assert.equal(set0.revisionForRepository(webkit), '191622');
472             assert.equal(set0.revisionForRepository(ownedJSC), 'owned-jsc-6161');
473             assert.equal(set1.revisionForRepository(macos), '15A284');
474             assert.equal(set1.revisionForRepository(ownedJSC), 'owned-jsc-9191');
475         });
476     });
477
478     it('should create a test group from commitSets with the repetition count of one when repetitionCount is omitted', () => {
479         return addTriggerableAndCreateTask('some task').then((taskId) => {
480             let insertedGroupId;
481             return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, commitSets: {'macOS': ['15A284', '15A284'], 'WebKit': ['191622', '191623']}}).then((content) => {
482                 insertedGroupId = content['testGroupId'];
483                 return TestGroup.fetchForTask(taskId, true);
484             }).then((testGroups) => {
485                 assert.equal(testGroups.length, 1);
486                 const group = testGroups[0];
487                 assert.equal(group.id(), insertedGroupId);
488                 assert.equal(group.repetitionCount(), 1);
489                 assert.ok(!group.needsNotification());
490                 const requests = group.buildRequests();
491                 assert.equal(requests.length, 2);
492
493                 const macos = Repository.findById(MockData.macosRepositoryId());
494                 const webkit = Repository.findById(MockData.webkitRepositoryId());
495                 const set0 = requests[0].commitSet();
496                 const set1 = requests[1].commitSet();
497                 assert.deepEqual(Repository.sortByNamePreferringOnesWithURL(set0.repositories()), [webkit, macos]);
498                 assert.deepEqual(Repository.sortByNamePreferringOnesWithURL(set1.repositories()), [webkit, macos]);
499                 assert.equal(set0.revisionForRepository(macos), '15A284');
500                 assert.equal(set0.revisionForRepository(webkit), '191622');
501                 assert.equal(set1.revisionForRepository(macos), '15A284');
502                 assert.equal(set1.revisionForRepository(webkit), '191623');
503
504                 const repositoryGroup = requests[0].repositoryGroup();
505                 assert.equal(repositoryGroup.name(), 'system-and-webkit');
506                 assert.equal(requests[1].repositoryGroup(), repositoryGroup);
507                 assert(repositoryGroup.accepts(set0));
508                 assert(repositoryGroup.accepts(set1));
509             });
510         });
511     });
512
513     it('should create a test group from revisionSets with the repetition count of one when repetitionCount is omitted', () => {
514         let webkit;
515         return addTriggerableAndCreateTask('some task').then((taskId) => {
516             const webkit = Repository.findById(MockData.webkitRepositoryId());
517             const revisionSets = [{[webkit.id()]: {revision: '191622'}}, {[webkit.id()]: {revision: '191623'}}];
518             const params = {name: 'test', task: taskId, revisionSets};
519             let insertedGroupId;
520             return PrivilegedAPI.sendRequest('create-test-group', params).then((content) => {
521                 insertedGroupId = content['testGroupId'];
522                 return TestGroup.fetchForTask(taskId, true);
523             }).then((testGroups) => {
524                 assert.equal(testGroups.length, 1);
525                 const group = testGroups[0];
526                 assert.equal(group.id(), insertedGroupId);
527                 assert.equal(group.repetitionCount(), 1);
528                 assert.ok(!group.needsNotification());
529                 const requests = group.buildRequests();
530                 assert.equal(requests.length, 2);
531
532                 const set0 = requests[0].commitSet();
533                 const set1 = requests[1].commitSet();
534                 assert.deepEqual(set0.repositories(), [webkit]);
535                 assert.deepEqual(set1.repositories(), [webkit]);
536                 assert.equal(set0.revisionForRepository(webkit), '191622');
537                 assert.equal(set1.revisionForRepository(webkit), '191623');
538
539                 const repositoryGroup = requests[0].repositoryGroup();
540                 assert.equal(repositoryGroup.name(), 'webkit-only');
541                 assert.equal(repositoryGroup, requests[1].repositoryGroup());
542                 assert(repositoryGroup.accepts(set0));
543                 assert(repositoryGroup.accepts(set1));
544             });
545         });
546     });
547
548     it('should create a test group with the repetition count of two with two repositories', () => {
549         return addTriggerableAndCreateTask('some task').then((taskId) => {
550             let insertedGroupId;
551             return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, repetitionCount: 2,
552                 commitSets: {'WebKit': ['191622', '191623'], 'macOS': ['15A284', '15A284']}}).then((content) => {
553                 insertedGroupId = content['testGroupId'];
554                 return TestGroup.fetchForTask(taskId, true);
555             }).then((testGroups) => {
556                 assert.equal(testGroups.length, 1);
557                 const group = testGroups[0];
558                 assert.equal(group.id(), insertedGroupId);
559                 assert.equal(group.repetitionCount(), 2);
560                 assert.ok(!group.needsNotification());
561                 const requests = group.buildRequests();
562                 assert.equal(requests.length, 4);
563                 const webkit = Repository.all().filter((repository) => repository.name() == 'WebKit')[0];
564                 const macos = Repository.all().filter((repository) => repository.name() == 'macOS')[0];
565
566                 const set0 = requests[0].commitSet();
567                 const set1 = requests[1].commitSet();
568                 assert.equal(requests[2].commitSet(), set0);
569                 assert.equal(requests[3].commitSet(), set1);
570                 assert.deepEqual(Repository.sortByNamePreferringOnesWithURL(set0.repositories()), [webkit, macos]);
571                 assert.deepEqual(Repository.sortByNamePreferringOnesWithURL(set1.repositories()), [webkit, macos]);
572                 assert.equal(set0.revisionForRepository(webkit), '191622');
573                 assert.equal(set0.revisionForRepository(macos), '15A284');
574                 assert.equal(set1.revisionForRepository(webkit), '191623');
575                 assert.equal(set1.revisionForRepository(macos), '15A284');
576                 assert.equal(set0.commitForRepository(macos), set1.commitForRepository(macos));
577
578                 const repositoryGroup = requests[0].repositoryGroup();
579                 assert.equal(repositoryGroup.name(), 'system-and-webkit');
580                 assert.equal(requests[1].repositoryGroup(), repositoryGroup);
581                 assert.equal(requests[2].repositoryGroup(), repositoryGroup);
582                 assert.equal(requests[3].repositoryGroup(), repositoryGroup);
583                 assert(repositoryGroup.accepts(set0));
584                 assert(repositoryGroup.accepts(set1));
585             });
586         });
587     });
588
589     it('should create a test group using Git partial hashes', () => {
590         let webkit;
591         let macos;
592         return addTriggerableAndCreateTask('some task', ['2ceda45d3cd63cde58d0dbf5767714e03d902e43', '5471a8ddc1f661663ccfd1a29c633ba57e879533']).then((taskId) => {
593             webkit = Repository.findById(MockData.webkitRepositoryId());
594             macos = Repository.findById(MockData.macosRepositoryId());
595             const revisionSets = [{[macos.id()]: {revision: '15A284'}, [webkit.id()]: {revision: '2ceda'}},
596                 {[macos.id()]: {revision: '15A284'}, [webkit.id()]: {revision: '5471a'}}];
597             const params = {name: 'test', task: taskId, repetitionCount: 2, revisionSets};
598             let insertedGroupId;
599             return PrivilegedAPI.sendRequest('create-test-group', params).then((content) => {
600                 insertedGroupId = content['testGroupId'];
601                 return TestGroup.fetchForTask(taskId, true);
602             }).then((testGroups) => {
603                 assert.equal(testGroups.length, 1);
604                 const group = testGroups[0];
605                 assert.equal(group.id(), insertedGroupId);
606                 assert.equal(group.repetitionCount(), 2);
607                 assert.ok(!group.needsNotification());
608                 const requests = group.buildRequests();
609                 assert.equal(requests.length, 4);
610
611                 const set0 = requests[0].commitSet();
612                 const set1 = requests[1].commitSet();
613                 assert.deepEqual(Repository.sortByNamePreferringOnesWithURL(set0.repositories()), [webkit, macos]);
614                 assert.deepEqual(Repository.sortByNamePreferringOnesWithURL(set1.repositories()), [webkit, macos]);
615                 assert.equal(set0.revisionForRepository(webkit), '2ceda45d3cd63cde58d0dbf5767714e03d902e43');
616                 assert.equal(set0.revisionForRepository(macos), '15A284');
617                 assert.equal(set1.revisionForRepository(webkit), '5471a8ddc1f661663ccfd1a29c633ba57e879533');
618                 assert.equal(set1.revisionForRepository(macos), '15A284');
619
620                 const repositoryGroup0 = requests[0].repositoryGroup();
621                 assert.equal(repositoryGroup0.name(), 'system-and-webkit');
622                 assert.equal(repositoryGroup0, requests[2].repositoryGroup());
623                 const repositoryGroup1 = requests[1].repositoryGroup();
624                 assert.equal(repositoryGroup1, repositoryGroup0);
625                 assert(repositoryGroup0.accepts(set0));
626                 assert(repositoryGroup0.accepts(set1));
627             });
628         });
629     });
630
631     it('should create a test group using different repository groups if needed', () => {
632         let webkit;
633         let macos;
634         return addTriggerableAndCreateTask('some task').then((taskId) => {
635             webkit = Repository.findById(MockData.webkitRepositoryId());
636             macos = Repository.findById(MockData.macosRepositoryId());
637             const revisionSets = [{[macos.id()]: {revision: '15A284'}, [webkit.id()]: {revision: '191622'}},
638                 {[webkit.id()]: {revision: '191623'}}];
639             const params = {name: 'test', task: taskId, repetitionCount: 2, revisionSets};
640             let insertedGroupId;
641             return PrivilegedAPI.sendRequest('create-test-group', params).then((content) => {
642                 insertedGroupId = content['testGroupId'];
643                 return TestGroup.fetchForTask(taskId, true);
644             }).then((testGroups) => {
645                 assert.equal(testGroups.length, 1);
646                 const group = testGroups[0];
647                 assert.equal(group.id(), insertedGroupId);
648                 assert.equal(group.repetitionCount(), 2);
649                 assert.ok(!group.needsNotification());
650                 const requests = group.buildRequests();
651                 assert.equal(requests.length, 4);
652
653                 const set0 = requests[0].commitSet();
654                 const set1 = requests[1].commitSet();
655                 assert.deepEqual(Repository.sortByNamePreferringOnesWithURL(set0.repositories()), [webkit, macos]);
656                 assert.deepEqual(set1.repositories(), [webkit]);
657                 assert.equal(set0.revisionForRepository(webkit), '191622');
658                 assert.equal(set0.revisionForRepository(macos), '15A284');
659                 assert.equal(set1.revisionForRepository(webkit), '191623');
660                 assert.equal(set1.revisionForRepository(macos), null);
661
662                 const repositoryGroup0 = requests[0].repositoryGroup();
663                 assert.equal(repositoryGroup0.name(), 'system-and-webkit');
664                 assert.equal(repositoryGroup0, requests[2].repositoryGroup());
665                 assert(repositoryGroup0.accepts(set0));
666                 assert(!repositoryGroup0.accepts(set1));
667
668                 const repositoryGroup1 = requests[1].repositoryGroup();
669                 assert.equal(repositoryGroup1.name(), 'webkit-only');
670                 assert.equal(repositoryGroup1, requests[3].repositoryGroup());
671                 assert(!repositoryGroup1.accepts(set0));
672                 assert(repositoryGroup1.accepts(set1));
673             });
674         });
675     });
676
677     it('should create a test group with a custom root', () => {
678         return addTriggerableAndCreateTask('some task').then((taskId) => {
679             let insertedGroupId;
680             const webkit = Repository.all().filter((repository) => repository.name() == 'WebKit')[0];
681             const macos = Repository.all().filter((repository) => repository.name() == 'macOS')[0];
682             let uploadedFile;
683             return TemporaryFile.makeTemporaryFile('some.dat', 'some content').then((stream) => {
684                 return PrivilegedAPI.sendRequest('upload-file', {newFile: stream}, {useFormData: true});
685             }).then((response) => {
686                 uploadedFile = response['uploadedFile'];
687                 const revisionSets = [{[webkit.id()]: {revision: '191622'}, [macos.id()]: {revision: '15A284'}},
688                     {[webkit.id()]: {revision: '191622'}, [macos.id()]: {revision: '15A284'}, 'customRoots': [uploadedFile['id']]}];
689                 return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, repetitionCount: 2, revisionSets}).then((content) => {
690                     insertedGroupId = content['testGroupId'];
691                     return TestGroup.fetchForTask(taskId, true);
692                 });
693             }).then((testGroups) => {
694                 assert.equal(testGroups.length, 1);
695                 const group = testGroups[0];
696                 assert.equal(group.id(), insertedGroupId);
697                 assert.equal(group.repetitionCount(), 2);
698                 assert.ok(!group.needsNotification());
699                 const requests = group.buildRequests();
700                 assert.equal(requests.length, 4);
701
702                 const set0 = requests[0].commitSet();
703                 const set1 = requests[1].commitSet();
704                 assert.equal(requests[2].commitSet(), set0);
705                 assert.equal(requests[3].commitSet(), set1);
706                 assert.deepEqual(Repository.sortByNamePreferringOnesWithURL(set0.repositories()), [webkit, macos]);
707                 assert.deepEqual(set0.customRoots(), []);
708                 assert.deepEqual(Repository.sortByNamePreferringOnesWithURL(set1.repositories()), [webkit, macos]);
709                 assert.deepEqual(set1.customRoots(), [UploadedFile.ensureSingleton(uploadedFile['id'], uploadedFile)]);
710                 assert.equal(set0.revisionForRepository(webkit), '191622');
711                 assert.equal(set0.revisionForRepository(webkit), set1.revisionForRepository(webkit));
712                 assert.equal(set0.commitForRepository(webkit), set1.commitForRepository(webkit));
713                 assert.equal(set0.revisionForRepository(macos), '15A284');
714                 assert.equal(set0.commitForRepository(macos), set1.commitForRepository(macos));
715                 assert.equal(set0.revisionForRepository(macos), set1.revisionForRepository(macos));
716                 assert(!set0.equals(set1));
717             });
718         });
719     });
720
721     it('should create a build test group with a patch', () => {
722         let taskId;
723         let webkit;
724         let macos;
725         let insertedGroupId;
726         let uploadedFile;
727         return addTriggerableAndCreateTask('some task').then((id) => taskId = id).then(() => {
728             webkit = Repository.all().filter((repository) => repository.name() == 'WebKit')[0];
729             macos = Repository.all().filter((repository) => repository.name() == 'macOS')[0];
730             return TemporaryFile.makeTemporaryFile('some.dat', 'some content');
731         }).then((stream) => {
732             return PrivilegedAPI.sendRequest('upload-file', {newFile: stream}, {useFormData: true});
733         }).then((response) => {
734             const rawFile = response['uploadedFile'];
735             uploadedFile = UploadedFile.ensureSingleton(rawFile.id, rawFile);
736             const revisionSets = [{[webkit.id()]: {revision: '191622', patch: uploadedFile.id()}, [macos.id()]: {revision: '15A284'}},
737                 {[webkit.id()]: {revision: '191622'}, [macos.id()]: {revision: '15A284'}}];
738             return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, repetitionCount: 2, revisionSets});
739         }).then((content) => {
740             insertedGroupId = content['testGroupId'];
741             return TestGroup.fetchForTask(taskId, true);
742         }).then((testGroups) => {
743             assert.equal(testGroups.length, 1);
744             const group = testGroups[0];
745             assert.equal(group.id(), insertedGroupId);
746             assert.equal(group.repetitionCount(), 2);
747             assert.ok(!group.needsNotification());
748             assert.equal(group.test(), Test.findById(MockData.someTestId()));
749             assert.equal(group.platform(), Platform.findById(MockData.somePlatformId()));
750             const requests = group.buildRequests();
751             assert.equal(requests.length, 6);
752
753             assert.equal(requests[0].isBuild(), true);
754             assert.equal(requests[1].isBuild(), true);
755             assert.equal(requests[2].isBuild(), false);
756             assert.equal(requests[3].isBuild(), false);
757             assert.equal(requests[4].isBuild(), false);
758             assert.equal(requests[5].isBuild(), false);
759
760             assert.equal(requests[0].isTest(), false);
761             assert.equal(requests[1].isTest(), false);
762             assert.equal(requests[2].isTest(), true);
763             assert.equal(requests[3].isTest(), true);
764             assert.equal(requests[4].isTest(), true);
765             assert.equal(requests[5].isTest(), true);
766
767             const set0 = requests[0].commitSet();
768             const set1 = requests[1].commitSet();
769             assert.equal(requests[2].commitSet(), set0);
770             assert.equal(requests[3].commitSet(), set1);
771             assert.equal(requests[4].commitSet(), set0);
772             assert.equal(requests[5].commitSet(), set1);
773             assert.deepEqual(Repository.sortByNamePreferringOnesWithURL(set0.repositories()), [webkit, macos]);
774             assert.deepEqual(set0.customRoots(), []);
775             assert.deepEqual(Repository.sortByNamePreferringOnesWithURL(set1.repositories()), [webkit, macos]);
776             assert.deepEqual(set1.customRoots(), []);
777             assert.equal(set0.revisionForRepository(webkit), '191622');
778             assert.equal(set0.revisionForRepository(webkit), set1.revisionForRepository(webkit));
779             assert.equal(set0.commitForRepository(webkit), set1.commitForRepository(webkit));
780             assert.equal(set0.patchForRepository(webkit), uploadedFile);
781             assert.equal(set1.patchForRepository(webkit), null);
782             assert.equal(set0.revisionForRepository(macos), '15A284');
783             assert.equal(set0.revisionForRepository(macos), set1.revisionForRepository(macos));
784             assert.equal(set0.commitForRepository(macos), set1.commitForRepository(macos));
785             assert.equal(set0.patchForRepository(macos), null);
786             assert.equal(set1.patchForRepository(macos), null);
787             assert(!set0.equals(set1));
788         });
789     });
790
791     it('should create a build test group with a owned commits even when one of group does not contain an owned commit', () => {
792         let taskId;
793         let webkit;
794         let jsc;
795         let macos;
796         let insertedGroupId;
797         return addTriggerableAndCreateTask('some task').then((id) => taskId = id).then(() => {
798             webkit = Repository.all().filter((repository) => repository.name() == 'WebKit')[0];
799             macos = Repository.all().filter((repository) => repository.name() == 'macOS')[0];
800             jsc = Repository.all().filter((repository) => repository.name() == 'JavaScriptCore')[0];
801             const revisionSets = [{[webkit.id()]: {revision: '191622'}, [macos.id()]: {revision: '15A284'}},
802                 {[webkit.id()]: {revision: '192736'}, [macos.id()]: {revision: '15A284'}, [jsc.id()]: {revision: 'owned-jsc-9191', ownerRevision: '192736'}}];
803             return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, repetitionCount: 2, revisionSets});
804         }).then((content) => {
805             insertedGroupId = content['testGroupId'];
806             return TestGroup.fetchForTask(taskId, true);
807         }).then((testGroups) => {
808             assert.equal(testGroups.length, 1);
809             const group = testGroups[0];
810             assert.equal(group.id(), insertedGroupId);
811             assert.equal(group.repetitionCount(), 2);
812             assert.ok(!group.needsNotification());
813             assert.equal(group.test(), Test.findById(MockData.someTestId()));
814             assert.equal(group.platform(), Platform.findById(MockData.somePlatformId()));
815             const requests = group.buildRequests();
816             assert.equal(requests.length, 6);
817
818             assert.equal(requests[0].isBuild(), true);
819             assert.equal(requests[1].isBuild(), true);
820             assert.equal(requests[2].isBuild(), false);
821             assert.equal(requests[3].isBuild(), false);
822             assert.equal(requests[4].isBuild(), false);
823             assert.equal(requests[5].isBuild(), false);
824
825             assert.equal(requests[0].isTest(), false);
826             assert.equal(requests[1].isTest(), false);
827             assert.equal(requests[2].isTest(), true);
828             assert.equal(requests[3].isTest(), true);
829             assert.equal(requests[4].isTest(), true);
830             assert.equal(requests[5].isTest(), true);
831
832             const set0 = requests[0].commitSet();
833             const set1 = requests[1].commitSet();
834             assert.equal(requests[2].commitSet(), set0);
835             assert.equal(requests[3].commitSet(), set1);
836             assert.equal(requests[4].commitSet(), set0);
837             assert.equal(requests[5].commitSet(), set1);
838             assert.deepEqual(Repository.sortByNamePreferringOnesWithURL(set0.repositories()), [webkit, macos]);
839             assert.deepEqual(set0.customRoots(), []);
840             assert.deepEqual(Repository.sortByNamePreferringOnesWithURL(set1.repositories()), [jsc, webkit, macos]);
841             assert.deepEqual(set1.customRoots(), []);
842             assert.equal(set0.revisionForRepository(webkit), '191622');
843             assert.equal(set1.revisionForRepository(webkit), '192736');
844             assert.equal(set0.patchForRepository(webkit), null);
845             assert.equal(set1.patchForRepository(webkit), null);
846             assert.equal(set0.requiresBuildForRepository(webkit), false);
847             assert.equal(set1.requiresBuildForRepository(webkit), false);
848             assert.equal(set0.revisionForRepository(macos), '15A284');
849             assert.equal(set0.revisionForRepository(macos), set1.revisionForRepository(macos));
850             assert.equal(set0.commitForRepository(macos), set1.commitForRepository(macos));
851             assert.equal(set0.patchForRepository(macos), null);
852             assert.equal(set1.patchForRepository(macos), null);
853             assert.equal(set0.requiresBuildForRepository(macos), false);
854             assert.equal(set1.requiresBuildForRepository(macos), false);
855             assert.equal(set0.revisionForRepository(jsc), null);
856             assert.equal(set1.revisionForRepository(jsc), 'owned-jsc-9191');
857             assert.equal(set0.patchForRepository(jsc), null);
858             assert.equal(set1.patchForRepository(jsc), null);
859             assert.equal(set0.ownerRevisionForRepository(jsc), null);
860             assert.equal(set1.ownerRevisionForRepository(jsc), '192736');
861             assert.equal(set1.requiresBuildForRepository(jsc), true);
862             assert(!set0.equals(set1));
863         });
864     });
865
866     it('should create a test group with a owned commits even when no patch is specified', () => {
867         let taskId;
868         let webkit;
869         let jsc;
870         let macos;
871         let insertedGroupId;
872         return addTriggerableAndCreateTask('some task').then((id) => taskId = id).then(() => {
873             webkit = Repository.all().filter((repository) => repository.name() == 'WebKit')[0];
874             macos = Repository.all().filter((repository) => repository.name() == 'macOS')[0];
875             jsc = Repository.all().filter((repository) => repository.name() == 'JavaScriptCore')[0];
876             const revisionSets = [{[webkit.id()]: {revision: '191622'}, [macos.id()]: {revision: '15A284'}, [jsc.id()]: {revision: 'owned-jsc-6161', ownerRevision: '191622'}},
877                 {[webkit.id()]: {revision: '192736'}, [macos.id()]: {revision: '15A284'}, [jsc.id()]: {revision: 'owned-jsc-9191', ownerRevision: '192736'}}];
878             return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, repetitionCount: 2, revisionSets});
879         }).then((content) => {
880             insertedGroupId = content['testGroupId'];
881             return TestGroup.fetchForTask(taskId, true);
882         }).then((testGroups) => {
883             assert.equal(testGroups.length, 1);
884             const group = testGroups[0];
885             assert.equal(group.id(), insertedGroupId);
886             assert.equal(group.repetitionCount(), 2);
887             assert.ok(!group.needsNotification());
888             assert.equal(group.test(), Test.findById(MockData.someTestId()));
889             assert.equal(group.platform(), Platform.findById(MockData.somePlatformId()));
890             const requests = group.buildRequests();
891             assert.equal(requests.length, 6);
892
893             assert.equal(requests[0].isBuild(), true);
894             assert.equal(requests[1].isBuild(), true);
895             assert.equal(requests[2].isBuild(), false);
896             assert.equal(requests[3].isBuild(), false);
897             assert.equal(requests[4].isBuild(), false);
898             assert.equal(requests[5].isBuild(), false);
899
900             assert.equal(requests[0].isTest(), false);
901             assert.equal(requests[1].isTest(), false);
902             assert.equal(requests[2].isTest(), true);
903             assert.equal(requests[3].isTest(), true);
904             assert.equal(requests[4].isTest(), true);
905             assert.equal(requests[5].isTest(), true);
906
907             const set0 = requests[0].commitSet();
908             const set1 = requests[1].commitSet();
909             assert.equal(requests[2].commitSet(), set0);
910             assert.equal(requests[3].commitSet(), set1);
911             assert.equal(requests[4].commitSet(), set0);
912             assert.equal(requests[5].commitSet(), set1);
913             assert.deepEqual(Repository.sortByNamePreferringOnesWithURL(set0.repositories()), [jsc, webkit, macos]);
914             assert.deepEqual(set0.customRoots(), []);
915             assert.deepEqual(Repository.sortByNamePreferringOnesWithURL(set1.repositories()), [jsc, webkit, macos]);
916             assert.deepEqual(set1.customRoots(), []);
917             assert.equal(set0.revisionForRepository(webkit), '191622');
918             assert.equal(set1.revisionForRepository(webkit), '192736');
919             assert.equal(set0.patchForRepository(webkit), null);
920             assert.equal(set1.patchForRepository(webkit), null);
921             assert.equal(set0.requiresBuildForRepository(webkit), false);
922             assert.equal(set1.requiresBuildForRepository(webkit), false);
923             assert.equal(set0.revisionForRepository(macos), '15A284');
924             assert.equal(set0.revisionForRepository(macos), set1.revisionForRepository(macos));
925             assert.equal(set0.commitForRepository(macos), set1.commitForRepository(macos));
926             assert.equal(set0.patchForRepository(macos), null);
927             assert.equal(set1.patchForRepository(macos), null);
928             assert.equal(set0.requiresBuildForRepository(macos), false);
929             assert.equal(set1.requiresBuildForRepository(macos), false);
930             assert.equal(set0.revisionForRepository(jsc), 'owned-jsc-6161');
931             assert.equal(set1.revisionForRepository(jsc), 'owned-jsc-9191');
932             assert.equal(set0.patchForRepository(jsc), null);
933             assert.equal(set1.patchForRepository(jsc), null);
934             assert.equal(set0.ownerRevisionForRepository(jsc), '191622');
935             assert.equal(set1.ownerRevisionForRepository(jsc), '192736');
936             assert.equal(set0.requiresBuildForRepository(jsc), true);
937             assert.equal(set1.requiresBuildForRepository(jsc), true);
938             assert(!set0.equals(set1));
939         });
940     });
941
942     it('should create a test group with a owned commits and a patch', () => {
943         let taskId;
944         let webkit;
945         let macos;
946         let jsc;
947         let insertedGroupId;
948         let uploadedFile;
949         return addTriggerableAndCreateTask('some task').then((id) => taskId = id).then(() => {
950             webkit = Repository.all().filter((repository) => repository.name() == 'WebKit')[0];
951             macos = Repository.all().filter((repository) => repository.name() == 'macOS')[0];
952             jsc = Repository.all().filter((repository) => repository.name() == 'JavaScriptCore')[0];
953             return TemporaryFile.makeTemporaryFile('some.dat', 'some content');
954         }).then((stream) => {
955             return PrivilegedAPI.sendRequest('upload-file', {newFile: stream}, {useFormData: true});
956         }).then((response) => {
957             const rawFile = response['uploadedFile'];
958             uploadedFile = UploadedFile.ensureSingleton(rawFile.id, rawFile);
959             const revisionSets = [{[webkit.id()]: {revision: '191622'}, [macos.id()]: {revision: '15A284'}, [jsc.id()]: {revision: 'owned-jsc-6161', ownerRevision: '191622'}},
960                 {[webkit.id()]: {revision: '192736', patch: uploadedFile.id()}, [macos.id()]: {revision: '15A284'}, [jsc.id()]: {revision: 'owned-jsc-9191', ownerRevision: '192736'}}];
961             return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, repetitionCount: 2, revisionSets});
962         }).then((content) => {
963             insertedGroupId = content['testGroupId'];
964             return TestGroup.fetchForTask(taskId, true);
965         }).then((testGroups) => {
966             assert.equal(testGroups.length, 1);
967             const group = testGroups[0];
968             assert.equal(group.id(), insertedGroupId);
969             assert.equal(group.repetitionCount(), 2);
970             assert.ok(!group.needsNotification());
971             assert.equal(group.test(), Test.findById(MockData.someTestId()));
972             assert.equal(group.platform(), Platform.findById(MockData.somePlatformId()));
973             const requests = group.buildRequests();
974             assert.equal(requests.length, 6);
975
976             assert.equal(requests[0].isBuild(), true);
977             assert.equal(requests[1].isBuild(), true);
978             assert.equal(requests[2].isBuild(), false);
979             assert.equal(requests[3].isBuild(), false);
980             assert.equal(requests[4].isBuild(), false);
981             assert.equal(requests[5].isBuild(), false);
982
983             assert.equal(requests[0].isTest(), false);
984             assert.equal(requests[1].isTest(), false);
985             assert.equal(requests[2].isTest(), true);
986             assert.equal(requests[3].isTest(), true);
987             assert.equal(requests[4].isTest(), true);
988             assert.equal(requests[5].isTest(), true);
989
990             const set0 = requests[0].commitSet();
991             const set1 = requests[1].commitSet();
992             assert.equal(requests[2].commitSet(), set0);
993             assert.equal(requests[3].commitSet(), set1);
994             assert.equal(requests[4].commitSet(), set0);
995             assert.equal(requests[5].commitSet(), set1);
996             assert.deepEqual(Repository.sortByNamePreferringOnesWithURL(set0.repositories()), [jsc, webkit, macos]);
997             assert.deepEqual(set0.customRoots(), []);
998             assert.deepEqual(Repository.sortByNamePreferringOnesWithURL(set1.repositories()), [jsc, webkit, macos]);
999             assert.deepEqual(set1.customRoots(), []);
1000
1001             assert.equal(set0.revisionForRepository(webkit), '191622');
1002             assert.equal(set1.revisionForRepository(webkit), '192736');
1003             assert.equal(set0.patchForRepository(webkit), null);
1004             assert.equal(set1.patchForRepository(webkit), uploadedFile);
1005             assert.equal(set0.ownerRevisionForRepository(webkit), null);
1006             assert.equal(set1.ownerRevisionForRepository(webkit), null);
1007             assert.equal(set0.requiresBuildForRepository(webkit), true);
1008             assert.equal(set1.requiresBuildForRepository(webkit), true);
1009
1010             assert.equal(set0.revisionForRepository(macos), '15A284');
1011             assert.equal(set0.revisionForRepository(macos), set1.revisionForRepository(macos));
1012             assert.equal(set0.commitForRepository(macos), set1.commitForRepository(macos));
1013             assert.equal(set0.patchForRepository(macos), null);
1014             assert.equal(set1.patchForRepository(macos), null);
1015             assert.equal(set0.ownerRevisionForRepository(macos), null);
1016             assert.equal(set1.ownerRevisionForRepository(macos), null);
1017             assert.equal(set0.requiresBuildForRepository(macos), false);
1018             assert.equal(set1.requiresBuildForRepository(macos), false);
1019
1020             assert.equal(set0.revisionForRepository(jsc), 'owned-jsc-6161');
1021             assert.equal(set1.revisionForRepository(jsc), 'owned-jsc-9191');
1022             assert.equal(set0.patchForRepository(jsc), null);
1023             assert.equal(set1.patchForRepository(jsc), null);
1024             assert.equal(set0.ownerRevisionForRepository(jsc), '191622');
1025             assert.equal(set1.ownerRevisionForRepository(jsc), '192736');
1026             assert.equal(set0.requiresBuildForRepository(jsc), true);
1027             assert.equal(set1.requiresBuildForRepository(jsc), true);
1028             assert(!set0.equals(set1));
1029         });
1030     });
1031
1032     it('should still work even if components with same name but one is owned, one is not', () => {
1033         let taskId;
1034         let webkit;
1035         let macos;
1036         let jsc;
1037         let ownedJSC;
1038         let insertedGroupId;
1039         let uploadedFile;
1040         return addTriggerableAndCreateTask('some task').then((id) => taskId = id).then(() => {
1041             return MockData.addAnotherTriggerable(TestServer.database());
1042         }).then(() => {
1043             webkit = Repository.all().filter((repository) => repository.name() == 'WebKit')[0];
1044             macos = Repository.all().filter((repository) => repository.name() == 'macOS')[0];
1045             jsc = Repository.all().filter((repository) => repository.name() == 'JavaScriptCore' && !repository.ownerId())[0];
1046             ownedJSC = Repository.all().filter((repository) => repository.name() == 'JavaScriptCore' && repository.ownerId())[0];
1047             return TemporaryFile.makeTemporaryFile('some.dat', 'some content');
1048         }).then((stream) => {
1049             return PrivilegedAPI.sendRequest('upload-file', {newFile: stream}, {useFormData: true});
1050         }).then((response) => {
1051             const rawFile = response['uploadedFile'];
1052             uploadedFile = UploadedFile.ensureSingleton(rawFile.id, rawFile);
1053             const revisionSets = [{[jsc.id()]: {revision: 'jsc-6161'}, [webkit.id()]: {revision: '191622'}, [macos.id()]: {revision: '15A284'}, [ownedJSC.id()]: {revision: 'owned-jsc-6161', ownerRevision: '191622'}},
1054                 {[jsc.id()]: {revision: 'jsc-9191'}, [webkit.id()]: {revision: '192736', patch: uploadedFile.id()}, [macos.id()]: {revision: '15A284'}, [ownedJSC.id()]: {revision: 'owned-jsc-9191', ownerRevision: '192736'}}];
1055             return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, repetitionCount: 2, revisionSets});
1056         }).then((content) => {
1057             insertedGroupId = content['testGroupId'];
1058             return TestGroup.fetchForTask(taskId, true);
1059         }).then((testGroups) => {
1060             assert.equal(testGroups.length, 1);
1061             const group = testGroups[0];
1062             assert.equal(group.id(), insertedGroupId);
1063             assert.equal(group.repetitionCount(), 2);
1064             assert.equal(group.test(), Test.findById(MockData.someTestId()));
1065             assert.ok(!group.needsNotification());
1066             assert.equal(group.platform(), Platform.findById(MockData.somePlatformId()));
1067             const requests = group.buildRequests();
1068             assert.equal(requests.length, 6);
1069
1070             assert.equal(requests[0].isBuild(), true);
1071             assert.equal(requests[1].isBuild(), true);
1072             assert.equal(requests[2].isBuild(), false);
1073             assert.equal(requests[3].isBuild(), false);
1074             assert.equal(requests[4].isBuild(), false);
1075             assert.equal(requests[5].isBuild(), false);
1076
1077             assert.equal(requests[0].isTest(), false);
1078             assert.equal(requests[1].isTest(), false);
1079             assert.equal(requests[2].isTest(), true);
1080             assert.equal(requests[3].isTest(), true);
1081             assert.equal(requests[4].isTest(), true);
1082             assert.equal(requests[5].isTest(), true);
1083
1084             const set0 = requests[0].commitSet();
1085             const set1 = requests[1].commitSet();
1086             assert.equal(requests[2].commitSet(), set0);
1087             assert.equal(requests[3].commitSet(), set1);
1088             assert.equal(requests[4].commitSet(), set0);
1089             assert.equal(requests[5].commitSet(), set1);
1090             assert.deepEqual(Repository.sortByNamePreferringOnesWithURL(set0.repositories()), [jsc, ownedJSC, webkit, macos]);
1091             assert.deepEqual(set0.customRoots(), []);
1092             assert.deepEqual(Repository.sortByNamePreferringOnesWithURL(set1.repositories()), [jsc, ownedJSC, webkit, macos]);
1093             assert.deepEqual(set1.customRoots(), []);
1094
1095             assert.equal(set0.revisionForRepository(webkit), '191622');
1096             assert.equal(set1.revisionForRepository(webkit), '192736');
1097             assert.equal(set0.patchForRepository(webkit), null);
1098             assert.equal(set1.patchForRepository(webkit), uploadedFile);
1099             assert.equal(set0.ownerRevisionForRepository(webkit), null);
1100             assert.equal(set1.ownerRevisionForRepository(webkit), null);
1101             assert.equal(set0.requiresBuildForRepository(webkit), true);
1102             assert.equal(set1.requiresBuildForRepository(webkit), true);
1103
1104             assert.equal(set0.revisionForRepository(macos), '15A284');
1105             assert.equal(set0.revisionForRepository(macos), set1.revisionForRepository(macos));
1106             assert.equal(set0.commitForRepository(macos), set1.commitForRepository(macos));
1107             assert.equal(set0.patchForRepository(macos), null);
1108             assert.equal(set1.patchForRepository(macos), null);
1109             assert.equal(set0.ownerRevisionForRepository(macos), null);
1110             assert.equal(set1.ownerRevisionForRepository(macos), null);
1111             assert.equal(set0.requiresBuildForRepository(macos), false);
1112             assert.equal(set1.requiresBuildForRepository(macos), false);
1113
1114             assert.equal(set0.revisionForRepository(ownedJSC), 'owned-jsc-6161');
1115             assert.equal(set1.revisionForRepository(ownedJSC), 'owned-jsc-9191');
1116             assert.equal(set0.patchForRepository(ownedJSC), null);
1117             assert.equal(set1.patchForRepository(ownedJSC), null);
1118             assert.equal(set0.ownerRevisionForRepository(ownedJSC), '191622');
1119             assert.equal(set1.ownerRevisionForRepository(ownedJSC), '192736');
1120             assert.equal(set0.requiresBuildForRepository(ownedJSC), true);
1121             assert.equal(set1.requiresBuildForRepository(ownedJSC), true);
1122
1123             assert.equal(set0.revisionForRepository(jsc), 'jsc-6161');
1124             assert.equal(set1.revisionForRepository(jsc), 'jsc-9191');
1125             assert.equal(set0.patchForRepository(jsc), null);
1126             assert.equal(set1.patchForRepository(jsc), null);
1127             assert.equal(set0.ownerRevisionForRepository(jsc), null);
1128             assert.equal(set1.ownerRevisionForRepository(jsc), null);
1129             assert.equal(set0.requiresBuildForRepository(jsc), false);
1130             assert.equal(set1.requiresBuildForRepository(jsc), false);
1131             assert(!set0.equals(set1));
1132         });
1133     });
1134
1135     it('should not create a build request to build a patch when the commit set does not have a patch', () => {
1136         let taskId;
1137         let webkit;
1138         let macos;
1139         let insertedGroupId;
1140         let uploadedFile;
1141         return addTriggerableAndCreateTask('some task').then((id) => taskId = id).then(() => {
1142             webkit = Repository.all().filter((repository) => repository.name() == 'WebKit')[0];
1143             macos = Repository.all().filter((repository) => repository.name() == 'macOS')[0];
1144             return TemporaryFile.makeTemporaryFile('some.dat', 'some content');
1145         }).then((stream) => {
1146             return PrivilegedAPI.sendRequest('upload-file', {newFile: stream}, {useFormData: true});
1147         }).then((response) => {
1148             const rawFile = response['uploadedFile'];
1149             uploadedFile = UploadedFile.ensureSingleton(rawFile.id, rawFile);
1150             const revisionSets = [{[macos.id()]: {revision: '15A284'}},
1151                 {[webkit.id()]: {revision: '191622', patch: uploadedFile.id()}, [macos.id()]: {revision: '15A284'}}];
1152             return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, repetitionCount: 2, revisionSets});
1153         }).then((content) => {
1154             insertedGroupId = content['testGroupId'];
1155             return TestGroup.fetchForTask(taskId, true);
1156         }).then((testGroups) => {
1157             assert.equal(testGroups.length, 1);
1158             const group = testGroups[0];
1159             assert.equal(group.id(), insertedGroupId);
1160             assert.equal(group.repetitionCount(), 2);
1161             assert.ok(!group.needsNotification());
1162             assert.equal(group.test(), Test.findById(MockData.someTestId()));
1163             assert.equal(group.platform(), Platform.findById(MockData.somePlatformId()));
1164             const requests = group.buildRequests();
1165             assert.equal(requests.length, 5);
1166
1167             assert.equal(requests[0].isBuild(), true);
1168             assert.equal(requests[1].isBuild(), false);
1169             assert.equal(requests[2].isBuild(), false);
1170             assert.equal(requests[3].isBuild(), false);
1171             assert.equal(requests[4].isBuild(), false);
1172
1173             assert.equal(requests[0].isTest(), false);
1174             assert.equal(requests[1].isTest(), true);
1175             assert.equal(requests[2].isTest(), true);
1176             assert.equal(requests[3].isTest(), true);
1177             assert.equal(requests[4].isTest(), true);
1178
1179             const set0 = requests[0].commitSet();
1180             const set1 = requests[1].commitSet();
1181             assert.equal(requests[2].commitSet(), set0);
1182             assert.equal(requests[3].commitSet(), set1);
1183             assert.equal(requests[4].commitSet(), set0);
1184             assert.deepEqual(Repository.sortByNamePreferringOnesWithURL(set0.repositories()), [webkit, macos]);
1185             assert.deepEqual(set0.customRoots(), []);
1186             assert.deepEqual(Repository.sortByNamePreferringOnesWithURL(set1.repositories()), [macos]);
1187             assert.deepEqual(set1.customRoots(), []);
1188             assert.equal(set0.revisionForRepository(webkit), '191622');
1189             assert.equal(set1.revisionForRepository(webkit), null);
1190             assert.equal(set0.patchForRepository(webkit), uploadedFile);
1191             assert.equal(set0.revisionForRepository(macos), '15A284');
1192             assert.equal(set0.revisionForRepository(macos), set1.revisionForRepository(macos));
1193             assert.equal(set0.commitForRepository(macos), set1.commitForRepository(macos));
1194             assert.equal(set0.patchForRepository(macos), null);
1195             assert.equal(set1.patchForRepository(macos), null);
1196             assert(!set0.equals(set1));
1197         });
1198     });
1199
1200     it('should return "PatchNotAccepted" when a patch is specified for a repository that does not accept a patch', () => {
1201         let taskId;
1202         let webkit;
1203         let macos;
1204         let uploadedFile;
1205         return addTriggerableAndCreateTask('some task').then((id) => taskId = id).then(() => {
1206             webkit = Repository.all().filter((repository) => repository.name() == 'WebKit')[0];
1207             macos = Repository.all().filter((repository) => repository.name() == 'macOS')[0];
1208             return TemporaryFile.makeTemporaryFile('some.dat', 'some content');
1209         }).then((stream) => {
1210             return PrivilegedAPI.sendRequest('upload-file', {newFile: stream}, {useFormData: true});
1211         }).then((response) => {
1212             const rawFile = response['uploadedFile'];
1213             uploadedFile = UploadedFile.ensureSingleton(rawFile.id, rawFile);
1214             const revisionSets = [{[webkit.id()]: {revision: '191622'}, [macos.id()]: {revision: '15A284', patch: uploadedFile.id()}},
1215                 {[webkit.id()]: {revision: '192736'}, [macos.id()]: {revision: '15A284'}}];
1216             return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, repetitionCount: 2, revisionSets});
1217         }).then(() => {
1218             assert(false, 'should never be reached');
1219         }, (error) => {
1220             assert.equal(error, 'PatchNotAccepted');
1221         });
1222     });
1223
1224     it('should create a test group with an analysis task with needs-notification flag set', async () => {
1225         await addTriggerableAndCreateTask('some task');
1226         const webkit = Repository.all().filter((repository) => repository.name() == 'WebKit')[0];
1227         const revisionSets = [{[webkit.id()]: {revision: '191622'}}, {[webkit.id()]: {revision: '191623'}}];
1228         let result = await PrivilegedAPI.sendRequest('create-test-group',
1229             {name: 'test', taskName: 'other task', platform: MockData.somePlatformId(), test: MockData.someTestId(), needsNotification: true, revisionSets});
1230         const insertedGroupId = result['testGroupId'];
1231
1232         const [analysisTask, testGroups] = await Promise.all([AnalysisTask.fetchById(result['taskId']), TestGroup.fetchForTask(result['taskId'], true)]);
1233         assert.equal(analysisTask.name(), 'other task');
1234
1235         assert.equal(testGroups.length, 1);
1236         const group = testGroups[0];
1237         assert.equal(group.id(), insertedGroupId);
1238         assert.equal(group.repetitionCount(), 1);
1239         assert.ok(group.needsNotification());
1240         const requests = group.buildRequests();
1241         assert.equal(requests.length, 2);
1242
1243         const set0 = requests[0].commitSet();
1244         const set1 = requests[1].commitSet();
1245         assert.deepEqual(set0.repositories(), [webkit]);
1246         assert.deepEqual(set0.customRoots(), []);
1247         assert.deepEqual(set1.repositories(), [webkit]);
1248         assert.deepEqual(set1.customRoots(), []);
1249         assert.equal(set0.revisionForRepository(webkit), '191622');
1250         assert.equal(set1.revisionForRepository(webkit), '191623');
1251     });
1252
1253     it('should be able to create a test group with needs-notification flag unset', async () => {
1254         await addTriggerableAndCreateTask('some task');
1255         const webkit = Repository.all().filter((repository) => repository.name() == 'WebKit')[0];
1256         const revisionSets = [{[webkit.id()]: {revision: '191622'}}, {[webkit.id()]: {revision: '191623'}}];
1257         let result = await PrivilegedAPI.sendRequest('create-test-group',
1258             {name: 'test', taskName: 'other task', platform: MockData.somePlatformId(), test: MockData.someTestId(), needsNotification: false, revisionSets});
1259         const insertedGroupId = result['testGroupId'];
1260
1261         const [analysisTask, testGroups] = await Promise.all([AnalysisTask.fetchById(result['taskId']), TestGroup.fetchForTask(result['taskId'], true)]);
1262         assert.equal(analysisTask.name(), 'other task');
1263
1264         assert.equal(testGroups.length, 1);
1265         const group = testGroups[0];
1266         assert.equal(group.id(), insertedGroupId);
1267         assert.equal(group.repetitionCount(), 1);
1268         assert.ok(!group.needsNotification());
1269         const requests = group.buildRequests();
1270         assert.equal(requests.length, 2);
1271
1272         const set0 = requests[0].commitSet();
1273         const set1 = requests[1].commitSet();
1274         assert.deepEqual(set0.repositories(), [webkit]);
1275         assert.deepEqual(set0.customRoots(), []);
1276         assert.deepEqual(set1.repositories(), [webkit]);
1277         assert.deepEqual(set1.customRoots(), []);
1278         assert.equal(set0.revisionForRepository(webkit), '191622');
1279         assert.equal(set1.revisionForRepository(webkit), '191623');
1280     });
1281
1282     it('should create a custom test group for an existing custom analysis task', () => {
1283         let firstResult;
1284         let secondResult;
1285         let webkit;
1286         let test = MockData.someTestId();
1287         return addTriggerableAndCreateTask('some task').then(() => {
1288             webkit = Repository.all().filter((repository) => repository.name() == 'WebKit')[0];
1289             const revisionSets = [{[webkit.id()]: {revision: '191622'}}, {[webkit.id()]: {revision: '191623'}}];
1290             return PrivilegedAPI.sendRequest('create-test-group',
1291                 {name: 'test1', taskName: 'other task', platform: MockData.somePlatformId(), test, revisionSets});
1292         }).then((result) => {
1293             firstResult = result;
1294             const revisionSets = [{[webkit.id()]: {revision: '191622'}}, {[webkit.id()]: {revision: '192736'}}];
1295             return PrivilegedAPI.sendRequest('create-test-group',
1296                 {name: 'test2', task: result['taskId'], platform: MockData.otherPlatformId(), test, revisionSets, repetitionCount: 2});
1297         }).then((result) => {
1298             secondResult = result;
1299             assert.equal(firstResult['taskId'], secondResult['taskId']);
1300             return Promise.all([AnalysisTask.fetchById(result['taskId']), TestGroup.fetchForTask(result['taskId'], true)]);
1301         }).then((result) => {
1302             const [analysisTask, testGroups] = result;
1303
1304             assert.equal(analysisTask.name(), 'other task');
1305
1306             assert.equal(testGroups.length, 2);
1307             TestGroup.sortByName(testGroups);
1308
1309             assert.equal(testGroups[0].name(), 'test1');
1310             assert.equal(testGroups[0].repetitionCount(), 1);
1311             let requests = testGroups[0].buildRequests();
1312             assert.equal(requests.length, 2);
1313             let set0 = requests[0].commitSet();
1314             let set1 = requests[1].commitSet();
1315             assert.deepEqual(set0.repositories(), [webkit]);
1316             assert.deepEqual(set0.customRoots(), []);
1317             assert.deepEqual(set1.repositories(), [webkit]);
1318             assert.deepEqual(set1.customRoots(), []);
1319             assert.equal(set0.revisionForRepository(webkit), '191622');
1320             assert.equal(set1.revisionForRepository(webkit), '191623');
1321
1322             assert.equal(testGroups[1].name(), 'test2');
1323             assert.equal(testGroups[1].repetitionCount(), 2);
1324             requests = testGroups[1].buildRequests();
1325             assert.equal(requests.length, 4);
1326             set0 = requests[0].commitSet();
1327             set1 = requests[1].commitSet();
1328             assert.deepEqual(requests[2].commitSet(), set0);
1329             assert.deepEqual(requests[3].commitSet(), set1);
1330             assert.deepEqual(set0.repositories(), [webkit]);
1331             assert.deepEqual(set0.customRoots(), []);
1332             assert.deepEqual(set1.repositories(), [webkit]);
1333             assert.deepEqual(set1.customRoots(), []);
1334             assert.equal(set0.revisionForRepository(webkit), '191622');
1335             assert.equal(set1.revisionForRepository(webkit), '192736');
1336         });
1337     });
1338 });