Add tests for privileged-api/create-analysis-task and privileged-api/create-test...
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 15 Mar 2017 19:15:02 +0000 (19:15 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 15 Mar 2017 19:15:02 +0000 (19:15 +0000)
https://bugs.webkit.org/show_bug.cgi?id=169688

Rubber-stamped by Antti Koivisto.

Added tests for privileged-api/create-analysis-task and privileged-api/create-test-group, and fixed newly found bugs.

* public/privileged-api/create-analysis-task.php:
(main): Fixed the bug that we were not explicitly checking whether start_run and end_run were integers or not.
Also return InvalidTimeRange when start and end times are identical as that makes no sense for an analysis task.

* public/privileged-api/create-test-group.php:
(main): Fixed a bug that we were not explicitly checking task and repetitionCount to be an integer.
(ensure_commit_sets): Fixed the bug that the number of commit sets weren't checked.

* server-tests/privileged-api-create-analysis-task-tests.js: Added.
* server-tests/privileged-api-create-test-group-tests.js: Added.

* server-tests/resources/common-operations.js:
(prepareServerTest): Increase the timeout from 1s to 5s.

* server-tests/resources/mock-data.js:
(MockData.addMockData): Use a higher database ID of 20 for a mock build_slave to avoid a conflict with auto-generated IDs.

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

Websites/perf.webkit.org/ChangeLog
Websites/perf.webkit.org/public/privileged-api/create-analysis-task.php
Websites/perf.webkit.org/public/privileged-api/create-test-group.php
Websites/perf.webkit.org/server-tests/privileged-api-create-analysis-task-tests.js [new file with mode: 0644]
Websites/perf.webkit.org/server-tests/privileged-api-create-test-group-tests.js [new file with mode: 0644]
Websites/perf.webkit.org/server-tests/resources/common-operations.js
Websites/perf.webkit.org/server-tests/resources/mock-data.js

index ea68368..bf70a16 100644 (file)
@@ -1,5 +1,31 @@
 2017-03-15  Ryosuke Niwa  <rniwa@webkit.org>
 
+        Add tests for privileged-api/create-analysis-task and privileged-api/create-test-group
+        https://bugs.webkit.org/show_bug.cgi?id=169688
+
+        Rubber-stamped by Antti Koivisto.
+
+        Added tests for privileged-api/create-analysis-task and privileged-api/create-test-group, and fixed newly found bugs.
+
+        * public/privileged-api/create-analysis-task.php:
+        (main): Fixed the bug that we were not explicitly checking whether start_run and end_run were integers or not.
+        Also return InvalidTimeRange when start and end times are identical as that makes no sense for an analysis task.
+
+        * public/privileged-api/create-test-group.php:
+        (main): Fixed a bug that we were not explicitly checking task and repetitionCount to be an integer.
+        (ensure_commit_sets): Fixed the bug that the number of commit sets weren't checked. 
+
+        * server-tests/privileged-api-create-analysis-task-tests.js: Added.
+        * server-tests/privileged-api-create-test-group-tests.js: Added.
+
+        * server-tests/resources/common-operations.js:
+        (prepareServerTest): Increase the timeout from 1s to 5s.
+
+        * server-tests/resources/mock-data.js:
+        (MockData.addMockData): Use a higher database ID of 20 for a mock build_slave to avoid a conflict with auto-generated IDs.
+
+2017-03-15  Ryosuke Niwa  <rniwa@webkit.org>
+
         Make unit tests return a promise instead of manually calling done
         https://bugs.webkit.org/show_bug.cgi?id=169663
 
index 0060a09..5c0a75b 100644 (file)
@@ -8,26 +8,25 @@ function main() {
 
     $author = remote_user_name($data);
     $name = array_get($data, 'name');
-    $start_run_id = array_get($data, 'startRun');
-    $end_run_id = array_get($data, 'endRun');
 
     $segmentation_name = array_get($data, 'segmentationStrategy');
     $test_range_name = array_get($data, 'testRangeStrategy');
 
     if (!$name)
         exit_with_error('MissingName', array('name' => $name));
-    $range = array('startRunId' => $start_run_id, 'endRunId' => $end_run_id);
-    if (!$start_run_id || !$end_run_id)
-        exit_with_error('MissingRange', $range);
 
-    $start_run = ensure_row_by_id($db, 'test_runs', 'run', $start_run_id, 'InvalidStartRun', $range);
-    $end_run = ensure_row_by_id($db, 'test_runs', 'run', $end_run_id, 'InvalidEndRun', $range);
+    $range = validate_arguments($data, array('startRun' => 'int', 'endRun' => 'int'));
+
+    $start_run = ensure_row_by_id($db, 'test_runs', 'run', $range['startRun'], 'InvalidStartRun', $range);
+    $start_run_id = $start_run['run_id'];
+    $end_run = ensure_row_by_id($db, 'test_runs', 'run', $range['endRun'], 'InvalidEndRun', $range);
+    $end_run_id = $end_run['run_id'];
 
     $config = ensure_config_from_runs($db, $start_run, $end_run);
 
     $start_run_time = time_for_run($db, $start_run_id);
     $end_run_time = time_for_run($db, $end_run_id);
-    if (!$start_run_time || !$end_run_time)
+    if (!$start_run_time || !$end_run_time || $start_run_time == $end_run_time)
         exit_with_error('InvalidTimeRange', array('startTime' => $start_run_time, 'endTime' => $end_run_time));
 
     $db->begin_transaction();
index e87e8bb..f68fc2e 100644 (file)
@@ -7,16 +7,23 @@ function main() {
     $data = ensure_privileged_api_data_and_token_or_slave($db);
     $author = remote_user_name($data);
 
-    $task_id = array_get($data, 'task');
-    $name = array_get($data, 'name');
+    $arguments = validate_arguments($data, array(
+        'name' => '/.+/',
+        'task' => 'int',
+        'repetitionCount' => 'int?',
+    ));
+    $name = $arguments['name'];
+    $task_id = $arguments['task'];
+    $repetition_count = $arguments['repetitionCount'];
     $commit_sets_info = array_get($data, 'commitSets');
-    $repetition_count = intval(array_get($data, 'repetitionCount', 1));
 
-    if (!$name)
-        exit_with_error('MissingName');
+    require_format('Task', $task_id, '/^\d+$/');
     if (!$commit_sets_info)
-        exit_with_error('MissingCommitSets');
-    if ($repetition_count < 1)
+        exit_with_error('InvalidCommitSets');
+
+    if ($repetition_count === null)
+        $repetition_count = 1;
+    else if ($repetition_count < 1)
         exit_with_error('InvalidRepetitionCount', array('repetitionCount' => $repetition_count));
 
     $task = $db->select_first_row('analysis_tasks', 'task', array('id' => $task_id));
@@ -80,6 +87,9 @@ function ensure_commit_sets($db, $commit_sets_info) {
         }
     }
 
+    if (count($commit_sets) < 2)
+        exit_with_error('InvalidCommitSets', array('commitSets' => $commit_sets_info));
+
     $commit_count_per_set = count($commit_sets[0]);
     foreach ($commit_sets as $commits) {
         if ($commit_count_per_set != count($commits))
diff --git a/Websites/perf.webkit.org/server-tests/privileged-api-create-analysis-task-tests.js b/Websites/perf.webkit.org/server-tests/privileged-api-create-analysis-task-tests.js
new file mode 100644 (file)
index 0000000..2d1873e
--- /dev/null
@@ -0,0 +1,255 @@
+'use strict';
+
+let assert = require('assert');
+
+let MockData = require('./resources/mock-data.js');
+let TestServer = require('./resources/test-server.js');
+const addBuilderForReport = require('./resources/common-operations.js').addBuilderForReport;
+const prepareServerTest = require('./resources/common-operations.js').prepareServerTest;
+
+const reportWithRevision = [{
+    "buildNumber": "124",
+    "buildTime": "2015-10-27T15:34:51",
+    "revisions": {
+        "WebKit": {
+            "revision": "191622",
+            "timestamp": '2015-10-27T11:36:56.878473Z',
+        },
+    },
+    "builderName": "someBuilder",
+    "builderPassword": "somePassword",
+    "platform": "some platform",
+    "tests": {
+        "Suite": {
+            "metrics": {
+                "Time": ["Arithmetic"],
+            },
+            "tests": {
+                "test1": {
+                    "metrics": {"Time": { "current": [11] }},
+                }
+            }
+        },
+    }}];
+
+const anotherReportWithRevision = [{
+    "buildNumber": "125",
+    "buildTime": "2015-10-27T17:27:41",
+    "revisions": {
+        "WebKit": {
+            "revision": "191623",
+            "timestamp": '2015-10-27T16:38:10.768995Z',
+        },
+    },
+    "builderName": "someBuilder",
+    "builderPassword": "somePassword",
+    "platform": "some platform",
+    "tests": {
+        "Suite": {
+            "metrics": {
+                "Time": ["Arithmetic"],
+            },
+            "tests": {
+                "test1": {
+                    "metrics": {"Time": { "current": [12] }},
+                }
+            }
+        },
+    }}];
+
+describe('/privileged-api/create-analysis-task', function () {
+    prepareServerTest(this);
+
+    it('should return "MissingName" on an empty request', () => {
+        return PrivilegedAPI.sendRequest('create-analysis-task', {}).then((content) => {
+            assert(false, 'should never be reached');
+        }, (response) => {
+            assert.equal(response['status'], 'MissingName');
+        });
+    });
+
+    it('should return "InvalidStartRun" when startRun is missing but endRun is set', () => {
+        return PrivilegedAPI.sendRequest('create-analysis-task', {name: 'hi', endRun: 1}).then((content) => {
+            assert(false, 'should never be reached');
+        }, (response) => {
+            assert.equal(response['status'], 'InvalidStartRun');
+        });
+    });
+
+    it('should return "InvalidEndRun" when endRun is missing but startRun is set', () => {
+        return PrivilegedAPI.sendRequest('create-analysis-task', {name: 'hi', startRun: 1}).then((content) => {
+            assert(false, 'should never be reached');
+        }, (response) => {
+            assert.equal(response['status'], 'InvalidEndRun');
+        });
+    });
+
+    it('should return "InvalidStartRun" when startRun is not a valid integer', () => {
+        return PrivilegedAPI.sendRequest('create-analysis-task', {name: 'hi', startRun: "foo", endRun: 1}).then((content) => {
+            assert(false, 'should never be reached');
+        }, (response) => {
+            assert.equal(response['status'], 'InvalidStartRun');
+        });
+    });
+
+    it('should return "InvalidEndRun" when endRun is not a valid integer', () => {
+        return PrivilegedAPI.sendRequest('create-analysis-task', {name: 'hi', startRun: 1, endRun: "foo"}).then((content) => {
+            assert(false, 'should never be reached');
+        }, (response) => {
+            assert.equal(response['status'], 'InvalidEndRun');
+        });
+    });
+
+    it('should return "InvalidStartRun" when startRun is invalid', () => {
+        return addBuilderForReport(reportWithRevision[0]).then(() => {
+            return TestServer.remoteAPI().postJSON('/api/report/', reportWithRevision);
+        }).then(() => {
+            return PrivilegedAPI.sendRequest('create-analysis-task', {name: 'hi', startRun: 100, endRun: 1}).then((content) => {
+                assert(false, 'should never be reached');
+            }, (response) => {
+                assert.equal(response['status'], 'InvalidStartRun');
+            });
+        });
+    });
+
+    it('should return "InvalidEndRun" when endRun is invalid', () => {
+        return addBuilderForReport(reportWithRevision[0]).then(() => {
+            return TestServer.remoteAPI().postJSON('/api/report/', reportWithRevision);
+        }).then(() => {
+            return PrivilegedAPI.sendRequest('create-analysis-task', {name: 'hi', startRun: 1, endRun: 100}).then((content) => {
+                assert(false, 'should never be reached');
+            }, (response) => {
+                assert.equal(response['status'], 'InvalidEndRun');
+            });
+        });
+    });
+
+    it('should return "InvalidTimeRange" when startRun and endRun are identical', () => {
+        return addBuilderForReport(reportWithRevision[0]).then(() => {
+            return TestServer.remoteAPI().postJSON('/api/report/', reportWithRevision);
+        }).then(() => {
+            return PrivilegedAPI.sendRequest('create-analysis-task', {name: 'hi', startRun: 1, endRun: 1}).then((content) => {
+                assert(false, 'should never be reached');
+            }, (response) => {
+                assert.equal(response['status'], 'InvalidTimeRange');
+            });
+        });
+    });
+
+    it('should return "RunConfigMismatch" when startRun and endRun come from a different test configurations', () => {
+        return addBuilderForReport(reportWithRevision[0]).then(() => {
+            return TestServer.remoteAPI().postJSON('/api/report/', reportWithRevision);
+        }).then(() => {
+            return PrivilegedAPI.sendRequest('create-analysis-task', {name: 'hi', startRun: 1, endRun: 2}).then((content) => {
+                assert(false, 'should never be reached');
+            }, (response) => {
+                assert.equal(response['status'], 'RunConfigMismatch');
+            });
+        });
+    });
+
+    it('should create an analysis task when name, startRun, and endRun are set properly', () => {
+        const db = TestServer.database();
+        return addBuilderForReport(reportWithRevision[0]).then(() => {
+            return TestServer.remoteAPI().postJSON('/api/report/', reportWithRevision);
+        }).then(() => {
+            return TestServer.remoteAPI().postJSON('/api/report/', anotherReportWithRevision);
+        }).then(() => {
+            return Manifest.fetch();
+        }).then(() => {
+            const test1 = Test.findByPath(['Suite', 'test1']);
+            const platform = Platform.findByName('some platform');
+            return db.selectFirstRow('test_configurations', {metric: test1.metrics()[0].id(), platform: platform.id()});
+        }).then((configRow) => {
+            return db.selectRows('test_runs', {config: configRow['id']});
+        }).then((testRuns) => {
+            assert.equal(testRuns.length, 2);
+            return PrivilegedAPI.sendRequest('create-analysis-task', {name: 'hi', startRun: testRuns[0]['id'], endRun: testRuns[1]['id']});
+        }).then((content) => {
+            return AnalysisTask.fetchById(content['taskId']);
+        }).then((task) => {
+            assert.equal(task.name(), 'hi');
+            assert(!task.hasResults());
+            assert(!task.hasPendingRequests());
+            assert.deepEqual(task.bugs(), []);
+            assert.deepEqual(task.causes(), []);
+            assert.deepEqual(task.fixes(), []);
+            assert.equal(task.changeType(), null);
+            assert.equal(task.platform().label(), 'some platform');
+            assert.equal(task.metric().test().label(), 'test1');
+        });
+    });
+
+    it('should return "DuplicateAnalysisTask" when there is already an analysis task for the specified range', () => {
+        const db = TestServer.database();
+        let startId;
+        let endId;
+        return addBuilderForReport(reportWithRevision[0]).then(() => {
+            return TestServer.remoteAPI().postJSON('/api/report/', reportWithRevision);
+        }).then(() => {
+            return TestServer.remoteAPI().postJSON('/api/report/', anotherReportWithRevision);
+        }).then(() => {
+            return Manifest.fetch();
+        }).then(() => {
+            const test1 = Test.findByPath(['Suite', 'test1']);
+            const platform = Platform.findByName('some platform');
+            return db.selectFirstRow('test_configurations', {metric: test1.metrics()[0].id(), platform: platform.id()});
+        }).then((configRow) => {
+            return db.selectRows('test_runs', {config: configRow['id']});
+        }).then((testRuns) => {
+            assert.equal(testRuns.length, 2);
+            startId = testRuns[0]['id'];
+            endId = testRuns[1]['id'];
+            return PrivilegedAPI.sendRequest('create-analysis-task', {name: 'hi', startRun: startId, endRun: endId});
+        }).then((content) => {
+            return PrivilegedAPI.sendRequest('create-analysis-task', {name: 'hi', startRun: startId, endRun: endId}).then(() => {
+                assert(false, 'should never be reached');
+            }, (response) => {
+                assert.equal(response['status'], 'DuplicateAnalysisTask');
+            });
+        }).then(() => {
+            return db.selectAll('analysis_tasks');
+        }).then((tasks) => {
+            assert.equal(tasks.length, 1);
+        });
+    });
+
+    it('should create an analysis task with analysis strategies when they are specified', () => {
+        const db = TestServer.database();
+        return addBuilderForReport(reportWithRevision[0]).then(() => {
+            return TestServer.remoteAPI().postJSON('/api/report/', reportWithRevision);
+        }).then(() => {
+            return TestServer.remoteAPI().postJSON('/api/report/', anotherReportWithRevision);
+        }).then(() => {
+            return Manifest.fetch();
+        }).then(() => {
+            const test1 = Test.findByPath(['Suite', 'test1']);
+            const platform = Platform.findByName('some platform');
+            return db.selectFirstRow('test_configurations', {metric: test1.metrics()[0].id(), platform: platform.id()});
+        }).then((configRow) => {
+            return db.selectRows('test_runs', {config: configRow['id']});
+        }).then((testRuns) => {
+            assert.equal(testRuns.length, 2);
+            return PrivilegedAPI.sendRequest('create-analysis-task', {
+                name: 'hi',
+                startRun: testRuns[0]['id'],
+                endRun: testRuns[1]['id'],
+                segmentationStrategy: "time series segmentation",
+                testRangeStrategy: "student's t-test"});
+        }).then(() => {
+            return Promise.all([db.selectFirstRow('analysis_tasks'), db.selectAll('analysis_strategies')]);
+        }).then((results) => {
+            const [taskRow, strategies] = results;
+            assert(taskRow['segmentation']);
+            assert(taskRow['test_range']);
+
+            const strategyIdMap = {};
+            for (let strategy of strategies)
+                strategyIdMap[strategy['id']] = strategy;
+
+            assert.equal(strategyIdMap[taskRow['segmentation']]['name'], "time series segmentation");
+            assert.equal(strategyIdMap[taskRow['test_range']]['name'], "student's t-test");
+        });
+    });
+
+});
diff --git a/Websites/perf.webkit.org/server-tests/privileged-api-create-test-group-tests.js b/Websites/perf.webkit.org/server-tests/privileged-api-create-test-group-tests.js
new file mode 100644 (file)
index 0000000..a77a6e1
--- /dev/null
@@ -0,0 +1,291 @@
+'use strict';
+
+let assert = require('assert');
+
+let MockData = require('./resources/mock-data.js');
+let TestServer = require('./resources/test-server.js');
+const addSlaveForReport = require('./resources/common-operations.js').addSlaveForReport;
+const prepareServerTest = require('./resources/common-operations.js').prepareServerTest;
+
+function createAnalysisTask(name)
+{
+    const reportWithRevision = [{
+        "buildNumber": "124",
+        "buildTime": "2015-10-27T15:34:51",
+        "revisions": {
+            "WebKit": {
+                "revision": "191622",
+                "timestamp": '2015-10-27T11:36:56.878473Z',
+            },
+            "macOS": {
+                "revision": "15A284",
+            }
+        },
+        "builderName": "someBuilder",
+        "slaveName": "someSlave",
+        "slavePassword": "somePassword",
+        "platform": "some platform",
+        "tests": {
+            "some test": {
+                "metrics": {
+                    "Time": ["Arithmetic"],
+                },
+                "tests": {
+                    "test1": {
+                        "metrics": {"Time": { "current": [11] }},
+                    }
+                }
+            },
+        }}];
+
+    const anotherReportWithRevision = [{
+        "buildNumber": "125",
+        "buildTime": "2015-10-27T17:27:41",
+        "revisions": {
+            "WebKit": {
+                "revision": "191623",
+                "timestamp": '2015-10-27T16:38:10.768995Z',
+            },
+            "macOS": {
+                "revision": "15A284",
+            }
+        },
+        "builderName": "someBuilder",
+        "slaveName": "someSlave",
+        "slavePassword": "somePassword",
+        "platform": "some platform",
+        "tests": {
+            "some test": {
+                "metrics": {
+                    "Time": ["Arithmetic"],
+                },
+                "tests": {
+                    "test1": {
+                        "metrics": {"Time": { "current": [12] }},
+                    }
+                }
+            },
+        }}];
+
+    const db = TestServer.database();
+    const remote = TestServer.remoteAPI();
+    return addSlaveForReport(reportWithRevision[0]).then(() => {
+        return remote.postJSON('/api/report/', reportWithRevision);
+    }).then(() => {
+        return remote.postJSON('/api/report/', anotherReportWithRevision);
+    }).then((result) => {
+        return Manifest.fetch();
+    }).then(() => {
+        const test = Test.findByPath(['some test', 'test1']);
+        const platform = Platform.findByName('some platform');
+        return db.selectFirstRow('test_configurations', {metric: test.metrics()[0].id(), platform: platform.id()});
+    }).then((configRow) => {
+        return db.selectRows('test_runs', {config: configRow['id']});
+    }).then((testRuns) => {
+        assert.equal(testRuns.length, 2);
+        return PrivilegedAPI.sendRequest('create-analysis-task', {
+            name: name,
+            startRun: testRuns[0]['id'],
+            endRun: testRuns[1]['id'],
+        });
+    }).then((content) => content['taskId']);
+}
+
+function addTriggerableAndCreateTask(name)
+{
+    const report = {
+        'slaveName': 'anotherSlave',
+        'slavePassword': 'anotherPassword',
+        'triggerable': 'build-webkit',
+        'configurations': [
+            {test: MockData.someTestId(), platform: MockData.somePlatformId()}
+        ],
+    };
+    return MockData.addMockData(TestServer.database()).then(() => {
+        return addSlaveForReport(report);
+    }).then(() => {
+        return TestServer.remoteAPI().postJSON('/api/update-triggerable/', report);
+    }).then(() => {
+        return createAnalysisTask(name);
+    });
+}
+
+describe('/privileged-api/create-test-group', function () {
+    prepareServerTest(this);
+
+    it('should return "InvalidName" on an empty request', () => {
+        return PrivilegedAPI.sendRequest('create-test-group', {}).then((content) => {
+            assert(false, 'should never be reached');
+        }, (response) => {
+            assert.equal(response['status'], 'InvalidName');
+        });
+    });
+
+    it('should return "InvalidTask" when task is not specified', () => {
+        return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', commitSets: [[1]]}).then((content) => {
+            assert(false, 'should never be reached');
+        }, (response) => {
+            assert.equal(response['status'], 'InvalidTask');
+        });
+    });
+
+    it('should return "InvalidTask" when task is not a valid integer', () => {
+        return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: 'foo', commitSets: [[1]]}).then((content) => {
+            assert(false, 'should never be reached');
+        }, (response) => {
+            assert.equal(response['status'], 'InvalidTask');
+        });
+    });
+
+    it('should return "InvalidCommitSets" when commit sets are not specified', () => {
+        return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: 1, repetitionCount: 1}).then((content) => {
+            assert(false, 'should never be reached');
+        }, (response) => {
+            assert.equal(response['status'], 'InvalidCommitSets');
+        });
+    });
+
+    it('should return "InvalidCommitSets" when commit sets is empty', () => {
+        return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: 1, repetitionCount: 1, commitSets: {}}).then((content) => {
+            assert(false, 'should never be reached');
+        }, (response) => {
+            assert.equal(response['status'], 'InvalidCommitSets');
+        });
+    });
+
+    it('should return "InvalidTask" when there is no matching task', () => {
+        return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: 1, repetitionCount: 1, commitSets: {'WebKit': []}}).then((content) => {
+            assert(false, 'should never be reached');
+        }, (response) => {
+            assert.equal(response['status'], 'InvalidTask');
+        });
+    });
+
+    it('should return "InvalidRepetitionCount" when repetitionCount is not a valid integer', () => {
+        return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: 1, repetitionCount: 'foo', commitSets: {'WebKit': []}}).then((content) => {
+            assert(false, 'should never be reached');
+        }, (response) => {
+            assert.equal(response['status'], 'InvalidRepetitionCount');
+        });
+    });
+
+    it('should return "InvalidRepetitionCount" when repetitionCount is a negative integer', () => {
+        return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: 1, repetitionCount: -5, commitSets: {'WebKit': []}}).then((content) => {
+            assert(false, 'should never be reached');
+        }, (response) => {
+            assert.equal(response['status'], 'InvalidRepetitionCount');
+        });
+    });
+
+    it('should return "InvalidTask" when there is no matching task', () => {
+        return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: 1, commitSets: {'WebKit': []}}).then((content) => {
+            assert(false, 'should never be reached');
+        }, (response) => {
+            assert.equal(response['status'], 'InvalidTask');
+        });
+    });
+
+    it('should return "TriggerableNotFoundForTask" when there is no matching triggerable', () => {
+        return createAnalysisTask('some task').then((taskId) => {
+            return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, commitSets: {'WebKit': []}}).then((content) => {
+                assert(false, 'should never be reached');
+            }, (response) => {
+                assert.equal(response['status'], 'TriggerableNotFoundForTask');
+            });
+        });
+    });
+
+    it('should return "InvalidCommitSets" when each repository specifies zero revisions', () => {
+        return addTriggerableAndCreateTask('some task').then((taskId) => {
+            return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, commitSets: {'WebKit': []}}).then((content) => {
+                assert(false, 'should never be reached');
+            }, (response) => {
+                assert.equal(response['status'], 'InvalidCommitSets');
+            });
+        });
+    });
+
+    it('should return "RepositoryNotFound" when commit sets contains an invalid repository', () => {
+        return addTriggerableAndCreateTask('some task').then((taskId) => {
+            return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, commitSets: {'Foo': []}}).then((content) => {
+                assert(false, 'should never be reached');
+            }, (response) => {
+                assert.equal(response['status'], 'RepositoryNotFound');
+            });
+        });
+    });
+
+    it('should return "RevisionNotFound" when commit sets contains an invalid revision', () => {
+        return addTriggerableAndCreateTask('some task').then((taskId) => {
+            return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, commitSets: {'WebKit': ['1']}}).then((content) => {
+                assert(false, 'should never be reached');
+            }, (response) => {
+                assert.equal(response['status'], 'RevisionNotFound');
+            });
+        });
+    });
+
+    it('should return "InvalidCommitSets" when commit sets contains an inconsistent number of revisions', () => {
+        return addTriggerableAndCreateTask('some task').then((taskId) => {
+            return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, commitSets: {'WebKit': ['191622', '191623'], 'macOS': ['15A284']}}).then((content) => {
+                assert(false, 'should never be reached');
+            }, (response) => {
+                assert.equal(response['status'], 'InvalidCommitSets');
+            });
+        });
+    });
+
+    it('should create a test group with the repetition count of one when repetitionCount is omitted', () => {
+        return addTriggerableAndCreateTask('some task').then((taskId) => {
+            let insertedGroupId;
+            return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, commitSets: {'WebKit': ['191622', '191623']}}).then((content) => {
+                insertedGroupId = content['testGroupId'];
+                return TestGroup.fetchByTask(taskId);
+            }).then((testGroups) => {
+                assert.equal(testGroups.length, 1);
+                const group = testGroups[0];
+                assert.equal(group.id(), insertedGroupId);
+                assert.equal(group.repetitionCount(), 1);
+                const requests = group.buildRequests();
+                assert.equal(requests.length, 2);
+                const webkit = Repository.all().filter((repository) => repository.name() == 'WebKit')[0];
+                assert.deepEqual(requests[0].commitSet().repositories(), [webkit]);
+                assert.deepEqual(requests[1].commitSet().repositories(), [webkit]);
+                assert.equal(requests[0].commitSet().revisionForRepository(webkit), '191622');
+                assert.equal(requests[1].commitSet().revisionForRepository(webkit), '191623');
+            });
+        });
+    });
+
+    it('should create a test group with the repetition count of two with two repositories', () => {
+        return addTriggerableAndCreateTask('some task').then((taskId) => {
+            let insertedGroupId;
+            return PrivilegedAPI.sendRequest('create-test-group', {name: 'test', task: taskId, repetitionCount: 2,
+                commitSets: {'WebKit': ['191622', '191623'], 'macOS': ['15A284', '15A284']}}).then((content) => {
+                insertedGroupId = content['testGroupId'];
+                return TestGroup.fetchByTask(taskId);
+            }).then((testGroups) => {
+                assert.equal(testGroups.length, 1);
+                const group = testGroups[0];
+                assert.equal(group.id(), insertedGroupId);
+                assert.equal(group.repetitionCount(), 2);
+                const requests = group.buildRequests();
+                assert.equal(requests.length, 4);
+                const webkit = Repository.all().filter((repository) => repository.name() == 'WebKit')[0];
+                const macos = Repository.all().filter((repository) => repository.name() == 'macOS')[0];
+                const set1 = requests[0].commitSet();
+                const set2 = requests[1].commitSet();
+                assert.equal(requests[2].commitSet(), set1);
+                assert.equal(requests[3].commitSet(), set2);
+                assert.deepEqual(Repository.sortByNamePreferringOnesWithURL(set1.repositories()), [webkit, macos]);
+                assert.deepEqual(Repository.sortByNamePreferringOnesWithURL(set2.repositories()), [webkit, macos]);
+                assert.equal(set1.revisionForRepository(webkit), '191622');
+                assert.equal(set1.revisionForRepository(macos), '15A284');
+                assert.equal(set2.revisionForRepository(webkit), '191623');
+                assert.equal(set2.revisionForRepository(macos), '15A284');
+                assert.equal(set1.commitForRepository(macos), set2.commitForRepository(macos));
+            });
+        });
+    });
+
+});
index 41c579e..7c1f34d 100644 (file)
@@ -19,7 +19,7 @@ function addSlaveForReport(report)
 
 function prepareServerTest(test)
 {
-    test.timeout(1000);
+    test.timeout(5000);
     TestServer.inject();
 
     beforeEach(function () {
index 7491ca8..0e24f0f 100644 (file)
@@ -25,7 +25,7 @@ MockData = {
             statusList = ['pending', 'pending', 'pending', 'pending'];
         return Promise.all([
             db.insert('build_triggerables', {id: 1, name: 'build-webkit'}),
-            db.insert('build_slaves', {id: 2, name: 'sync-slave', password_hash: crypto.createHash('sha256').update('password').digest('hex')}),
+            db.insert('build_slaves', {id: 20, name: 'sync-slave', password_hash: crypto.createHash('sha256').update('password').digest('hex')}),
             db.insert('repositories', {id: 9, name: 'OS X'}),
             db.insert('repositories', {id: 11, name: 'WebKit'}),
             db.insert('commits', {id: 87832, repository: 9, revision: '10.11 15A284'}),