+2016-04-07 Ryosuke Niwa <rniwa@webkit.org>
+
+ Migrate legacy perf dashboard tests to mocha.js based tests
+ https://bugs.webkit.org/show_bug.cgi?id=156335
+
+ Reviewed by Chris Dumez.
+
+ Migrated all legacy run-tests.js tests to mocha.js based tests. Since the new harness uses Promise
+ for most of asynchronous operations, refactored the tests to use Promises as well, and added more
+ assertions where appropriate.
+
+ Also consolidated common helper functions into server-tests/resources/common-operations.js.
+ Unfortunately there were multiple inconsistent implementations of addBuilder/addSlave. Some were
+ taking an array of reports while others were taking a single report. New shared implementation in
+ common-operations.js now takes a single report.
+
+ Also decreased the timeout in most tests from 10s to 1s so that tests fail early when they timeout.
+ Most of tests are passing under 100ms on my computer so 1s should be plenty still.
+
+ * run-tests.js: Removed.
+ * server-tests/admin-platforms-tests.js: Moved from tests/admin-platforms.js.
+ (reportsForDifferentPlatforms):
+ * server-tests/admin-reprocess-report-tests.js: Moved from tests/admin-reprocess-report.js.
+ (.addBuilder): Moved to common-operations.js.
+ * server-tests/api-build-requests-tests.js:
+ * server-tests/api-manifest.js: Use MockData.resetV3Models() instead of manually clearing maps.
+ * server-tests/api-measurement-set-tests.js: Moved from tests/api-measurement-set.js.
+ (.queryPlatformAndMetric):
+ (.format):
+ * server-tests/api-report-commits-tests.js: Moved from tests/api-report-commits.js.
+ * server-tests/api-report-tests.js: Moved from tests/api-report.js.
+ (.emptyReport):
+ (.emptySlaveReport):
+ (.reportWithSameSubtestName):
+ * server-tests/resources/common-operations.js: Added.
+ (addBuilderForReport): Extracted from tests.
+ (addSlaveForReport): Ditto.
+ (connectToDatabaseInEveryTest): Added.
+ (submitReport): Extracted from admin-platforms-tests.js.
+ * server-tests/resources/test-server.js:
+ (TestServer): Make TestServer a singleton since it doesn't make any sense for each module to start
+ its own Apache instance (that would certainly will fail).
+ * server-tests/tools-buildbot-triggerable-tests.js:
+ * tests: Removed.
+ * tools/js/database.js:
+ (Database.prototype.selectAll): Added.
+ (Database.prototype.selectFirstRow): Added.
+ (Database.prototype.selectRows): Added. Dynamically construct a query string based on arguments.
+
2016-04-05 Ryosuke Niwa <rniwa@webkit.org>
New buildbot syncing scripts that supports multiple builders and slaves
-#!/usr/local/bin/node
-
-var assert = require('assert');
-var crypto = require('crypto');
-var fs = require('fs');
-var http = require('http');
-var path = require('path');
-var vm = require('vm');
-
-function connect(keepAlive) {
- var pg = require('pg');
- var database = config('database');
- var connectionString = 'tcp://' + database.username + ':' + database.password + '@' + database.host + ':' + database.port
- + '/' + database.name;
-
- var client = new pg.Client(connectionString);
- if (!keepAlive) {
- client.on('drain', function () {
- client.end();
- client = undefined;
- });
- }
- client.connect();
-
- return client;
-}
-
-function pathToDatabseSQL(relativePath) {
- return path.resolve(__dirname, 'init-database.sql');
-}
-
-function pathToTests(testName) {
- return testName ? path.resolve(__dirname, 'tests', testName) : path.resolve(__dirname, 'tests');
-}
-
-var configurationJSON = require('./config.json');
-function config(key) {
- return configurationJSON[key];
-}
-
-function TaskQueue() {
- var queue = [];
- var numberOfRemainingTasks = 0;
- var emptyQueueCallback;
-
- function startTasksInQueue() {
- if (!queue.length)
- return emptyQueueCallback();
-
- var swappedQueue = queue;
- queue = [];
-
- // Increase the counter before the loop in the case taskCallback is called synchronously.
- numberOfRemainingTasks += swappedQueue.length;
- for (var i = 0; i < swappedQueue.length; ++i)
- swappedQueue[i](null, taskCallback);
- }
-
- function taskCallback(error) {
- // FIXME: Handle error.
- console.assert(numberOfRemainingTasks > 0);
- numberOfRemainingTasks--;
- if (!numberOfRemainingTasks)
- setTimeout(startTasksInQueue, 0);
- }
-
- this.addTask = function (task) { queue.push(task); }
- this.start = function (callback) {
- emptyQueueCallback = callback;
- startTasksInQueue();
- }
-}
-
-function SerializedTaskQueue() {
- var queue = [];
-
- function executeNextTask(error) {
- // FIXME: Handle error.
- var callback = queue.pop();
- setTimeout(function () { callback(null, executeNextTask); }, 0);
- }
-
- this.addTask = function (task) { queue.push(task); }
- this.start = function (callback) {
- queue.push(callback);
- queue.reverse();
- executeNextTask();
- }
-}
-
-function main(argv) {
- var client = connect(true);
- var filter = argv[2];
- confirmUserWantsDatabaseToBeInitializedIfNeeded(client, function (error, shouldContinue) {
- if (error)
- console.error(error);
-
- if (error || !shouldContinue) {
- client.end();
- process.exit(1);
- return;
- }
-
- initializeDatabase(client, function (error) {
- if (error) {
- console.error('Failed to initialize the database', error);
- client.end();
- process.exit(1);
- }
-
- var testCaseQueue = new SerializedTaskQueue();
- var testFileQueue = new SerializedTaskQueue();
- fs.readdirSync(pathToTests()).map(function (testFile) {
- if (!testFile.match(/.js$/) || (filter && testFile.indexOf(filter) != 0))
- return;
- testFileQueue.addTask(function (error, callback) {
- var testContent = fs.readFileSync(pathToTests(testFile), 'utf-8');
- var environment = new TestEnvironment(testCaseQueue);
- vm.runInNewContext(testContent, environment, pathToTests(testFile));
- callback();
- });
- });
- testFileQueue.start(function () {
- client.end();
- testCaseQueue.start(function () {
- console.log('DONE');
- });
- });
- });
- });
-}
-
-function confirmUserWantsDatabaseToBeInitializedIfNeeded(client, callback) {
- function fetchTableNames(error, callback) {
- if (error)
- return callback(error);
-
- client.query('SELECT table_name FROM information_schema.tables WHERE table_type = \'BASE TABLE\' and table_schema = \'public\'', function (error, result) {
- if (error)
- return callback(error);
- callback(null, result.rows.map(function (row) { return row['table_name']; }));
- });
- }
-
- function findNonEmptyTable(error, list, callback) {
- if (error || !list.length)
- return callback(error);
-
- var tableName = list.shift();
- client.query('SELECT COUNT(*) FROM ' + tableName + ' LIMIT 1', function (error, result) {
- if (error)
- return callback(error);
-
- if (result.rows[0]['count'])
- return callback(null, tableName);
-
- findNonEmptyTable(null, list, callback);
- });
- }
-
- fetchTableNames(null, function (error, tableNames) {
- if (error)
- return callback(error, false);
-
- findNonEmptyTable(null, tableNames, function (error, nonEmptyTable) {
- if (error)
- return callback(error, false);
-
- if (!nonEmptyTable)
- return callback(null, true);
-
- console.warn('Table ' + nonEmptyTable + ' is not empty but running tests will drop all tables.');
- askYesOrNoQuestion(null, 'Do you really want to continue?', callback);
- });
- });
-}
-
-function askYesOrNoQuestion(error, question, callback) {
- if (error)
- return callback(error);
-
- process.stdout.write(question + ' (y/n):');
- process.stdin.resume();
- process.stdin.setEncoding('utf-8');
- process.stdin.on('data', function (line) {
- line = line.trim();
- if (line === 'y') {
- process.stdin.pause();
- callback(null, true);
- } else if (line === 'n') {
- process.stdin.pause();
- callback(null, false);
- } else
- console.warn('Invalid input:', line);
- });
-}
-
-function initializeDatabase(client, callback) {
- var commaSeparatedSqlStatements = fs.readFileSync(pathToDatabseSQL(), "utf8");
-
- var firstError;
- var queue = new TaskQueue();
- commaSeparatedSqlStatements.split(/;\s*(?=CREATE|DROP)/).forEach(function (statement) {
- queue.addTask(function (error, callback) {
- client.query(statement, function (error) {
- if (error && !firstError)
- firstError = error;
- callback();
- });
- })
- });
-
- queue.start(function () { callback(firstError); });
-}
-
-var currentTestContext;
-function TestEnvironment(testCaseQueue) {
- var currentTestGroup;
-
- this.assert = assert;
- this.console = console;
-
- // describe("~", function () {
- // it("~", function () { assert(true); });
- // });
- this.describe = function (testGroup, callback) {
- currentTestGroup = testGroup;
- callback();
- }
-
- this.it = function (testCaseDescription, testCase) {
- testCaseQueue.addTask(function (error, callback) {
- currentTestContext = new TestContext(currentTestGroup, testCaseDescription, function () {
- currentTestContext = null;
- initializeDatabase(connect(), callback);
- });
- testCase();
- });
- }
-
- this.postJSON = function (path, content, callback) {
- sendHttpRequest(path, 'POST', 'application/json', JSON.stringify(content), function (error, response) {
- assert.ifError(error);
- callback(response);
- });
- }
-
- this.httpGet = function (path, callback) {
- sendHttpRequest(path, 'GET', null, '', function (error, response) {
- assert.ifError(error);
- callback(response);
- });
- }
-
- this.httpPost= function (path, content, callback) {
- var contentType = null;
- if (typeof(content) != "string") {
- contentType = 'application/x-www-form-urlencoded';
- var components = [];
- for (var key in content)
- components.push(key + '=' + escape(content[key]));
- content = components.join('&');
- }
- sendHttpRequest(path, 'POST', contentType, content, function (error, response) {
- assert.ifError(error);
- callback(response);
- });
- }
-
- this.queryAndFetchAll = function (query, parameters, callback) {
- var client = connect();
- client.query(query, parameters, function (error, result) {
- setTimeout(function () {
- assert.ifError(error);
- callback(result.rows);
- }, 0);
- });
- }
-
- this.sha256 = function (data) {
- var hash = crypto.createHash('sha256');
- hash.update(data);
- return hash.digest('hex');
- }
-
- this.config = config;
-
- this.notifyDone = function () { currentTestContext.done(); }
-}
-
-process.on('uncaughtException', function (error) {
- if (!currentTestContext)
- throw error;
- currentTestContext.logError('Uncaught exception', error);
- currentTestContext.done();
-});
-
-function sendHttpRequest(path, method, contentType, content, callback) {
- var options = config('testServer');
- options.path = path;
- options.method = method;
-
- var request = http.request(options, function (response) {
- var responseText = '';
- response.setEncoding('utf8');
- response.on('data', function (chunk) { responseText += chunk; });
- response.on('end', function () {
- setTimeout(function () {
- callback(null, {statusCode: response.statusCode, responseText: responseText});
- }, 0);
- });
- });
- request.on('error', callback);
- if (contentType)
- request.setHeader('Content-Type', contentType);
- if (content)
- request.write(content);
- request.end();
-}
-
-function TestContext(testGroup, testCase, callback) {
- var failed = false;
-
- this.description = function () {
- return testGroup + ' ' + testCase;
- }
- this.done = function () {
- if (!failed)
- console.log('PASS');
- callback();
- }
- this.logError = function (error, details) {
- failed = true;
- console.error(error, details);
- }
-
- process.stdout.write(this.description() + ': ');
-}
-
-main(process.argv);
--- /dev/null
+'use strict';
+
+const assert = require('assert');
+
+const TestServer = require('./resources/test-server.js');
+const connectToDatabaseInEveryTest = require('./resources/common-operations.js').connectToDatabaseInEveryTest;
+const submitReport = require('./resources/common-operations.js').submitReport;
+
+describe("/admin/platforms", function () {
+ this.timeout(1000);
+ TestServer.inject();
+ connectToDatabaseInEveryTest();
+
+ function reportsForDifferentPlatforms()
+ {
+ return [
+ {
+ "buildNumber": "3001",
+ "buildTime": "2013-02-28T09:01:47",
+ "builderName": "someBuilder",
+ "builderPassword": "somePassword",
+ "platform": "Mavericks",
+ "tests": {"test": { "metrics": {"FrameRate": { "current": [[1, 1, 1], [1, 1, 1]] } } } },
+ },
+ {
+ "buildNumber": "3001",
+ "buildTime": "2013-02-28T10:12:03",
+ "builderName": "someBuilder",
+ "builderPassword": "somePassword",
+ "platform": "Mountain Lion",
+ "tests": {"test": { "metrics": {"FrameRate": { "current": [[2, 2, 2], [2, 2, 2]] }, "Combined": { "current": [[3, 3, 3], [3, 3, 3]] }} } },
+ },
+ {
+ "buildNumber": "3003",
+ "buildTime": "2013-02-28T12:56:26",
+ "builderName": "someBuilder",
+ "builderPassword": "somePassword",
+ "platform": "Trunk Mountain Lion",
+ "tests": {"test": { "metrics": {"FrameRate": { "current": [[4, 4, 4], [4, 4, 4]] } } } }
+ }];
+ }
+
+ it("should delete the platform that got merged into another one", function (done) {
+ const db = TestServer.database();
+ let oldPlatforms;
+ submitReport(reportsForDifferentPlatforms()).then(function () {
+ return db.selectAll('platforms', 'name');
+ }).then(function (platforms) {
+ oldPlatforms = platforms;
+ assert.equal(oldPlatforms.length, 3);
+ assert.equal(oldPlatforms[0]['name'], 'Mavericks');
+ assert.equal(oldPlatforms[1]['name'], 'Mountain Lion');
+ assert.equal(oldPlatforms[2]['name'], 'Trunk Mountain Lion');
+ }).then(function () {
+ return TestServer.remoteAPI().postFormUrlencodedData('/admin/platforms.php',
+ {'action': 'merge', 'id': oldPlatforms[1]['id'], 'destination': oldPlatforms[2]['id']});
+ }).then(function () {
+ return db.selectAll('platforms');
+ }).then(function (newPlatforms) {
+ assert.equal(newPlatforms.length, 2);
+ assert.deepEqual(newPlatforms[0], oldPlatforms[0]);
+ assert.deepEqual(newPlatforms[1], oldPlatforms[2]);
+ done();
+ }).catch(done);
+ });
+
+ it("should move test runs from the merged platform to the destination platform", function (done) {
+ let oldTestRuns;
+ const queryForRuns = 'SELECT * FROM test_runs, test_configurations, platforms WHERE run_config = config_id AND config_platform = platform_id ORDER by run_mean_cache';
+ const db = TestServer.database();
+ submitReport(reportsForDifferentPlatforms()).then(function () {
+ return db.query(queryForRuns);
+ }).then(function (result) {
+ oldTestRuns = result.rows;
+ assert.equal(oldTestRuns.length, 4);
+ assert.equal(oldTestRuns[0]['platform_name'], 'Mavericks');
+ assert.equal(oldTestRuns[0]['run_sum_cache'], 6);
+ assert.equal(oldTestRuns[1]['platform_name'], 'Mountain Lion');
+ assert.equal(oldTestRuns[1]['run_sum_cache'], 12);
+ assert.equal(oldTestRuns[2]['platform_name'], 'Mountain Lion');
+ assert.equal(oldTestRuns[2]['run_sum_cache'], 18);
+ assert.equal(oldTestRuns[3]['platform_name'], 'Trunk Mountain Lion');
+ assert.equal(oldTestRuns[3]['run_sum_cache'], 24);
+ }).then(function () {
+ return TestServer.remoteAPI().postFormUrlencodedData('/admin/platforms.php',
+ {'action': 'merge', 'id': oldTestRuns[1]['platform_id'], 'destination': oldTestRuns[3]['platform_id']});
+ }).then(function () {
+ return db.query(queryForRuns);
+ }).then(function (result) {
+ const newTestRuns = result.rows;
+ assert.equal(newTestRuns.length, 4);
+ assert.equal(newTestRuns[0]['run_id'], oldTestRuns[0]['run_id']);
+ assert.equal(newTestRuns[0]['platform_name'], 'Mavericks');
+ assert.equal(newTestRuns[0]['run_sum_cache'], 6);
+ assert.equal(newTestRuns[1]['run_id'], oldTestRuns[1]['run_id']);
+ assert.equal(newTestRuns[1]['platform_name'], 'Trunk Mountain Lion');
+ assert.equal(newTestRuns[1]['run_sum_cache'], 12);
+ assert.equal(newTestRuns[2]['run_id'], oldTestRuns[2]['run_id']);
+ assert.equal(newTestRuns[2]['platform_name'], 'Trunk Mountain Lion');
+ assert.equal(newTestRuns[2]['run_sum_cache'], 18);
+ assert.equal(newTestRuns[3]['run_id'], oldTestRuns[3]['run_id']);
+ assert.equal(newTestRuns[3]['platform_name'], 'Trunk Mountain Lion');
+ assert.equal(newTestRuns[3]['run_sum_cache'], 24);
+ assert.equal(newTestRuns[1]['run_config'], newTestRuns[3]['run_config']);
+ done();
+ }).catch(done);
+ });
+
+ it("should move test configurations from the merged platform to the destination platform", function (done) {
+ let oldConfigs;
+ const reports = reportsForDifferentPlatforms();
+ reports[0]['tests'] = {"test": { "metrics": {"FrameRate": { "baseline": [[1, 1, 1], [1, 1, 1]] } } } };
+ const queryForConfig = 'SELECT * from test_configurations, platforms, test_metrics'
+ + ' where config_platform = platform_id and config_metric = metric_id and platform_name in ($1, $2) order by config_id';
+ const db = TestServer.database();
+ submitReport(reports).then(function () {
+ return db.query(queryForConfig, [reports[0]['platform'], reports[2]['platform']]);
+ }).then(function (result) {
+ oldConfigs = result.rows;
+ assert.equal(oldConfigs.length, 2);
+ assert.equal(oldConfigs[0]['platform_name'], reports[0]['platform']);
+ assert.equal(oldConfigs[0]['metric_name'], 'FrameRate');
+ assert.equal(oldConfigs[0]['config_type'], 'baseline');
+ assert.equal(oldConfigs[1]['platform_name'], reports[2]['platform']);
+ assert.equal(oldConfigs[1]['metric_name'], 'FrameRate');
+ assert.equal(oldConfigs[1]['config_type'], 'current');
+ }).then(function () {
+ return TestServer.remoteAPI().postFormUrlencodedData('/admin/platforms.php',
+ {'action': 'merge', 'id': oldConfigs[0]['platform_id'], 'destination': oldConfigs[1]['platform_id']});
+ }).then(function () {
+ return db.query(queryForConfig, [reports[0]['platform'], reports[2]['platform']]);
+ }).then(function (result) {
+ const newConfigs = result.rows;
+ assert.equal(newConfigs.length, 2);
+ assert.equal(newConfigs[0]['platform_name'], reports[2]['platform']);
+ assert.equal(newConfigs[0]['metric_name'], 'FrameRate');
+ assert.equal(newConfigs[0]['config_type'], 'baseline');
+ assert.equal(newConfigs[1]['platform_name'], reports[2]['platform']);
+ assert.equal(newConfigs[1]['metric_name'], 'FrameRate');
+ assert.equal(newConfigs[1]['config_type'], 'current');
+ done();
+ }).catch(done);
+ });
+
+});
--- /dev/null
+'use strict';
+
+const assert = require('assert');
+
+const TestServer = require('./resources/test-server.js');
+const addBuilderForReport = require('./resources/common-operations.js').addBuilderForReport;
+const connectToDatabaseInEveryTest = require('./resources/common-operations.js').connectToDatabaseInEveryTest;
+
+describe("/admin/reprocess-report", function () {
+ this.timeout(1000);
+ TestServer.inject();
+ connectToDatabaseInEveryTest();
+
+ const simpleReport = [{
+ "buildNumber": "1986",
+ "buildTime": "2013-02-28T10:12:03",
+ "builderName": "someBuilder",
+ "builderPassword": "somePassword",
+ "platform": "Mountain Lion",
+ "tests": {
+ "test": {
+ "metrics": {"FrameRate": { "current": [[1, 2, 3], [4, 5, 6]] }}
+ },
+ },
+ }];
+
+ it("should add build", function (done) {
+ let db = TestServer.database();
+ let reportId;
+ addBuilderForReport(simpleReport[0]).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report/', simpleReport);
+ }).then(function (response) {
+ assert.equal(response['status'], 'OK');
+ return Promise.all([db.selectAll('builds'), db.selectAll('reports')]);
+ }).then(function (result) {
+ const builds = result[0];
+ const reports = result[1];
+ assert.equal(builds.length, 1);
+ assert.equal(builds[0]['number'], 1986);
+ assert.equal(reports.length, 1);
+ reportId = reports[0]['id'];
+ assert.equal(reports[0]['build_number'], 1986);
+ return db.query('UPDATE reports SET report_build = NULL; DELETE FROM builds');
+ }).then(function () {
+ return db.selectAll('builds');
+ }).then(function (builds) {
+ assert.equal(builds.length, 0);
+ return TestServer.remoteAPI().getJSONWithStatus(`/admin/reprocess-report?report=${reportId}`);
+ }).then(function () {
+ return db.selectAll('builds');
+ }).then(function (builds) {
+ assert.equal(builds.length, 1);
+ assert.equal(builds[0]['number'], 1986);
+ done();
+ }).catch(done);
+ });
+
+ it("should not duplicate the reprocessed report", function (done) {
+ let db = TestServer.database();
+ let originalReprot;
+ addBuilderForReport(simpleReport[0]).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report/', simpleReport);
+ }).then(function (response) {
+ assert.equal(response['status'], 'OK');
+ return db.selectAll('reports');
+ }).then(function (reports) {
+ assert.equal(reports.length, 1);
+ originalReprot = reports[0];
+ return db.query('UPDATE reports SET report_build = NULL; DELETE FROM builds');
+ }).then(function () {
+ return TestServer.remoteAPI().getJSONWithStatus(`/admin/reprocess-report?report=${originalReprot['id']}`);
+ }).then(function () {
+ return db.selectAll('reports');
+ }).then(function (reports) {
+ assert.equal(reports.length, 1);
+ const newPort = reports[0];
+ originalReprot['committed_at'] = null;
+ newPort['committed_at'] = null;
+ assert.notEqual(originalReprot['build'], newPort['build']);
+ originalReprot['build'] = null;
+ newPort['build'] = null;
+ assert.deepEqual(originalReprot, newPort);
+ done();
+ }).catch(done);
+ });
+});
let TestServer = require('./resources/test-server.js');
describe('/api/build-requests', function () {
- this.timeout(10000);
+ this.timeout(1000);
TestServer.inject();
beforeEach(function () {
let TestServer = require('./resources/test-server.js');
describe('/api/manifest', function () {
- this.timeout(10000);
+ this.timeout(1000);
TestServer.inject();
beforeEach(function () {
- Builder.clearStaticMap();
- BugTracker.clearStaticMap();
- Test.clearStaticMap();
- Metric.clearStaticMap();
- Platform.clearStaticMap();
- Repository.clearStaticMap();
+ MockData.resetV3Models();
});
it("should generate an empty manifest when database is empty", function (done) {
--- /dev/null
+'use strict';
+
+const assert = require('assert');
+const crypto = require('crypto');
+
+const TestServer = require('./resources/test-server.js');
+const addBuilderForReport = require('./resources/common-operations.js').addBuilderForReport;
+const connectToDatabaseInEveryTest = require('./resources/common-operations.js').connectToDatabaseInEveryTest;
+
+describe("/api/measurement-set", function () {
+ this.timeout(1000);
+ TestServer.inject();
+ connectToDatabaseInEveryTest();
+
+ function queryPlatformAndMetric(platformName, metricName)
+ {
+ const db = TestServer.database();
+ return Promise.all([
+ db.selectFirstRow('platforms', {name: 'Mountain Lion'}),
+ db.selectFirstRow('test_metrics', {name: 'Time'}),
+ ]).then(function (result) {
+ return {platformId: result[0]['id'], metricId: result[1]['id']};
+ });
+ }
+
+ function format(formatMap, row)
+ {
+ var result = {};
+ for (var i = 0; i < formatMap.length; i++) {
+ var key = formatMap[i];
+ if (key == 'id' || key == 'build' || key == 'builder')
+ continue;
+ result[key] = row[i];
+ }
+ return result;
+ }
+
+ let clusterStart = TestServer.testConfig().clusterStart;
+ clusterStart = +Date.UTC(clusterStart[0], clusterStart[1] - 1, clusterStart[2], clusterStart[3], clusterStart[4]);
+
+ let clusterSize = TestServer.testConfig().clusterSize;
+ const DAY = 24 * 3600 * 1000;
+ const YEAR = 365.24 * DAY;
+ const MONTH = 30 * DAY;
+ clusterSize = clusterSize[0] * YEAR + clusterSize[1] * MONTH + clusterSize[2] * DAY;
+
+ function clusterTime(index) { return new Date(clusterStart + clusterSize * index); }
+
+ const reportWithBuildTime = [{
+ "buildNumber": "123",
+ "buildTime": clusterTime(7.8).toISOString(),
+ "builderName": "someBuilder",
+ "builderPassword": "somePassword",
+ "platform": "Mountain Lion",
+ "tests": {
+ "Suite": {
+ "tests": {
+ "test1": {
+ "metrics": {"Time": { "current": [1, 2, 3, 4, 5] }}
+ },
+ }
+ },
+ }}];
+ reportWithBuildTime.startTime = +clusterTime(7);
+
+ const reportWithRevision = [{
+ "buildNumber": "124",
+ "buildTime": "2013-02-28T15:34:51",
+ "revisions": {
+ "WebKit": {
+ "revision": "144000",
+ "timestamp": clusterTime(10.3).toISOString(),
+ },
+ },
+ "builderName": "someBuilder",
+ "builderPassword": "somePassword",
+ "platform": "Mountain Lion",
+ "tests": {
+ "Suite": {
+ "tests": {
+ "test1": {
+ "metrics": {"Time": { "current": [11, 12, 13, 14, 15] }}
+ }
+ }
+ },
+ }}];
+
+ const reportWithNewRevision = [{
+ "buildNumber": "125",
+ "buildTime": "2013-02-28T21:45:17",
+ "revisions": {
+ "WebKit": {
+ "revision": "160609",
+ "timestamp": clusterTime(12.1).toISOString()
+ },
+ },
+ "builderName": "someBuilder",
+ "builderPassword": "somePassword",
+ "platform": "Mountain Lion",
+ "tests": {
+ "Suite": {
+ "tests": {
+ "test1": {
+ "metrics": {"Time": { "current": [16, 17, 18, 19, 20] }}
+ }
+ }
+ },
+ }}];
+
+ const reportWithAncentRevision = [{
+ "buildNumber": "126",
+ "buildTime": "2013-02-28T23:07:25",
+ "revisions": {
+ "WebKit": {
+ "revision": "137793",
+ "timestamp": clusterTime(1.8).toISOString()
+ },
+ },
+ "builderName": "someBuilder",
+ "builderPassword": "somePassword",
+ "platform": "Mountain Lion",
+ "tests": {
+ "Suite": {
+ "tests": {
+ "test1": {
+ "metrics": {"Time": { "current": [21, 22, 23, 24, 25] }}
+ }
+ }
+ },
+ }}];
+
+ it("should reject when platform ID is missing", function (done) {
+ addBuilderForReport(reportWithBuildTime[0]).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report/', reportWithBuildTime);
+ }).then(function (response) {
+ assert.equal(response['status'], 'OK');
+ return queryPlatformAndMetric('Mountain Lion', 'Time');
+ }).then(function (result) {
+ return TestServer.remoteAPI().getJSON(`/api/measurement-set/?metric=${result.metricId}`);
+ }).then(function (response) {
+ assert.equal(response['status'], 'AmbiguousRequest');
+ done();
+ }).catch(done);
+ });
+
+ it("should reject when metric ID is missing", function (done) {
+ addBuilderForReport(reportWithBuildTime[0]).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report/', reportWithBuildTime);
+ }).then(function (response) {
+ assert.equal(response['status'], 'OK');
+ return queryPlatformAndMetric('Mountain Lion', 'Time');
+ }).then(function (result) {
+ return TestServer.remoteAPI().getJSON(`/api/measurement-set/?platform=${result.platformId}`);
+ }).then(function (response) {
+ assert.equal(response['status'], 'AmbiguousRequest');
+ done();
+ }).catch(done);
+ });
+
+ it("should reject an invalid platform name", function (done) {
+ addBuilderForReport(reportWithBuildTime[0]).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report/', reportWithBuildTime);
+ }).then(function (response) {
+ assert.equal(response['status'], 'OK');
+ return queryPlatformAndMetric('Mountain Lion', 'Time');
+ }).then(function (result) {
+ return TestServer.remoteAPI().getJSON(`/api/measurement-set/?platform=${result.platformId}a&metric=${result.metricId}`);
+ }).then(function (response) {
+ assert.equal(response['status'], 'InvalidPlatform');
+ done();
+ }).catch(done);
+ });
+
+ it("should reject an invalid metric name", function (done) {
+ addBuilderForReport(reportWithBuildTime[0]).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report/', reportWithBuildTime);
+ }).then(function (response) {
+ assert.equal(response['status'], 'OK');
+ return queryPlatformAndMetric('Mountain Lion', 'Time');
+ }).then(function (result) {
+ return TestServer.remoteAPI().getJSON(`/api/measurement-set/?platform=${result.platformId}&metric=${result.metricId}b`);
+ }).then(function (response) {
+ assert.equal(response['status'], 'InvalidMetric');
+ done();
+ }).catch(done);
+ });
+
+ it("should be able to retrieve a reported value", function (done) {
+ addBuilderForReport(reportWithBuildTime[0]).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report/', reportWithBuildTime);
+ }).then(function (response) {
+ assert.equal(response['status'], 'OK');
+ return queryPlatformAndMetric('Mountain Lion', 'Time');
+ }).then(function (result) {
+ return TestServer.remoteAPI().getJSONWithStatus(`/api/measurement-set/?platform=${result.platformId}&metric=${result.metricId}`);
+ }).then(function (response) {
+ const buildTime = +(new Date(reportWithBuildTime[0]['buildTime']));
+
+ assert.deepEqual(Object.keys(response).sort(),
+ ['clusterCount', 'clusterSize', 'clusterStart',
+ 'configurations', 'elapsedTime', 'endTime', 'formatMap', 'lastModified', 'startTime', 'status']);
+ assert.equal(response['status'], 'OK');
+ assert.equal(response['clusterCount'], 1);
+ assert.deepEqual(response['formatMap'], [
+ 'id', 'mean', 'iterationCount', 'sum', 'squareSum', 'markedOutlier',
+ 'revisions', 'commitTime', 'build', 'buildTime', 'buildNumber', 'builder']);
+
+ assert.equal(response['startTime'], reportWithBuildTime.startTime);
+ assert(typeof(response['lastModified']) == 'number', 'lastModified time should be a numeric');
+
+ assert.deepEqual(Object.keys(response['configurations']), ['current']);
+
+ var currentRows = response['configurations']['current'];
+ assert.equal(currentRows.length, 1);
+ assert.equal(currentRows[0].length, response['formatMap'].length);
+ assert.deepEqual(format(response['formatMap'], currentRows[0]), {
+ mean: 3,
+ iterationCount: 5,
+ sum: 15,
+ squareSum: 55,
+ markedOutlier: false,
+ revisions: [],
+ commitTime: buildTime,
+ buildTime: buildTime,
+ buildNumber: '123'});
+ done();
+ }).catch(done);
+ });
+
+ it("should return return the right IDs for measurement, build, and builder", function (done) {
+ addBuilderForReport(reportWithBuildTime[0]).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report/', reportWithBuildTime);
+ }).then(function (response) {
+ assert.equal(response['status'], 'OK');
+ return queryPlatformAndMetric('Mountain Lion', 'Time');
+ }).then(function (result) {
+ const db = TestServer.database();
+ return Promise.all([
+ db.selectAll('test_runs'),
+ db.selectAll('builds'),
+ db.selectAll('builders'),
+ TestServer.remoteAPI().getJSONWithStatus(`/api/measurement-set/?platform=${result.platformId}&metric=${result.metricId}`),
+ ]);
+ }).then(function (result) {
+ const runs = result[0];
+ const builds = result[1];
+ const builders = result[2];
+ const response = result[3];
+
+ assert.equal(runs.length, 1);
+ assert.equal(builds.length, 1);
+ assert.equal(builders.length, 1);
+ const measurementId = runs[0]['id'];
+ const buildId = builds[0]['id'];
+ const builderId = builders[0]['id'];
+
+ assert.equal(response['configurations']['current'].length, 1);
+ const measurement = response['configurations']['current'][0];
+ assert.equal(response['status'], 'OK');
+
+ assert.equal(measurement[response['formatMap'].indexOf('id')], measurementId);
+ assert.equal(measurement[response['formatMap'].indexOf('build')], buildId);
+ assert.equal(measurement[response['formatMap'].indexOf('builder')], builderId);
+
+ done();
+ }).catch(done);
+ });
+
+ function postReports(reports, callback)
+ {
+ if (!reports.length)
+ return callback();
+
+ postJSON('/api/report/', reports[0], function (response) {
+ assert.equal(response.statusCode, 200);
+ assert.equal(JSON.parse(response.responseText)['status'], 'OK');
+
+ postReports(reports.slice(1), callback);
+ });
+ }
+
+ function queryPlatformAndMetricWithRepository(platformName, metricName, repositoryName)
+ {
+ const db = TestServer.database();
+ return Promise.all([
+ db.selectFirstRow('platforms', {name: platformName}),
+ db.selectFirstRow('test_metrics', {name: metricName}),
+ db.selectFirstRow('repositories', {name: repositoryName}),
+ ]).then(function (result) {
+ return {platformId: result[0]['id'], metricId: result[1]['id'], repositoryId: result[2]['id']};
+ });
+ }
+
+ it("should order results by commit time", function (done) {
+ const remote = TestServer.remoteAPI();
+ let repositoryId;
+ addBuilderForReport(reportWithBuildTime[0]).then(function () {
+ return remote.postJSON('/api/report/', reportWithBuildTime);
+ }).then(function () {
+ return remote.postJSON('/api/report/', reportWithRevision);
+ }).then(function () {
+ return queryPlatformAndMetricWithRepository('Mountain Lion', 'Time', 'WebKit');
+ }).then(function (result) {
+ repositoryId = result.repositoryId;
+ return remote.getJSONWithStatus(`/api/measurement-set/?platform=${result.platformId}&metric=${result.metricId}`);
+ }).then(function (response) {
+ const currentRows = response['configurations']['current'];
+ const buildTime = +(new Date(reportWithBuildTime[0]['buildTime']));
+ const revisionTime = +(new Date(reportWithRevision[0]['revisions']['WebKit']['timestamp']));
+ const revisionBuildTime = +(new Date(reportWithRevision[0]['buildTime']));
+
+ assert.equal(currentRows.length, 2);
+ assert.deepEqual(format(response['formatMap'], currentRows[0]), {
+ mean: 13,
+ iterationCount: 5,
+ sum: 65,
+ squareSum: 855,
+ markedOutlier: false,
+ revisions: [[1, repositoryId, '144000', revisionTime]],
+ commitTime: revisionTime,
+ buildTime: revisionBuildTime,
+ buildNumber: '124' });
+ assert.deepEqual(format(response['formatMap'], currentRows[1]), {
+ mean: 3,
+ iterationCount: 5,
+ sum: 15,
+ squareSum: 55,
+ markedOutlier: false,
+ revisions: [],
+ commitTime: buildTime,
+ buildTime: buildTime,
+ buildNumber: '123' });
+ done();
+ }).catch(done);
+ });
+
+ function buildNumbers(parsedResult, config)
+ {
+ return parsedResult['configurations'][config].map(function (row) {
+ return format(parsedResult['formatMap'], row)['buildNumber'];
+ });
+ }
+
+ it("should include one data point after the current time range", function (done) {
+ const remote = TestServer.remoteAPI();
+ addBuilderForReport(reportWithBuildTime[0]).then(function () {
+ return remote.postJSON('/api/report/', reportWithAncentRevision);
+ }).then(function () {
+ return remote.postJSON('/api/report/', reportWithNewRevision);
+ }).then(function () {
+ return queryPlatformAndMetric('Mountain Lion', 'Time');
+ }).then(function (result) {
+ return remote.getJSONWithStatus(`/api/measurement-set/?platform=${result.platformId}&metric=${result.metricId}`);
+ }).then(function (response) {
+ assert.equal(response['status'], 'OK');
+ assert.equal(response['clusterCount'], 2, 'should have two clusters');
+ assert.deepEqual(buildNumbers(response, 'current'),
+ [reportWithAncentRevision[0]['buildNumber'], reportWithNewRevision[0]['buildNumber']]);
+ done();
+ }).catch(done);
+ });
+
+ it("should always include one old data point before the current time range", function (done) {
+ const remote = TestServer.remoteAPI();
+ addBuilderForReport(reportWithBuildTime[0]).then(function () {
+ return remote.postJSON('/api/report/', reportWithBuildTime);
+ }).then(function () {
+ return remote.postJSON('/api/report/', reportWithAncentRevision);
+ }).then(function () {
+ return queryPlatformAndMetric('Mountain Lion', 'Time');
+ }).then(function (result) {
+ return remote.getJSONWithStatus(`/api/measurement-set/?platform=${result.platformId}&metric=${result.metricId}`);
+ }).then(function (response) {
+ assert.equal(response['clusterCount'], 2, 'should have two clusters');
+ let currentRows = response['configurations']['current'];
+ assert.equal(currentRows.length, 2, 'should contain two data points');
+ assert.deepEqual(buildNumbers(response, 'current'), [reportWithAncentRevision[0]['buildNumber'], reportWithBuildTime[0]['buildNumber']]);
+ done();
+ }).catch(done);
+ });
+
+
+ it("should create cache results", function (done) {
+ const remote = TestServer.remoteAPI();
+ let cachePrefix;
+ addBuilderForReport(reportWithBuildTime[0]).then(function () {
+ return remote.postJSON('/api/report/', reportWithAncentRevision);
+ }).then(function () {
+ return remote.postJSON('/api/report/', reportWithRevision);
+ }).then(function () {
+ return remote.postJSON('/api/report/', reportWithNewRevision);
+ }).then(function () {
+ return queryPlatformAndMetric('Mountain Lion', 'Time');
+ }).then(function (result) {
+ cachePrefix = '/data/measurement-set-' + result.platformId + '-' + result.metricId;
+ return remote.getJSONWithStatus(`/api/measurement-set/?platform=${result.platformId}&metric=${result.metricId}`);
+ }).then(function (newResult) {
+ return remote.getJSONWithStatus(`${cachePrefix}.json`).then(function (cachedResult) {
+ assert.deepEqual(newResult, cachedResult);
+ return remote.getJSONWithStatus(`${cachePrefix}-${cachedResult['startTime']}.json`);
+ }).then(function (oldResult) {
+ var oldBuildNumbers = buildNumbers(oldResult, 'current');
+ var newBuildNumbers = buildNumbers(newResult, 'current');
+ assert(oldBuildNumbers.length >= 2, 'The old cluster should contain at least two data points');
+ assert(newBuildNumbers.length >= 2, 'The new cluster should contain at least two data points');
+ assert.deepEqual(oldBuildNumbers.slice(oldBuildNumbers.length - 2), newBuildNumbers.slice(0, 2),
+ 'Two conseqcutive clusters should share two data points');
+ done();
+ });
+ }).catch(done);
+ });
+
+});
--- /dev/null
+'use strict';
+
+const assert = require('assert');
+const crypto = require('crypto');
+
+const TestServer = require('./resources/test-server.js');
+const addSlaveForReport = require('./resources/common-operations.js').addSlaveForReport;
+const connectToDatabaseInEveryTest = require('./resources/common-operations.js').connectToDatabaseInEveryTest;
+
+describe("/api/report-commits/", function () {
+ this.timeout(1000);
+ TestServer.inject();
+ connectToDatabaseInEveryTest();
+
+ const emptyReport = {
+ "slaveName": "someSlave",
+ "slavePassword": "somePassword",
+ };
+ const subversionCommit = {
+ "slaveName": "someSlave",
+ "slavePassword": "somePassword",
+ "commits": [
+ {
+ "repository": "WebKit",
+ "revision": "141977",
+ "time": "2013-02-06T08:55:20.9Z",
+ "author": {"name": "Commit Queue", "account": "commit-queue@webkit.org"},
+ "message": "some message",
+ }
+ ],
+ };
+ const subversionInvalidCommit = {
+ "slaveName": "someSlave",
+ "slavePassword": "somePassword",
+ "commits": [
+ {
+ "repository": "WebKit",
+ "revision": "_141977",
+ "time": "2013-02-06T08:55:20.9Z",
+ "author": {"name": "Commit Queue", "account": "commit-queue@webkit.org"},
+ "message": "some message",
+ }
+ ],
+ };
+ const subversionTwoCommits = {
+ "slaveName": "someSlave",
+ "slavePassword": "somePassword",
+ "commits": [
+ {
+ "repository": "WebKit",
+ "revision": "141977",
+ "time": "2013-02-06T08:55:20.9Z",
+ "author": {"name": "Commit Queue", "account": "commit-queue@webkit.org"},
+ "message": "some message",
+ },
+ {
+ "repository": "WebKit",
+ "parent": "141977",
+ "revision": "141978",
+ "time": "2013-02-06T09:54:56.0Z",
+ "author": {"name": "Mikhail Pozdnyakov", "account": "mikhail.pozdnyakov@intel.com"},
+ "message": "another message",
+ }
+ ]
+ }
+
+ it("should reject error when slave name is missing", function (done) {
+ TestServer.remoteAPI().postJSON('/api/report-commits/', {}).then(function (response) {
+ assert.equal(response['status'], 'MissingSlaveName');
+ done();
+ }).catch(done);
+ });
+
+ it("should reject when there are no slaves", function (done) {
+ TestServer.remoteAPI().postJSON('/api/report-commits/', emptyReport).then(function (response) {
+ assert.equal(response['status'], 'SlaveNotFound');
+ return TestServer.database().selectAll('commits');
+ }).then(function (rows) {
+ assert.equal(rows.length, 0);
+ done();
+ }).catch(done);
+ });
+
+ it("should accept an empty report", function (done) {
+ addSlaveForReport(emptyReport).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report-commits/', emptyReport);
+ }).then(function (response) {
+ assert.equal(response['status'], 'OK');
+ done();
+ }).catch(done);
+ });
+
+ it("should add a missing repository", function (done) {
+ const db = TestServer.database();
+ addSlaveForReport(subversionCommit).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report-commits/', subversionCommit);
+ }).then(function (response) {
+ assert.equal(response['status'], 'OK');
+ return db.selectAll('repositories');
+ }).then(function (rows) {
+ assert.equal(rows.length, 1);
+ assert.equal(rows[0]['name'], subversionCommit.commits[0]['repository']);
+ done();
+ }).catch(done);
+ });
+
+ it("should store a commit from a valid slave", function (done) {
+ const db = TestServer.database();
+ addSlaveForReport(subversionCommit).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report-commits/', subversionCommit);
+ }).then(function (response) {
+ assert.equal(response['status'], 'OK');
+ return Promise.all([db.selectAll('commits'), db.selectAll('committers')]);
+ }).then(function (result) {
+ let commits = result[0];
+ let committers = result[1];
+ let reportedData = subversionCommit.commits[0];
+
+ assert.equal(commits.length, 1);
+ assert.equal(committers.length, 1);
+ assert.equal(commits[0]['revision'], reportedData['revision']);
+ assert.equal(commits[0]['time'].toString(), new Date('2013-02-06 08:55:20.9').toString());
+ assert.equal(commits[0]['message'], reportedData['message']);
+ assert.equal(commits[0]['committer'], committers[0]['id']);
+ assert.equal(committers[0]['name'], reportedData['author']['name']);
+ assert.equal(committers[0]['account'], reportedData['author']['account']);
+
+ done();
+ }).catch(done);
+ });
+
+ it("should reject an invalid revision number", function (done) {
+ addSlaveForReport(subversionCommit).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report-commits/', subversionInvalidCommit);
+ }).then(function (response) {
+ assert.equal(response['status'], 'InvalidRevision');
+ return TestServer.database().selectAll('commits');
+ }).then(function (rows) {
+ assert.equal(rows.length, 0);
+ done();
+ }).catch(done);
+ });
+
+ it("should store two commits from a valid slave", function (done) {
+ const db = TestServer.database();
+ addSlaveForReport(subversionTwoCommits).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report-commits/', subversionTwoCommits);
+ }).then(function (response) {
+ assert.equal(response['status'], 'OK');
+ return Promise.all([db.selectAll('commits'), db.selectAll('committers')]);
+ }).then(function (result) {
+ const commits = result[0];
+ const committers = result[1];
+ assert.equal(commits.length, 2);
+ assert.equal(committers.length, 2);
+
+ let reportedData = subversionTwoCommits.commits[0];
+ assert.equal(commits[0]['revision'], reportedData['revision']);
+ assert.equal(commits[0]['time'].toString(), new Date('2013-02-06 08:55:20.9').toString());
+ assert.equal(commits[0]['message'], reportedData['message']);
+ assert.equal(commits[0]['committer'], committers[0]['id']);
+ assert.equal(committers[0]['name'], reportedData['author']['name']);
+ assert.equal(committers[0]['account'], reportedData['author']['account']);
+
+ reportedData = subversionTwoCommits.commits[1];
+ assert.equal(commits[1]['revision'], reportedData['revision']);
+ assert.equal(commits[1]['time'].toString(), new Date('2013-02-06 09:54:56.0').toString());
+ assert.equal(commits[1]['message'], reportedData['message']);
+ assert.equal(commits[1]['committer'], committers[1]['id']);
+ assert.equal(committers[1]['name'], reportedData['author']['name']);
+ assert.equal(committers[1]['account'], reportedData['author']['account']);
+
+ done();
+ }).catch(done);
+ });
+
+ it("should update an existing commit if there is one", function (done) {
+ const db = TestServer.database();
+ const reportedData = subversionCommit.commits[0];
+ addSlaveForReport(subversionCommit).then(function () {
+ return Promise.all([
+ db.insert('repositories', {'id': 1, 'name': 'WebKit'}),
+ db.insert('commits', {'repository': 1, 'revision': reportedData['revision'], 'time': reportedData['time']})
+ ]);
+ }).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report-commits/', subversionCommit);
+ }).then(function (response) {
+ assert.equal(response['status'], 'OK');
+ return Promise.all([db.selectAll('commits'), db.selectAll('committers')]);
+ }).then(function (result) {
+ const commits = result[0];
+ const committers = result[1];
+
+ assert.equal(commits.length, 1);
+ assert.equal(committers.length, 1);
+ assert.equal(commits[0]['message'], reportedData['message']);
+ assert.equal(commits[0]['committer'], committers[0]['id']);
+ assert.equal(committers[0]['name'], reportedData['author']['name']);
+ assert.equal(committers[0]['account'], reportedData['author']['account']);
+
+ done();
+ }).catch(done);
+ });
+
+ it("should not update an unrelated commit", function (done) {
+ const db = TestServer.database();
+ const firstData = subversionTwoCommits.commits[0];
+ const secondData = subversionTwoCommits.commits[1];
+ addSlaveForReport(subversionCommit).then(function () {
+ return Promise.all([
+ db.insert('repositories', {'id': 1, 'name': 'WebKit'}),
+ db.insert('commits', {'id': 2, 'repository': 1, 'revision': firstData['revision'], 'time': firstData['time']}),
+ db.insert('commits', {'id': 3, 'repository': 1, 'revision': secondData['revision'], 'time': secondData['time']})
+ ]);
+ }).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report-commits/', subversionCommit);
+ }).then(function (response) {
+ assert.equal(response['status'], 'OK');
+ return Promise.all([db.selectAll('commits'), db.selectAll('committers')]);
+ }).then(function (result) {
+ const commits = result[0];
+ const committers = result[1];
+
+ assert.equal(commits.length, 2);
+ assert.equal(committers.length, 1);
+ assert.equal(commits[0]['id'], 2);
+ assert.equal(commits[0]['message'], firstData['message']);
+ assert.equal(commits[0]['committer'], committers[0]['id']);
+ assert.equal(committers[0]['name'], firstData['author']['name']);
+ assert.equal(committers[0]['account'], firstData['author']['account']);
+
+ assert.equal(commits[1]['id'], 3);
+ assert.equal(commits[1]['message'], null);
+ assert.equal(commits[1]['committer'], null);
+
+ done();
+ }).catch(done);
+ });
+
+ it("should update an existing committer if there is one", function (done) {
+ const db = TestServer.database();
+ const author = subversionCommit.commits[0]['author'];
+ addSlaveForReport(subversionCommit).then(function () {
+ return Promise.all([
+ db.insert('repositories', {'id': 1, 'name': 'WebKit'}),
+ db.insert('committers', {'repository': 1, 'account': author['account']}),
+ ]);
+ }).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report-commits/', subversionCommit);
+ }).then(function (response) {
+ assert.equal(response['status'], 'OK');
+ return db.selectAll('committers');
+ }).then(function (committers) {
+ assert.equal(committers.length, 1);
+ assert.equal(committers[0]['name'], author['name']);
+ assert.equal(committers[0]['account'], author['account']);
+ done();
+ }).catch(done);
+ });
+
+});
--- /dev/null
+'use strict';
+
+const assert = require('assert');
+
+const TestServer = require('./resources/test-server.js');
+const addBuilderForReport = require('./resources/common-operations.js').addBuilderForReport;
+const addSlaveForReport = require('./resources/common-operations.js').addSlaveForReport;
+const connectToDatabaseInEveryTest = require('./resources/common-operations.js').connectToDatabaseInEveryTest;
+
+describe("/api/report", function () {
+ this.timeout(1000);
+ TestServer.inject();
+ connectToDatabaseInEveryTest();
+
+ function emptyReport()
+ {
+ return {
+ "buildNumber": "123",
+ "buildTime": "2013-02-28T10:12:03.388304",
+ "builderName": "someBuilder",
+ "slaveName": "someSlave",
+ "builderPassword": "somePassword",
+ "platform": "Mountain Lion",
+ "tests": {},
+ "revisions": {
+ "OS X": {
+ "revision": "10.8.2 12C60"
+ },
+ "WebKit": {
+ "revision": "141977",
+ "timestamp": "2013-02-06T08:55:20.9Z"
+ }
+ }
+ };
+ }
+
+ function emptySlaveReport()
+ {
+ return {
+ "buildNumber": "123",
+ "buildTime": "2013-02-28T10:12:03.388304",
+ "builderName": "someBuilder",
+ "slaveName": "someSlave",
+ "slavePassword": "otherPassword",
+ "platform": "Mountain Lion",
+ "tests": {},
+ "revisions": {
+ "OS X": {
+ "revision": "10.8.2 12C60"
+ },
+ "WebKit": {
+ "revision": "141977",
+ "timestamp": "2013-02-06T08:55:20.9Z"
+ }
+ }
+ };
+ }
+
+ it("should reject error when builder name is missing", function (done) {
+ TestServer.remoteAPI().postJSON('/api/report/', [{"buildTime": "2013-02-28T10:12:03.388304"}]).then(function (response) {
+ assert.equal(response['status'], 'MissingBuilderName');
+ done();
+ }).catch(done);
+ });
+
+ it("should reject error when build time is missing", function (done) {
+ addBuilderForReport(emptyReport()).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report/', [{"builderName": "someBuilder", "builderPassword": "somePassword"}]);
+ }).then(function (response) {
+ assert.equal(response['status'], 'MissingBuildTime');
+ done();
+ });
+ });
+
+ it("should reject when there are no builders", function (done) {
+ TestServer.remoteAPI().postJSON('/api/report/', [emptyReport()]).then(function (response) {
+ assert.equal(response['status'], 'BuilderNotFound');
+ assert.equal(response['failureStored'], false);
+ assert.equal(response['processedRuns'], 0);
+ return TestServer.database().selectAll('reports');
+ }).then(function (reports) {
+ assert.equal(reports.length, 0);
+ done();
+ }).catch(done);
+ });
+
+ it("should reject a report without a builder password", function (done) {
+ addBuilderForReport(emptyReport()).then(function () {
+ var report = [{
+ "buildNumber": "123",
+ "buildTime": "2013-02-28T10:12:03.388304",
+ "builderName": "someBuilder",
+ "tests": {},
+ "revisions": {}}];
+ return TestServer.remoteAPI().postJSON('/api/report/', report);
+ }).then(function (response) {
+ assert.equal(response['status'], 'BuilderNotFound');
+ assert.equal(response['failureStored'], false);
+ assert.equal(response['processedRuns'], 0);
+ return TestServer.database().selectAll('reports');
+ }).then(function (reports) {
+ assert.equal(reports.length, 0);
+ done();
+ }).catch(done);
+ });
+
+ it("should store a report from a valid builder", function (done) {
+ addBuilderForReport(emptyReport()).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report/', [emptyReport()]);
+ }).then(function (response) {
+ assert.equal(response['status'], 'OK');
+ assert.equal(response['failureStored'], false);
+ assert.equal(response['processedRuns'], 1);
+ return TestServer.database().selectAll('reports');
+ }).then(function (reports) {
+ assert.equal(reports.length, 1);
+ const submittedContent = emptyReport();
+ const storedContent = JSON.parse(reports[0]['content']);
+
+ delete submittedContent['builderPassword'];
+ delete submittedContent['tests'];
+ delete storedContent['tests'];
+ assert.deepEqual(storedContent, submittedContent);
+
+ done();
+ }).catch(done);
+ });
+
+ it("should treat the slave password as the builder password if there is no matching slave", function (done) {
+ let report = emptyReport();
+ report['slavePassword'] = report['builderPassword'];
+ delete report['builderPassword'];
+
+ addSlaveForReport(report).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report/', [report]);
+ }).then(function (response) {
+ assert.equal(response['status'], 'OK');
+ assert.equal(response['failureStored'], false);
+ assert.equal(response['processedRuns'], 1);
+ return TestServer.database().selectAll('reports');
+ }).then(function (reports) {
+ assert.equal(reports.length, 1);
+ const storedContent = JSON.parse(reports[0]['content']);
+
+ delete report['slavePassword'];
+ delete report['tests'];
+ delete storedContent['tests'];
+ assert.deepEqual(storedContent, report);
+
+ done();
+ }).catch(done);
+ });
+
+ it("should store a report from a valid slave", function (done) {
+ addSlaveForReport(emptySlaveReport()).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report/', [emptySlaveReport()]);
+ }).then(function (response) {
+ assert.equal(response['status'], 'OK');
+ assert.equal(response['failureStored'], false);
+ assert.equal(response['processedRuns'], 1);
+ return TestServer.database().selectAll('reports');
+ }).then(function (reports) {
+ assert.equal(reports.length, 1);
+ const submittedContent = emptySlaveReport();
+ const storedContent = JSON.parse(reports[0]['content']);
+
+ delete submittedContent['slavePassword'];
+ delete submittedContent['tests'];
+ delete storedContent['tests'];
+ assert.deepEqual(storedContent, submittedContent);
+
+ done();
+ }).catch(done);
+ });
+
+ it("should store the builder name but not the builder password", function (done) {
+ addBuilderForReport(emptyReport()).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report/', [emptyReport()]);
+ }).then(function (response) {
+ return TestServer.database().selectAll('reports');
+ }).then(function (reports) {
+ assert.equal(reports.length, 1);
+ const storedContent = JSON.parse(reports[0]['content']);
+ assert.equal(storedContent['builderName'], emptyReport()['builderName']);
+ assert(!('builderPassword' in storedContent));
+ done();
+ }).catch(done);
+ });
+
+ it("should add a slave if there isn't one and the report was authenticated by a builder", function (done) {
+ addBuilderForReport(emptyReport()).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report/', [emptyReport()]);
+ }).then(function (response) {
+ return TestServer.database().selectAll('build_slaves');
+ }).then(function (slaves) {
+ assert.equal(slaves.length, 1);
+ assert.equal(slaves[0]['name'], emptyReport()['slaveName']);
+ done();
+ }).catch(done);
+ });
+
+ it("should add a builder if there isn't one and the report was authenticated by a slave", function (done) {
+ addSlaveForReport(emptySlaveReport()).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report/', [emptySlaveReport()]);
+ }).then(function (response) {
+ return TestServer.database().selectAll('builders');
+ }).then(function (builders) {
+ assert.equal(builders.length, 1);
+ assert.equal(builders[0]['name'], emptyReport()['builderName']);
+ done();
+ }).catch(done);
+ });
+
+ it("should add a build", function (done) {
+ addBuilderForReport(emptyReport()).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report/', [emptyReport()]);
+ }).then(function () {
+ return TestServer.database().selectAll('builds');
+ }).then(function (builds) {
+ assert.strictEqual(builds[0]['number'], 123);
+ done();
+ }).catch(done);
+ });
+
+ it("should add the platform", function (done) {
+ addBuilderForReport(emptyReport()).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report/', [emptyReport()]);
+ }).then(function () {
+ return TestServer.database().selectAll('platforms');
+ }).then(function (platforms) {
+ assert.equal(platforms.length, 1);
+ assert.equal(platforms[0]['name'], 'Mountain Lion');
+ done();
+ }).catch(done);
+ });
+
+ it("should add repositories and build revisions", function (done) {
+ addBuilderForReport(emptyReport()).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report/', [emptyReport()]);
+ }).then(function (response) {
+ const db = TestServer.database();
+ return Promise.all([
+ db.selectAll('repositories'),
+ db.selectAll('commits'),
+ db.selectAll('build_commits', 'build_commit'),
+ ]);
+ }).then(function (result) {
+ const repositories = result[0];
+ const commits = result[1];
+ const buildCommitsRelations = result[2];
+ assert.equal(repositories.length, 2);
+ assert.deepEqual(repositories.map(function (row) { return row['name']; }).sort(), ['OS X', 'WebKit']);
+
+ assert.equal(commits.length, 2);
+ assert.equal(buildCommitsRelations.length, 2);
+ assert.equal(buildCommitsRelations[0]['build_commit'], commits[0]['id']);
+ assert.equal(buildCommitsRelations[1]['build_commit'], commits[1]['id']);
+ assert.equal(buildCommitsRelations[0]['commit_build'], buildCommitsRelations[1]['commit_build']);
+
+ let repositoryIdToName = {};
+ for (let repository of repositories)
+ repositoryIdToName[repository['id']] = repository['name'];
+
+ let repositoryNameToRevisionRow = {};
+ for (let commit of commits)
+ repositoryNameToRevisionRow[repositoryIdToName[commit['repository']]] = commit;
+
+ assert.equal(repositoryNameToRevisionRow['OS X']['revision'], '10.8.2 12C60');
+ assert.equal(repositoryNameToRevisionRow['WebKit']['revision'], '141977');
+ assert.equal(repositoryNameToRevisionRow['WebKit']['time'].toString(),
+ new Date('2013-02-06 08:55:20.9').toString());
+ done();
+ }).catch(done);
+ });
+
+ it("should not create a duplicate build for the same build number if build times are close", function (done) {
+ let firstReport = emptyReport();
+ firstReport['buildTime'] = '2013-02-28T10:12:04';
+ let secondReport = emptyReport();
+ secondReport['buildTime'] = '2013-02-28T10:22:03';
+
+ addBuilderForReport(emptyReport()).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report/', [firstReport]);
+ }).then(function (response) {
+ assert.equal(response['status'], 'OK');
+ return TestServer.database().selectAll('builds');
+ }).then(function (builds) {
+ assert.equal(builds.length, 1);
+ return TestServer.remoteAPI().postJSON('/api/report/', [secondReport]);
+ }).then(function (response) {
+ assert.equal(response['status'], 'OK');
+ return TestServer.database().selectAll('builds');
+ }).then(function (builds) {
+ assert.equal(builds.length, 1);
+ done();
+ }).catch(done);
+ });
+
+ it("should create distinct builds for the same build number if build times are far apart", function (done) {
+ let firstReport = emptyReport();
+ firstReport['buildTime'] = '2013-02-28T10:12:03';
+ let secondReport = emptyReport();
+ secondReport['buildTime'] = '2014-01-20T22:23:34';
+
+ addBuilderForReport(emptyReport()).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report/', [firstReport]);
+ }).then(function (response) {
+ assert.equal(response['status'], 'OK');
+ return TestServer.database().selectAll('builds');
+ }).then(function (builds) {
+ assert.equal(builds.length, 1);
+ return TestServer.remoteAPI().postJSON('/api/report/', [secondReport]);
+ }).then(function (response) {
+ assert.equal(response['status'], 'OK');
+ return TestServer.database().selectAll('builds');
+ }).then(function (builds) {
+ assert.equal(builds.length, 2);
+ done();
+ }).catch(done);
+ });
+
+ it("should reject a report with mismatching revision info", function (done) {
+ let firstReport = emptyReport();
+ firstReport['revisions'] = {
+ "WebKit": {
+ "revision": "141977",
+ "timestamp": "2013-02-06T08:55:20.96Z"
+ }
+ };
+
+ let secondReport = emptyReport();
+ secondReport['revisions'] = {
+ "WebKit": {
+ "revision": "150000",
+ "timestamp": "2013-05-13T10:50:29.6Z"
+ }
+ };
+
+ addBuilderForReport(firstReport).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report/', [firstReport]);
+ }).then(function (response) {
+ assert.equal(response['status'], 'OK');
+ return TestServer.database().selectAll('builds');
+ }).then(function (builds) {
+ assert.equal(builds.length, 1);
+ return TestServer.remoteAPI().postJSON('/api/report/', [secondReport]);
+ }).then(function (response) {
+ assert.equal(response['status'], 'MismatchingCommitRevision');
+ assert(JSON.stringify(response).indexOf('141977') >= 0);
+ assert(JSON.stringify(response).indexOf('150000') >= 0);
+ assert.equal(response['failureStored'], true);
+ assert.equal(response['processedRuns'], 0);
+ done();
+ }).catch(done);
+ });
+
+ const reportWithTwoLevelsOfAggregations = {
+ "buildNumber": "123",
+ "buildTime": "2013-02-28T10:12:03.388304",
+ "builderName": "someBuilder",
+ "builderPassword": "somePassword",
+ "platform": "Mountain Lion",
+ "tests": {
+ "DummyPageLoading": {
+ "metrics": {"Time": { "aggregators" : ["Arithmetic"], "current": [300, 310, 320, 330] }},
+ "tests": {
+ "apple.com": {
+ "metrics": {"Time": { "current": [500, 510, 520, 530] }},
+ "url": "http://www.apple.com"
+ },
+ "webkit.org": {
+ "metrics": {"Time": { "current": [100, 110, 120, 130] }},
+ "url": "http://www.webkit.org"
+ }
+ }
+ },
+ "DummyBenchmark": {
+ "metrics": {"Time": ["Arithmetic"]},
+ "tests": {
+ "DOM": {
+ "metrics": {"Time": ["Geometric", "Arithmetic"]},
+ "tests": {
+ "ModifyNodes": {"metrics": {"Time": { "current": [[11, 12, 13, 14, 15], [16, 17, 18, 19, 20], [21, 22, 23, 24, 25]] }}},
+ "TraverseNodes": {"metrics": {"Time": { "current": [[31, 32, 33, 34, 35], [36, 37, 38, 39, 40], [41, 42, 43, 44, 45]] }}}
+ }
+ },
+ "CSS": {"metrics": {"Time": { "current": [[101, 102, 103, 104, 105], [106, 107, 108, 109, 110], [111, 112, 113, 114, 115]] }}}
+ }
+ }
+ },
+ "revisions": {
+ "OS X": {
+ "revision": "10.8.2 12C60"
+ },
+ "WebKit": {
+ "revision": "141977",
+ "timestamp": "2013-02-06T08:55:20.9Z"
+ }
+ }};
+
+ function reportAfterAddingBuilderAndAggregators(report)
+ {
+ return addBuilderForReport(report).then(function () {
+ const db = TestServer.database();
+ return Promise.all([
+ db.insert('aggregators', {name: 'Arithmetic'}),
+ db.insert('aggregators', {name: 'Geometric'}),
+ ]);
+ }).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report/', [report]);
+ }).then(function (response) {
+ assert.equal(response['status'], 'OK');
+ assert.equal(response['failureStored'], false);
+ return response;
+ });
+ }
+
+ function fetchRunForMetric(testName, metricName,callback) {
+ queryAndFetchAll('SELECT * FROM test_runs WHERE run_config IN'
+ + '(SELECT config_id FROM test_configurations, test_metrics, tests WHERE config_metric = metric_id AND metric_test = test_id AND'
+ + 'test_name = $1 AND metric_name = $2)',
+ ['Arithmetic', 'values.reduce(function (a, b) { return a + b; }) / values.length'], function () {
+ queryAndFetchAll('INSERT INTO aggregators (aggregator_name, aggregator_definition) values ($1, $2)',
+ ['Geometric', 'Math.pow(values.reduce(function (a, b) { return a * b; }), 1 / values.length)'], callback);
+ });
+ }
+
+ it("should accept a report with aggregators", function (done) {
+ reportAfterAddingBuilderAndAggregators(reportWithTwoLevelsOfAggregations).then(function () {
+ done();
+ }).catch(done);
+ });
+
+ it("should add tests", function (done) {
+ reportAfterAddingBuilderAndAggregators(reportWithTwoLevelsOfAggregations).then(function () {
+ return TestServer.database().selectAll('tests');
+ }).then(function (tests) {
+ assert.deepEqual(tests.map(function (row) { return row['name']; }).sort(),
+ ['CSS', 'DOM', 'DummyBenchmark', 'DummyPageLoading', 'ModifyNodes', 'TraverseNodes', 'apple.com', 'webkit.org']);
+ done();
+ }).catch(done);
+ });
+
+ it("should add metrics", function (done) {
+ reportAfterAddingBuilderAndAggregators(reportWithTwoLevelsOfAggregations).then(function () {
+ return TestServer.database().query('SELECT * FROM tests, test_metrics LEFT JOIN aggregators ON metric_aggregator = aggregator_id WHERE metric_test = test_id');
+ }).then(function (result) {
+ let testNameToMetrics = {};
+ result.rows.forEach(function (row) {
+ if (!(row['test_name'] in testNameToMetrics))
+ testNameToMetrics[row['test_name']] = new Array;
+ testNameToMetrics[row['test_name']].push([row['metric_name'], row['aggregator_name']]);
+ });
+ assert.deepEqual(testNameToMetrics['CSS'], [['Time', null]]);
+ assert.deepEqual(testNameToMetrics['DOM'].sort(), [['Time', 'Arithmetic'], ['Time', 'Geometric']]);
+ assert.deepEqual(testNameToMetrics['DummyBenchmark'], [['Time', 'Arithmetic']]);
+ assert.deepEqual(testNameToMetrics['DummyPageLoading'], [['Time', 'Arithmetic']]);
+ assert.deepEqual(testNameToMetrics['ModifyNodes'], [['Time', null]]);
+ assert.deepEqual(testNameToMetrics['TraverseNodes'], [['Time', null]]);
+ assert.deepEqual(testNameToMetrics['apple.com'], [['Time', null]]);
+ assert.deepEqual(testNameToMetrics['webkit.org'], [['Time', null]]);
+ done();
+ }).catch(done);
+ });
+
+ function fetchTestConfig(testName, metricName)
+ {
+ return TestServer.database().query(`SELECT * FROM tests, test_metrics, test_configurations
+ WHERE test_id = metric_test AND metric_id = config_metric
+ AND test_name = $1 AND metric_name = $2`, [testName, metricName]).then(function (result) {
+ assert.equal(result.rows.length, 1);
+ return result.rows[0];
+ });
+ }
+
+ function fetchTestRunIterationsForMetric(testName, metricName)
+ {
+ const db = TestServer.database();
+ return fetchTestConfig(testName, metricName).then(function (config) {
+ return db.selectFirstRow('test_runs', {config: config['config_id']});
+ }).then(function (run) {
+ return db.selectRows('run_iterations', {run: run['id']}, {sortBy: 'order'}).then(function (iterations) {
+ return {run: run, iterations: iterations};
+ });
+ });
+ }
+
+ it("should store run values", function (done) {
+ reportAfterAddingBuilderAndAggregators(reportWithTwoLevelsOfAggregations).then(function () {
+ return fetchTestRunIterationsForMetric('apple.com', 'Time');
+ }).then(function (result) {
+ const run = result.run;
+ const runId = run['id'];
+ assert.deepEqual(result.iterations, [
+ {run: runId, order: 0, group: null, value: 500, relative_time: null},
+ {run: runId, order: 1, group: null, value: 510, relative_time: null},
+ {run: runId, order: 2, group: null, value: 520, relative_time: null},
+ {run: runId, order: 3, group: null, value: 530, relative_time: null}]);
+ var sum = 500 + 510 + 520 + 530;
+ assert.equal(run['mean_cache'], sum / result.iterations.length);
+ assert.equal(run['sum_cache'], sum);
+ assert.equal(run['square_sum_cache'], 500 * 500 + 510 * 510 + 520 * 520 + 530 * 530);
+ return fetchTestRunIterationsForMetric('CSS', 'Time');
+ }).then(function (result) {
+ const run = result.run;
+ const runId = run['id'];
+ assert.deepEqual(result.iterations, [
+ {run: runId, order: 0, group: 0, value: 101, relative_time: null},
+ {run: runId, order: 1, group: 0, value: 102, relative_time: null},
+ {run: runId, order: 2, group: 0, value: 103, relative_time: null},
+ {run: runId, order: 3, group: 0, value: 104, relative_time: null},
+ {run: runId, order: 4, group: 0, value: 105, relative_time: null},
+ {run: runId, order: 5, group: 1, value: 106, relative_time: null},
+ {run: runId, order: 6, group: 1, value: 107, relative_time: null},
+ {run: runId, order: 7, group: 1, value: 108, relative_time: null},
+ {run: runId, order: 8, group: 1, value: 109, relative_time: null},
+ {run: runId, order: 9, group: 1, value: 110, relative_time: null},
+ {run: runId, order: 10, group: 2, value: 111, relative_time: null},
+ {run: runId, order: 11, group: 2, value: 112, relative_time: null},
+ {run: runId, order: 12, group: 2, value: 113, relative_time: null},
+ {run: runId, order: 13, group: 2, value: 114, relative_time: null},
+ {run: runId, order: 14, group: 2, value: 115, relative_time: null}]);
+ let sum = 0;
+ let squareSum = 0;
+ for (let value = 101; value <= 115; ++value) {
+ sum += value;
+ squareSum += value * value;
+ }
+ assert.equal(run['mean_cache'], sum / result.iterations.length);
+ assert.equal(run['sum_cache'], sum);
+ assert.equal(run['square_sum_cache'], squareSum);
+ done();
+ }).catch(done);
+ });
+
+ it("should store aggregated run values", function (done) {
+ reportAfterAddingBuilderAndAggregators(reportWithTwoLevelsOfAggregations).then(function () {
+ return fetchTestRunIterationsForMetric('DummyPageLoading', 'Time');
+ }).then(function (result) {
+ const run = result.run;
+ const runId = result.run['id'];
+ const expectedValues = [(500 + 100) / 2, (510 + 110) / 2, (520 + 120) / 2, (530 + 130) / 2];
+ assert.deepEqual(result.iterations, [
+ {run: runId, order: 0, group: null, value: expectedValues[0], relative_time: null},
+ {run: runId, order: 1, group: null, value: expectedValues[1], relative_time: null},
+ {run: runId, order: 2, group: null, value: expectedValues[2], relative_time: null},
+ {run: runId, order: 3, group: null, value: expectedValues[3], relative_time: null}]);
+ const sum = expectedValues.reduce(function (sum, value) { return sum + value; }, 0);
+ assert.equal(run['mean_cache'], sum / result.iterations.length);
+ assert.equal(run['sum_cache'], sum);
+ assert.equal(run['square_sum_cache'], expectedValues.reduce(function (sum, value) { return sum + value * value; }, 0));
+ done();
+ }).catch(done);
+ });
+
+ it("should be able to compute the aggregation of aggregated values", function (done) {
+ reportAfterAddingBuilderAndAggregators(reportWithTwoLevelsOfAggregations).then(function () {
+ return fetchTestRunIterationsForMetric('DummyBenchmark', 'Time');
+ }).then(function (result) {
+ const run = result.run;
+ const runId = run['id'];
+ const expectedIterations = [];
+ let sum = 0;
+ let squareSum = 0;
+ for (let i = 0; i < 15; ++i) {
+ const value = i + 1;
+ const DOMMean = ((10 + value) + (30 + value)) / 2;
+ const expectedValue = (DOMMean + 100 + value) / 2;
+ sum += expectedValue;
+ squareSum += expectedValue * expectedValue;
+ expectedIterations.push({run: runId, order: i, group: Math.floor(i / 5), value: expectedValue, relative_time: null});
+ }
+ assert.deepEqual(result.iterations, expectedIterations);
+ assert.equal(run['mean_cache'], sum / result.iterations.length);
+ assert.equal(run['sum_cache'], sum);
+ assert.equal(run['square_sum_cache'], squareSum);
+ done();
+ }).catch(done);
+ });
+
+ function reportWithSameSubtestName()
+ {
+ return {
+ "buildNumber": "123",
+ "buildTime": "2013-02-28T10:12:03.388304",
+ "builderName": "someBuilder",
+ "builderPassword": "somePassword",
+ "platform": "Mountain Lion",
+ "tests": {
+ "Suite1": {
+ "tests": {
+ "test1": {
+ "metrics": {"Time": { "current": [1, 2, 3, 4, 5] }}
+ },
+ "test2": {
+ "metrics": {"Time": { "current": [6, 7, 8, 9, 10] }}
+ }
+ }
+ },
+ "Suite2": {
+ "tests": {
+ "test1": {
+ "metrics": {"Time": { "current": [11, 12, 13, 14, 15] }}
+ },
+ "test2": {
+ "metrics": {"Time": { "current": [16, 17, 18, 19, 20] }}
+ }
+ }
+ }
+ }
+ };
+ }
+
+ it("should be able to add a report with same subtest name", function (done) {
+ reportAfterAddingBuilderAndAggregators(reportWithSameSubtestName()).then(function () {
+ done();
+ }).catch(done);
+ });
+
+ it("should be able to reuse the same test rows", function (done) {
+ reportAfterAddingBuilderAndAggregators(reportWithSameSubtestName()).then(function () {
+ return TestServer.database().selectAll('tests');
+ }).then(function (tests) {
+ assert.equal(tests.length, 6);
+ let newReport = reportWithSameSubtestName();
+ newReport.buildNumber = "125";
+ newReport.buildTime = "2013-02-28T12:17:24.1";
+ return TestServer.remoteAPI().postJSON('/api/report/', [newReport]);
+ }).then(function (response) {
+ assert.equal(response['status'], 'OK');
+ return TestServer.database().selectAll('tests');
+ }).then(function (tests) {
+ assert.equal(tests.length, 6);
+ done();
+ }).catch(done);
+ });
+
+ const reportWithSameSingleValue = {
+ "buildNumber": "123",
+ "buildTime": "2013-02-28T10:12:03.388304",
+ "builderName": "someBuilder",
+ "builderPassword": "somePassword",
+ "platform": "Mountain Lion",
+ "tests": {
+ "suite": {
+ "metrics": {"Combined": ["Arithmetic"]},
+ "tests": {
+ "test1": {
+ "metrics": {"Combined": { "current": 3 }}
+ },
+ "test2": {
+ "metrics": {"Combined": { "current": 7 }}
+ }
+ }
+ },
+ }};
+
+ it("should be able to add a report with single value results", function (done) {
+ reportAfterAddingBuilderAndAggregators(reportWithSameSingleValue).then(function () {
+ return fetchTestRunIterationsForMetric('test1', 'Combined');
+ }).then(function (result) {
+ const run = result.run;
+ assert.equal(run['iteration_count_cache'], 1);
+ assert.equal(run['mean_cache'], 3);
+ assert.equal(run['sum_cache'], 3);
+ assert.equal(run['square_sum_cache'], 9);
+ return fetchTestRunIterationsForMetric('suite', 'Combined');
+ }).then(function (result) {
+ const run = result.run;
+ assert.equal(run['iteration_count_cache'], 1);
+ assert.equal(run['mean_cache'], 5);
+ assert.equal(run['sum_cache'], 5);
+ assert.equal(run['square_sum_cache'], 25);
+ done();
+ }).catch(done);
+ });
+
+ const reportWithSameValuePairs = {
+ "buildNumber": "123",
+ "buildTime": "2013-02-28T10:12:03.388304",
+ "builderName": "someBuilder",
+ "builderPassword": "somePassword",
+ "platform": "Mountain Lion",
+ "tests": {
+ "test": {
+ "metrics": {"FrameRate": { "current": [[[0, 4], [100, 5], [205, 3]]] }}
+ },
+ },
+ };
+
+ it("should be able to add a report with (relative time, value) pairs", function (done) {
+ reportAfterAddingBuilderAndAggregators(reportWithSameValuePairs).then(function () {
+ return fetchTestRunIterationsForMetric('test', 'FrameRate');
+ }).then(function (result) {
+ const run = result.run;
+ assert.equal(run['iteration_count_cache'], 3);
+ assert.equal(run['mean_cache'], 4);
+ assert.equal(run['sum_cache'], 12);
+ assert.equal(run['square_sum_cache'], 16 + 25 + 9);
+
+ const runId = run['id'];
+ assert.deepEqual(result.iterations, [
+ {run: runId, order: 0, group: null, value: 4, relative_time: 0},
+ {run: runId, order: 1, group: null, value: 5, relative_time: 100},
+ {run: runId, order: 2, group: null, value: 3, relative_time: 205}]);
+ done();
+ }).catch(done);
+ });
+
+ const reportsUpdatingDifferentTests = [
+ {
+ "buildNumber": "123",
+ "buildTime": "2013-02-28T10:12:03",
+ "builderName": "someBuilder",
+ "builderPassword": "somePassword",
+ "platform": "Mountain Lion",
+ "tests": {"test1": {"metrics": {"Time": {"current": 3}}}}
+ },
+ {
+ "buildNumber": "124",
+ "buildTime": "2013-02-28T11:31:21",
+ "builderName": "someBuilder",
+ "builderPassword": "somePassword",
+ "platform": "Mountain Lion",
+ "tests": {"test2": {"metrics": {"Time": {"current": 3}}}}
+ },
+ {
+ "buildNumber": "125",
+ "buildTime": "2013-02-28T12:45:34",
+ "builderName": "someBuilder",
+ "builderPassword": "somePassword",
+ "platform": "Mountain Lion",
+ "tests": {"test1": {"metrics": {"Time": {"current": 3}}}}
+ },
+ ];
+
+ it("should update the last modified date of test configurations with new runs", function (done) {
+ addBuilderForReport(reportsUpdatingDifferentTests[0]).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report/', [reportsUpdatingDifferentTests[0]]);
+ }).then(function (response) {
+ assert.equal(response['status'], 'OK');
+ return fetchTestConfig('test1', 'Time');
+ }).then(function (originalConfig) {
+ return TestServer.remoteAPI().postJSON('/api/report/', [reportsUpdatingDifferentTests[2]]).then(function () {
+ return fetchTestConfig('test1', 'Time');
+ }).then(function (config) {
+ assert(originalConfig['config_runs_last_modified'] instanceof Date);
+ assert(config['config_runs_last_modified'] instanceof Date);
+ assert(+originalConfig['config_runs_last_modified'] < +config['config_runs_last_modified']);
+ done();
+ });
+ }).catch(done);
+ });
+
+ it("should not update the last modified date of unrelated test configurations", function (done) {
+ addBuilderForReport(reportsUpdatingDifferentTests[0]).then(function () {
+ return TestServer.remoteAPI().postJSON('/api/report/', [reportsUpdatingDifferentTests[0]]);
+ }).then(function (response) {
+ assert.equal(response['status'], 'OK');
+ return fetchTestConfig('test1', 'Time');
+ }).then(function (originalConfig) {
+ return TestServer.remoteAPI().postJSON('/api/report/', [reportsUpdatingDifferentTests[1]]).then(function (response) {
+ assert.equal(response['status'], 'OK');
+ return fetchTestConfig('test1', 'Time');
+ }).then(function (config) {
+ assert(originalConfig['config_runs_last_modified'] instanceof Date);
+ assert(config['config_runs_last_modified'] instanceof Date);
+ assert.equal(+originalConfig['config_runs_last_modified'], +config['config_runs_last_modified']);
+ done();
+ });
+ }).catch(done);
+ });
+});
let Database = require('../../tools/js/database.js');
let RemoteAPI = require('../../tools/js/remote.js').RemoteAPI;
-let TestServer = (new class TestServer {
+class TestServer {
constructor()
{
this._pidFile = null;
return self.stop();
});
}
-});
+}
+if (!global.TestServer)
+ global.TestServer = new TestServer;
if (typeof module != 'undefined')
- module.exports = TestServer;
+ module.exports = global.TestServer;
}
describe('BuildbotTriggerable', function () {
- this.timeout(10000);
+ this.timeout(1000);
TestServer.inject();
beforeEach(function () {
+++ /dev/null
-describe("/admin/platforms", function () {
- var reportsForDifferentPlatforms = [
- {
- "buildNumber": "3001",
- "buildTime": "2013-02-28T09:01:47",
- "builderName": "someBuilder",
- "builderPassword": "somePassword",
- "platform": "Mavericks",
- "tests": {"test": { "metrics": {"FrameRate": { "current": [[1, 1, 1], [1, 1, 1]] } } } },
- },
- {
- "buildNumber": "3001",
- "buildTime": "2013-02-28T10:12:03",
- "builderName": "someBuilder",
- "builderPassword": "somePassword",
- "platform": "Mountain Lion",
- "tests": {"test": { "metrics": {"FrameRate": { "current": [[2, 2, 2], [2, 2, 2]] }, "Combined": { "current": [[3, 3, 3], [3, 3, 3]] }} } },
- },
- {
- "buildNumber": "3003",
- "buildTime": "2013-02-28T12:56:26",
- "builderName": "someBuilder",
- "builderPassword": "somePassword",
- "platform": "Trunk Mountain Lion",
- "tests": {"test": { "metrics": {"FrameRate": { "current": [[4, 4, 4], [4, 4, 4]] } } } }
- }];
-
- function submitReport(report, callback) {
- queryAndFetchAll('INSERT INTO builders (builder_name, builder_password_hash) values ($1, $2)',
- [report[0].builderName, sha256(report[0].builderPassword)], function () {
- postJSON('/api/report/', reportsForDifferentPlatforms, function (response) {
- callback();
- });
- });
- }
-
- it("should delete the platform that got merged into another one", function () {
- submitReport(reportsForDifferentPlatforms, function () {
- queryAndFetchAll('SELECT * FROM platforms ORDER by platform_name', [], function (oldPlatforms) {
- assert.equal(oldPlatforms.length, 3);
- assert.equal(oldPlatforms[0]['platform_name'], 'Mavericks');
- assert.equal(oldPlatforms[1]['platform_name'], 'Mountain Lion');
- assert.equal(oldPlatforms[2]['platform_name'], 'Trunk Mountain Lion');
- httpPost('/admin/platforms.php', {'action': 'merge', 'id': oldPlatforms[1]['platform_id'], 'destination': oldPlatforms[2]['platform_id']}, function (response) {
- assert.equal(response.statusCode, 200);
- queryAndFetchAll('SELECT * FROM platforms ORDER by platform_name', [], function (newPlatforms) {
- assert.deepEqual(newPlatforms, [oldPlatforms[0], oldPlatforms[2]]);
- notifyDone();
- });
- });
- });
- });
- });
-
- it("should move test runs from the merged platform to the destination platform", function () {
- submitReport(reportsForDifferentPlatforms, function () {
- var queryForRuns = 'SELECT * FROM test_runs, test_configurations, platforms WHERE run_config = config_id AND config_platform = platform_id ORDER by run_mean_cache';
- queryAndFetchAll(queryForRuns, [], function (oldTestRuns) {
- assert.equal(oldTestRuns.length, 4);
- assert.equal(oldTestRuns[0]['platform_name'], 'Mavericks');
- assert.equal(oldTestRuns[0]['run_sum_cache'], 6);
- assert.equal(oldTestRuns[1]['platform_name'], 'Mountain Lion');
- assert.equal(oldTestRuns[1]['run_sum_cache'], 12);
- assert.equal(oldTestRuns[2]['platform_name'], 'Mountain Lion');
- assert.equal(oldTestRuns[2]['run_sum_cache'], 18);
- assert.equal(oldTestRuns[3]['platform_name'], 'Trunk Mountain Lion');
- assert.equal(oldTestRuns[3]['run_sum_cache'], 24);
- httpPost('/admin/platforms.php', {'action': 'merge', 'id': oldTestRuns[1]['platform_id'], 'destination': oldTestRuns[3]['platform_id']}, function (response) {
- assert.equal(response.statusCode, 200);
- queryAndFetchAll(queryForRuns, [], function (newTestRuns) {
- assert.equal(newTestRuns.length, 4);
- assert.equal(newTestRuns[0]['run_id'], oldTestRuns[0]['run_id']);
- assert.equal(newTestRuns[0]['platform_name'], 'Mavericks');
- assert.equal(newTestRuns[0]['run_sum_cache'], 6);
- assert.equal(newTestRuns[1]['run_id'], oldTestRuns[1]['run_id']);
- assert.equal(newTestRuns[1]['platform_name'], 'Trunk Mountain Lion');
- assert.equal(newTestRuns[1]['run_sum_cache'], 12);
- assert.equal(newTestRuns[2]['run_id'], oldTestRuns[2]['run_id']);
- assert.equal(newTestRuns[2]['platform_name'], 'Trunk Mountain Lion');
- assert.equal(newTestRuns[2]['run_sum_cache'], 18);
- assert.equal(newTestRuns[3]['run_id'], oldTestRuns[3]['run_id']);
- assert.equal(newTestRuns[3]['platform_name'], 'Trunk Mountain Lion');
- assert.equal(newTestRuns[3]['run_sum_cache'], 24);
- assert.equal(newTestRuns[1]['run_config'], newTestRuns[3]['run_config']);
- notifyDone();
- });
- });
- });
- });
- });
-
- it("should move test configurations from the merged platform to the destination platform", function () {
- reportsForDifferentPlatforms[0]['tests'] = {"test": { "metrics": {"FrameRate": { "baseline": [[1, 1, 1], [1, 1, 1]] } } } };
- submitReport(reportsForDifferentPlatforms, function () {
- var queryForConfig = 'SELECT * from test_configurations, platforms, test_metrics'
- + ' where config_platform = platform_id and config_metric = metric_id and platform_name in ($1, $2) order by config_id';
- queryAndFetchAll(queryForConfig, [reportsForDifferentPlatforms[0]['platform'], reportsForDifferentPlatforms[2]['platform']], function (configs) {
- assert.equal(configs.length, 2);
- assert.equal(configs[0]['platform_name'], reportsForDifferentPlatforms[0]['platform']);
- assert.equal(configs[0]['metric_name'], 'FrameRate');
- assert.equal(configs[0]['config_type'], 'baseline');
- assert.equal(configs[1]['platform_name'], reportsForDifferentPlatforms[2]['platform']);
- assert.equal(configs[1]['metric_name'], 'FrameRate');
- assert.equal(configs[1]['config_type'], 'current');
- httpPost('/admin/platforms.php', {'action': 'merge', 'id': configs[0]['platform_id'], 'destination': configs[1]['platform_id']}, function (response) {
- assert.equal(response.statusCode, 200);
- queryAndFetchAll(queryForConfig, [reportsForDifferentPlatforms[0]['platform'], reportsForDifferentPlatforms[2]['platform']], function (newConfigs) {
- assert.equal(newConfigs.length, 2);
- assert.equal(newConfigs[0]['platform_name'], reportsForDifferentPlatforms[2]['platform']);
- assert.equal(newConfigs[0]['metric_name'], 'FrameRate');
- assert.equal(newConfigs[0]['config_type'], 'baseline');
- assert.equal(newConfigs[1]['platform_name'], reportsForDifferentPlatforms[2]['platform']);
- assert.equal(newConfigs[1]['metric_name'], 'FrameRate');
- assert.equal(newConfigs[1]['config_type'], 'current');
- notifyDone();
- });
- });
- });
- });
- });
-
-});
+++ /dev/null
-describe("/admin/reprocess-report", function () {
- var simpleReport = [{
- "buildNumber": "1986",
- "buildTime": "2013-02-28T10:12:03",
- "builderName": "someBuilder",
- "builderPassword": "somePassword",
- "platform": "Mountain Lion",
- "tests": {
- "test": {
- "metrics": {"FrameRate": { "current": [[1, 2, 3], [4, 5, 6]] }}
- },
- },
- }];
-
- function addBuilder(report, callback) {
- queryAndFetchAll('INSERT INTO builders (builder_name, builder_password_hash) values ($1, $2)',
- [report[0].builderName, sha256(report[0].builderPassword)], callback);
- }
-
- it("should add build", function () {
- addBuilder(simpleReport, function () {
- postJSON('/api/report/', simpleReport, function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
- queryAndFetchAll('SELECT * FROM builds', [], function (buildRows) {
- assert.equal(buildRows.length, 1);
- assert.equal(buildRows[0]['build_number'], 1986);
- queryAndFetchAll('SELECT * FROM reports', [], function (reportRows) {
- assert.equal(reportRows.length, 1);
- assert.equal(reportRows[0]['report_build_number'], 1986);
- queryAndFetchAll('UPDATE reports SET report_build = NULL; DELETE FROM builds; SELECT * FROM builds', [], function (buildRows) {
- assert.equal(buildRows.length, 0);
- var reportId = reportRows[0]['report_id'];
- httpGet('/admin/reprocess-report?report=' + reportId, function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
- queryAndFetchAll('SELECT * FROM builds', [], function (buildRows) {
- assert.equal(buildRows.length, 1);
- assert.equal(buildRows[0]['build_number'], 1986);
- notifyDone();
- });
- });
- });
- });
- });
- });
- });
- });
-
- it("should not duplicate the reprocessed report", function () {
- addBuilder(simpleReport, function () {
- postJSON('/api/report/', simpleReport, function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
- queryAndFetchAll('SELECT * FROM reports', [], function (originalReprotRows) {
- assert.equal(originalReprotRows.length, 1);
- queryAndFetchAll('UPDATE reports SET report_build = NULL; DELETE FROM builds', [], function () {
- httpGet('/admin/reprocess-report?report=' + originalReprotRows[0]['report_id'], function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
- queryAndFetchAll('SELECT * FROM reports', [], function (reportRows) {
- originalReprotRows[0]['report_committed_at'] = null;
- reportRows[0]['report_committed_at'] = null;
- assert.notEqual(originalReprotRows[0]['report_build'], reportRows[0]['report_build']);
- originalReprotRows[0]['report_build'] = null;
- reportRows[0]['report_build'] = null;
- assert.deepEqual(reportRows, originalReprotRows);
- notifyDone();
- });
- });
- });
- });
- });
- });
- });
-});
+++ /dev/null
-describe("/api/measurement-set", function () {
- function addBuilder(report, callback) {
- queryAndFetchAll('INSERT INTO builders (builder_name, builder_password_hash) values ($1, $2)',
- [report[0].builderName, sha256(report[0].builderPassword)], callback);
- }
-
- function queryPlatformAndMetric(platformName, metricName, callback) {
- queryAndFetchAll('SELECT * FROM platforms WHERE platform_name = $1', [platformName], function (platformRows) {
- queryAndFetchAll('SELECT * FROM test_metrics WHERE metric_name = $1', [metricName], function (metricRows) {
- callback(platformRows[0]['platform_id'], metricRows[0]['metric_id']);
- });
- });
- }
-
- function format(formatMap, row) {
- var result = {};
- for (var i = 0; i < formatMap.length; i++) {
- var key = formatMap[i];
- if (key == 'id' || key == 'build' || key == 'builder')
- continue;
- result[key] = row[i];
- }
- return result;
- }
-
- var clusterStart = config('clusterStart');
- clusterStart = +Date.UTC(clusterStart[0], clusterStart[1] - 1, clusterStart[2], clusterStart[3], clusterStart[4]);
-
- var clusterSize = config('clusterSize');
- var DAY = 24 * 3600 * 1000;
- var YEAR = 365.24 * DAY;
- var MONTH = 30 * DAY;
- clusterSize = clusterSize[0] * YEAR + clusterSize[1] * MONTH + clusterSize[2] * DAY;
-
- function clusterTime(index) { return new Date(clusterStart + clusterSize * index); }
-
- var reportWithBuildTime = [{
- "buildNumber": "123",
- "buildTime": clusterTime(7.8).toISOString(),
- "builderName": "someBuilder",
- "builderPassword": "somePassword",
- "platform": "Mountain Lion",
- "tests": {
- "Suite": {
- "tests": {
- "test1": {
- "metrics": {"Time": { "current": [1, 2, 3, 4, 5] }}
- },
- }
- },
- }}];
- reportWithBuildTime.startTime = +clusterTime(7);
-
- var reportWithRevision = [{
- "buildNumber": "124",
- "buildTime": "2013-02-28T15:34:51",
- "revisions": {
- "WebKit": {
- "revision": "144000",
- "timestamp": clusterTime(10.3).toISOString(),
- },
- },
- "builderName": "someBuilder",
- "builderPassword": "somePassword",
- "platform": "Mountain Lion",
- "tests": {
- "Suite": {
- "tests": {
- "test1": {
- "metrics": {"Time": { "current": [11, 12, 13, 14, 15] }}
- }
- }
- },
- }}];
-
- var reportWithNewRevision = [{
- "buildNumber": "125",
- "buildTime": "2013-02-28T21:45:17",
- "revisions": {
- "WebKit": {
- "revision": "160609",
- "timestamp": clusterTime(12.1).toISOString()
- },
- },
- "builderName": "someBuilder",
- "builderPassword": "somePassword",
- "platform": "Mountain Lion",
- "tests": {
- "Suite": {
- "tests": {
- "test1": {
- "metrics": {"Time": { "current": [16, 17, 18, 19, 20] }}
- }
- }
- },
- }}];
-
- var reportWithAncentRevision = [{
- "buildNumber": "126",
- "buildTime": "2013-02-28T23:07:25",
- "revisions": {
- "WebKit": {
- "revision": "137793",
- "timestamp": clusterTime(1.8).toISOString()
- },
- },
- "builderName": "someBuilder",
- "builderPassword": "somePassword",
- "platform": "Mountain Lion",
- "tests": {
- "Suite": {
- "tests": {
- "test1": {
- "metrics": {"Time": { "current": [21, 22, 23, 24, 25] }}
- }
- }
- },
- }}];
-
- it("should reject when platform ID is missing", function () {
- addBuilder(reportWithBuildTime, function () {
- postJSON('/api/report/', reportWithBuildTime, function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
- queryPlatformAndMetric('Mountain Lion', 'Time', function (platformId, metricId) {
- httpGet('/api/measurement-set/?metric=' + metricId, function (response) {
- assert.notEqual(JSON.parse(response.responseText)['status'], 'InvalidMetric');
- notifyDone();
- });
- });
-
- });
- });
- });
-
- it("should reject when metric ID is missing", function () {
- addBuilder(reportWithBuildTime, function () {
- postJSON('/api/report/', reportWithBuildTime, function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
- queryPlatformAndMetric('Mountain Lion', 'Time', function (platformId, metricId) {
- httpGet('/api/measurement-set/?platform=' + platformId, function (response) {
- assert.notEqual(JSON.parse(response.responseText)['status'], 'InvalidPlatform');
- notifyDone();
- });
- });
-
- });
- });
- });
-
- it("should reject an invalid platform name", function () {
- addBuilder(reportWithBuildTime, function () {
- postJSON('/api/report/', reportWithBuildTime, function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
- queryPlatformAndMetric('Mountain Lion', 'Time', function (platformId, metricId) {
- httpGet('/api/measurement-set/?platform=' + platformId + 'a&metric=' + metricId, function (response) {
- assert.equal(JSON.parse(response.responseText)['status'], 'InvalidPlatform');
- notifyDone();
- });
- });
-
- });
- });
- });
-
- it("should reject an invalid metric name", function () {
- addBuilder(reportWithBuildTime, function () {
- postJSON('/api/report/', reportWithBuildTime, function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
- queryPlatformAndMetric('Mountain Lion', 'Time', function (platformId, metricId) {
- httpGet('/api/measurement-set/?platform=' + platformId + '&metric=' + metricId + 'b', function (response) {
- assert.equal(JSON.parse(response.responseText)['status'], 'InvalidMetric');
- notifyDone();
- });
- });
-
- });
- });
- });
-
- it("should be able to retrieve a reported value", function () {
- addBuilder(reportWithBuildTime, function () {
- postJSON('/api/report/', reportWithBuildTime, function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
- queryPlatformAndMetric('Mountain Lion', 'Time', function (platformId, metricId) {
- httpGet('/api/measurement-set/?platform=' + platformId + '&metric=' + metricId, function (response) {
- try {
- var paresdResult = JSON.parse(response.responseText);
- } catch (error) {
- assert.fail(error, null, response.responseText);
- }
-
- var buildTime = +(new Date(reportWithBuildTime[0]['buildTime']));
-
- assert.deepEqual(Object.keys(paresdResult).sort(),
- ['clusterCount', 'clusterSize', 'clusterStart',
- 'configurations', 'elapsedTime', 'endTime', 'formatMap', 'lastModified', 'startTime', 'status']);
- assert.equal(paresdResult['status'], 'OK');
- assert.equal(paresdResult['clusterCount'], 1);
- assert.deepEqual(paresdResult['formatMap'], [
- 'id', 'mean', 'iterationCount', 'sum', 'squareSum', 'markedOutlier',
- 'revisions', 'commitTime', 'build', 'buildTime', 'buildNumber', 'builder']);
-
- assert.equal(paresdResult['startTime'], reportWithBuildTime.startTime);
- assert(typeof(paresdResult['lastModified']) == 'number', 'lastModified time should be a numeric');
-
- assert.deepEqual(Object.keys(paresdResult['configurations']), ['current']);
-
- var currentRows = paresdResult['configurations']['current'];
- assert.equal(currentRows.length, 1);
- assert.equal(currentRows[0].length, paresdResult['formatMap'].length);
- assert.deepEqual(format(paresdResult['formatMap'], currentRows[0]), {
- mean: 3,
- iterationCount: 5,
- sum: 15,
- squareSum: 55,
- markedOutlier: false,
- revisions: [],
- commitTime: buildTime,
- buildTime: buildTime,
- buildNumber: '123'});
- notifyDone();
- });
- });
-
- });
- });
- });
-
- it("should return return the right IDs for measurement, build, and builder", function () {
- addBuilder(reportWithBuildTime, function () {
- postJSON('/api/report/', reportWithBuildTime, function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
- queryPlatformAndMetric('Mountain Lion', 'Time', function (platformId, metricId) {
- queryAndFetchAll('SELECT * FROM test_runs', [], function (runs) {
- assert.equal(runs.length, 1);
- var measurementId = runs[0]['run_id'];
- queryAndFetchAll('SELECT * FROM builds', [], function (builds) {
- assert.equal(builds.length, 1);
- var buildId = builds[0]['build_id'];
- queryAndFetchAll('SELECT * FROM builders', [], function (builders) {
- assert.equal(builders.length, 1);
- var builderId = builders[0]['builder_id'];
- httpGet('/api/measurement-set/?platform=' + platformId + '&metric=' + metricId, function (response) {
- var paresdResult = JSON.parse(response.responseText);
- assert.equal(paresdResult['configurations']['current'].length, 1);
- var measurement = paresdResult['configurations']['current'][0];
- assert.equal(paresdResult['status'], 'OK');
- assert.equal(measurement[paresdResult['formatMap'].indexOf('id')], measurementId);
- assert.equal(measurement[paresdResult['formatMap'].indexOf('build')], buildId);
- assert.equal(measurement[paresdResult['formatMap'].indexOf('builder')], builderId);
- notifyDone();
- });
- });
- });
- });
- });
- });
- });
- });
-
- function postReports(reports, callback) {
- if (!reports.length)
- return callback();
-
- postJSON('/api/report/', reports[0], function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
-
- postReports(reports.slice(1), callback);
- });
- }
-
- function queryPlatformAndMetricWithRepository(platformName, metricName, repositoryName, callback) {
- queryPlatformAndMetric(platformName, metricName, function (platformId, metricId) {
- queryAndFetchAll('SELECT * FROM repositories WHERE repository_name = $1', [repositoryName], function (rows) {
- callback(platformId, metricId, rows[0]['repository_id']);
- });
- });
- }
-
- it("should order results by commit time", function () {
- addBuilder(reportWithBuildTime, function () {
- postReports([reportWithBuildTime, reportWithRevision], function () {
- queryPlatformAndMetricWithRepository('Mountain Lion', 'Time', 'WebKit', function (platformId, metricId, repositoryId) {
- httpGet('/api/measurement-set/?platform=' + platformId + '&metric=' + metricId, function (response) {
- var parsedResult = JSON.parse(response.responseText);
- assert.equal(parsedResult['status'], 'OK');
-
- var buildTime = +(new Date(reportWithBuildTime[0]['buildTime']));
- var revisionTime = +(new Date(reportWithRevision[0]['revisions']['WebKit']['timestamp']));
- var revisionBuildTime = +(new Date(reportWithRevision[0]['buildTime']));
-
- var currentRows = parsedResult['configurations']['current'];
- assert.equal(currentRows.length, 2);
- assert.deepEqual(format(parsedResult['formatMap'], currentRows[0]), {
- mean: 13,
- iterationCount: 5,
- sum: 65,
- squareSum: 855,
- markedOutlier: false,
- revisions: [[1, repositoryId, '144000', revisionTime]],
- commitTime: revisionTime,
- buildTime: revisionBuildTime,
- buildNumber: '124' });
- assert.deepEqual(format(parsedResult['formatMap'], currentRows[1]), {
- mean: 3,
- iterationCount: 5,
- sum: 15,
- squareSum: 55,
- markedOutlier: false,
- revisions: [],
- commitTime: buildTime,
- buildTime: buildTime,
- buildNumber: '123' });
- notifyDone();
- });
- });
- });
- });
- });
-
- function buildNumbers(parsedResult, config) {
- return parsedResult['configurations'][config].map(function (row) {
- return format(parsedResult['formatMap'], row)['buildNumber'];
- });
- }
-
- it("should include one data point after the current time range", function () {
- addBuilder(reportWithBuildTime, function () {
- postReports([reportWithAncentRevision, reportWithNewRevision], function () {
- queryPlatformAndMetricWithRepository('Mountain Lion', 'Time', 'WebKit', function (platformId, metricId, repositoryId) {
- httpGet('/api/measurement-set/?platform=' + platformId + '&metric=' + metricId, function (response) {
- var parsedResult = JSON.parse(response.responseText);
- assert.equal(parsedResult['status'], 'OK');
- assert.equal(parsedResult['clusterCount'], 2, 'should have two clusters');
- assert.deepEqual(buildNumbers(parsedResult, 'current'),
- [reportWithAncentRevision[0]['buildNumber'], reportWithNewRevision[0]['buildNumber']]);
- notifyDone();
- });
- });
- });
- });
- });
-
- // FIXME: This test assumes a cluster step of 2-3 months
- it("should always include one old data point before the current time range", function () {
- addBuilder(reportWithBuildTime, function () {
- postReports([reportWithBuildTime, reportWithAncentRevision], function () {
- queryPlatformAndMetricWithRepository('Mountain Lion', 'Time', 'WebKit', function (platformId, metricId, repositoryId) {
- httpGet('/api/measurement-set/?platform=' + platformId + '&metric=' + metricId, function (response) {
- var parsedResult = JSON.parse(response.responseText);
- assert.equal(parsedResult['status'], 'OK');
- assert.equal(parsedResult['clusterCount'], 2, 'should have two clusters');
-
- var currentRows = parsedResult['configurations']['current'];
- assert.equal(currentRows.length, 2, 'should contain at least two data points');
- assert.deepEqual(buildNumbers(parsedResult, 'current'),
- [reportWithAncentRevision[0]['buildNumber'], reportWithBuildTime[0]['buildNumber']]);
- notifyDone();
- });
- });
- });
- });
- });
-
- // FIXME: This test assumes a cluster step of 2-3 months
- it("should create cache results", function () {
- addBuilder(reportWithBuildTime, function () {
- postReports([reportWithAncentRevision, reportWithRevision, reportWithNewRevision], function () {
- queryPlatformAndMetricWithRepository('Mountain Lion', 'Time', 'WebKit', function (platformId, metricId, repositoryId) {
- httpGet('/api/measurement-set/?platform=' + platformId + '&metric=' + metricId, function (response) {
- var parsedResult = JSON.parse(response.responseText);
- assert.equal(parsedResult['status'], 'OK');
- var cachePrefix = '/data/measurement-set-' + platformId + '-' + metricId;
- httpGet(cachePrefix + '.json', function (response) {
- var parsedCachedResult = JSON.parse(response.responseText);
- assert.deepEqual(parsedResult, parsedCachedResult);
-
- httpGet(cachePrefix + '-' + parsedResult['startTime'] + '.json', function (response) {
- var parsedOldResult = JSON.parse(response.responseText);
-
- var oldBuildNumbers = buildNumbers(parsedOldResult, 'current');
- var newBuildNumbers = buildNumbers(parsedResult, 'current');
- assert(oldBuildNumbers.length >= 2, 'The old cluster should contain at least two data points');
- assert(newBuildNumbers.length >= 2, 'The new cluster should contain at least two data points');
- assert.deepEqual(oldBuildNumbers.slice(oldBuildNumbers.length - 2), newBuildNumbers.slice(0, 2),
- 'Two conseqcutive clusters should share two data points');
-
- notifyDone();
- });
- });
- });
- });
- });
- });
- });
-
-});
+++ /dev/null
-describe("/api/report-commits/", function () {
- var emptyReport = {
- "slaveName": "someSlave",
- "slavePassword": "somePassword",
- };
- var subversionCommit = {
- "slaveName": "someSlave",
- "slavePassword": "somePassword",
- "commits": [
- {
- "repository": "WebKit",
- "revision": "141977",
- "time": "2013-02-06T08:55:20.9Z",
- "author": {"name": "Commit Queue", "account": "commit-queue@webkit.org"},
- "message": "some message",
- }
- ],
- };
- var subversionInvalidCommit = {
- "slaveName": "someSlave",
- "slavePassword": "somePassword",
- "commits": [
- {
- "repository": "WebKit",
- "revision": "_141977",
- "time": "2013-02-06T08:55:20.9Z",
- "author": {"name": "Commit Queue", "account": "commit-queue@webkit.org"},
- "message": "some message",
- }
- ],
- };
- var subversionTwoCommits = {
- "slaveName": "someSlave",
- "slavePassword": "somePassword",
- "commits": [
- {
- "repository": "WebKit",
- "revision": "141977",
- "time": "2013-02-06T08:55:20.9Z",
- "author": {"name": "Commit Queue", "account": "commit-queue@webkit.org"},
- "message": "some message",
- },
- {
- "repository": "WebKit",
- "parent": "141977",
- "revision": "141978",
- "time": "2013-02-06T09:54:56.0Z",
- "author": {"name": "Mikhail Pozdnyakov", "account": "mikhail.pozdnyakov@intel.com"},
- "message": "another message",
- }
- ]
- }
-
- function addSlave(report, callback) {
- queryAndFetchAll('INSERT INTO build_slaves (slave_name, slave_password_hash) values ($1, $2)',
- [report.slaveName, sha256(report.slavePassword)], callback);
- }
-
- it("should reject error when slave name is missing", function () {
- postJSON('/api/report-commits/', {}, function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'MissingSlaveName');
- notifyDone();
- });
- });
-
- it("should reject when there are no slaves", function () {
- postJSON('/api/report-commits/', emptyReport, function (response) {
- assert.equal(response.statusCode, 200);
- assert.notEqual(JSON.parse(response.responseText)['status'], 'OK');
-
- queryAndFetchAll('SELECT COUNT(*) from commits', [], function (rows) {
- assert.equal(rows[0].count, 0);
- notifyDone();
- });
- });
- });
-
- it("should accept an empty report", function () {
- addSlave(emptyReport, function () {
- postJSON('/api/report-commits/', emptyReport, function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
- notifyDone();
- });
- });
- });
-
- it("should add a missing repository", function () {
- addSlave(subversionCommit, function () {
- postJSON('/api/report-commits/', subversionCommit, function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
- queryAndFetchAll('SELECT * FROM repositories', [], function (rows) {
- assert.equal(rows.length, 1);
- assert.equal(rows[0]['repository_name'], subversionCommit.commits[0]['repository']);
- notifyDone();
- });
- });
- });
- });
-
- it("should store a commit from a valid slave", function () {
- addSlave(subversionCommit, function () {
- postJSON('/api/report-commits/', subversionCommit, function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
- queryAndFetchAll('SELECT * FROM commits JOIN committers ON commit_committer = committer_id', [], function (rows) {
- assert.equal(rows.length, 1);
- var reportedData = subversionCommit.commits[0];
- assert.equal(rows[0]['commit_revision'], reportedData['revision']);
- assert.equal(rows[0]['commit_time'].toString(), new Date('2013-02-06 08:55:20.9').toString());
- assert.equal(rows[0]['committer_name'], reportedData['author']['name']);
- assert.equal(rows[0]['committer_account'], reportedData['author']['account']);
- assert.equal(rows[0]['commit_message'], reportedData['message']);
- notifyDone();
- });
- });
- });
- });
-
- it("should reject an invalid revision number", function () {
- addSlave(subversionCommit, function () {
- subversionCommit
- postJSON('/api/report-commits/', subversionInvalidCommit, function (response) {
- assert.equal(response.statusCode, 200);
- assert.notEqual(JSON.parse(response.responseText)['status'], 'OK');
- queryAndFetchAll('SELECT * FROM commits', [], function (rows) {
- assert.equal(rows.length, 0);
- notifyDone();
- });
- });
- });
- });
-
- it("should store two commits from a valid slave", function () {
- addSlave(subversionTwoCommits, function () {
- postJSON('/api/report-commits/', subversionTwoCommits, function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
- queryAndFetchAll('SELECT * FROM commits JOIN committers ON commit_committer = committer_id ORDER BY commit_time', [], function (rows) {
- assert.equal(rows.length, 2);
- var reportedData = subversionTwoCommits.commits[0];
- assert.equal(rows[0]['commit_revision'], reportedData['revision']);
- assert.equal(rows[0]['commit_time'].toString(), new Date('2013-02-06 08:55:20.9').toString());
- assert.equal(rows[0]['committer_name'], reportedData['author']['name']);
- assert.equal(rows[0]['committer_account'], reportedData['author']['account']);
- assert.equal(rows[0]['commit_message'], reportedData['message']);
- var reportedData = subversionTwoCommits.commits[1];
- assert.equal(rows[1]['commit_revision'], reportedData['revision']);
- assert.equal(rows[1]['commit_time'].toString(), new Date('2013-02-06 09:54:56.0').toString());
- assert.equal(rows[1]['committer_name'], reportedData['author']['name']);
- assert.equal(rows[1]['committer_account'], reportedData['author']['account']);
- assert.equal(rows[1]['commit_message'], reportedData['message']);
- notifyDone();
- });
- });
- });
- });
-
- it("should update an existing commit if there is one", function () {
- queryAndFetchAll('INSERT INTO repositories (repository_name) VALUES ($1) RETURNING *', ['WebKit'], function (repositories) {
- var repositoryId = repositories[0]['repository_id'];
- var reportedData = subversionCommit.commits[0];
- queryAndFetchAll('INSERT INTO commits (commit_repository, commit_revision, commit_time) VALUES ($1, $2, $3) RETURNING *',
- [repositoryId, reportedData['revision'], reportedData['time']], function (existingCommits) {
- var commitId = existingCommits[0]['commit_id'];
- assert.equal(existingCommits[0]['commit_message'], null);
- addSlave(subversionCommit, function () {
- postJSON('/api/report-commits/', subversionCommit, function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
- queryAndFetchAll('SELECT * FROM commits JOIN committers ON commit_committer = committer_id', [], function (rows) {
- assert.equal(rows.length, 1);
- var reportedData = subversionCommit.commits[0];
- assert.equal(rows[0]['committer_name'], reportedData['author']['name']);
- assert.equal(rows[0]['committer_account'], reportedData['author']['account']);
- assert.equal(rows[0]['commit_message'], reportedData['message']);
- notifyDone();
- });
- });
- });
- });
- });
- });
-
- it("should not update an unrelated commit", function () {
- queryAndFetchAll('INSERT INTO repositories (repository_name) VALUES ($1) RETURNING *', ['WebKit'], function (repositories) {
- var repositoryId = repositories[0]['repository_id'];
- var reportedData = subversionTwoCommits.commits[1];
- queryAndFetchAll('INSERT INTO commits (commit_repository, commit_revision, commit_time) VALUES ($1, $2, $3) RETURNING *',
- [repositoryId, reportedData['revision'], reportedData['time']], function (existingCommits) {
- reportedData = subversionTwoCommits.commits[0];
- queryAndFetchAll('INSERT INTO commits (commit_repository, commit_revision, commit_time) VALUES ($1, $2, $3) RETURNING *',
- [repositoryId, reportedData['revision'], reportedData['time']], function () {
- addSlave(subversionCommit, function () {
- postJSON('/api/report-commits/', subversionCommit, function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
- queryAndFetchAll('SELECT * FROM commits LEFT OUTER JOIN committers ON commit_committer = committer_id ORDER BY commit_time', [], function (rows) {
- assert.equal(rows.length, 2);
- assert.equal(rows[0]['committer_name'], reportedData['author']['name']);
- assert.equal(rows[0]['committer_account'], reportedData['author']['account']);
- assert.equal(rows[0]['commit_message'], reportedData['message']);
- assert.equal(rows[1]['committer_name'], null);
- assert.equal(rows[1]['committer_account'], null);
- assert.equal(rows[1]['commit_message'], null);
- notifyDone();
- });
- });
- });
- });
- });
- });
- });
-
- it("should update an existing committer if there is one", function () {
- queryAndFetchAll('INSERT INTO repositories (repository_id, repository_name) VALUES (1, \'WebKit\')', [], function () {
- var author = subversionCommit.commits[0]['author'];
- queryAndFetchAll('INSERT INTO committers (committer_repository, committer_account) VALUES (1, $1)', [author['account']], function () {
- addSlave(subversionCommit, function () {
- postJSON('/api/report-commits/', subversionCommit, function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
- queryAndFetchAll('SELECT * FROM committers', [], function (rows) {
- assert.equal(rows.length, 1);
- assert.equal(rows[0]['committer_name'], author['name']);
- assert.equal(rows[0]['committer_account'], author['account']);
- notifyDone();
- });
- });
- });
- });
- });
- });
-
-});
+++ /dev/null
-describe("/api/report", function () {
- var emptyReport = [{
- "buildNumber": "123",
- "buildTime": "2013-02-28T10:12:03.388304",
- "builderName": "someBuilder",
- "slaveName": "someSlave",
- "builderPassword": "somePassword",
- "platform": "Mountain Lion",
- "tests": {},
- "revisions": {
- "OS X": {
- "revision": "10.8.2 12C60"
- },
- "WebKit": {
- "revision": "141977",
- "timestamp": "2013-02-06T08:55:20.9Z"
- }
- }}];
-
- var emptySlaveReport = [{
- "buildNumber": "123",
- "buildTime": "2013-02-28T10:12:03.388304",
- "builderName": "someBuilder",
- "builderPassword": "somePassword",
- "slaveName": "someSlave",
- "slavePassword": "otherPassword",
- "platform": "Mountain Lion",
- "tests": {},
- "revisions": {
- "OS X": {
- "revision": "10.8.2 12C60"
- },
- "WebKit": {
- "revision": "141977",
- "timestamp": "2013-02-06T08:55:20.9Z"
- }
- }}];
-
- function addBuilder(report, callback) {
- queryAndFetchAll('INSERT INTO builders (builder_name, builder_password_hash) values ($1, $2)',
- [report[0].builderName, sha256(report[0].builderPassword)], callback);
- }
-
- function addSlave(report, callback) {
- queryAndFetchAll('INSERT INTO build_slaves (slave_name, slave_password_hash) values ($1, $2)',
- [report[0].slaveName, sha256(report[0].slavePassword)], callback);
- }
-
- it("should reject error when builder name is missing", function () {
- postJSON('/api/report/', [{"buildTime": "2013-02-28T10:12:03.388304"}], function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'MissingBuilderName');
- notifyDone();
- });
- });
-
- it("should reject error when build time is missing", function () {
- addBuilder(emptyReport, function () {
- postJSON('/api/report/', [{"builderName": "someBuilder", "builderPassword": "somePassword"}], function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'MissingBuildTime');
- notifyDone();
- });
- });
- });
-
- it("should reject when there are no builders", function () {
- postJSON('/api/report/', emptyReport, function (response) {
- assert.equal(response.statusCode, 200);
- assert.notEqual(JSON.parse(response.responseText)['status'], 'OK');
- assert.equal(JSON.parse(response.responseText)['failureStored'], false);
- assert.equal(JSON.parse(response.responseText)['processedRuns'], 0);
-
- queryAndFetchAll('SELECT COUNT(*) from reports', [], function (rows) {
- assert.equal(rows[0].count, 0);
- notifyDone();
- });
- });
- });
-
- it("should reject a report without a builder password", function () {
- addBuilder(emptyReport, function () {
- var report = [{
- "buildNumber": "123",
- "buildTime": "2013-02-28T10:12:03.388304",
- "builderName": "someBuilder",
- "tests": {},
- "revisions": {}}];
- postJSON('/api/report/', report, function (response) {
- assert.equal(response.statusCode, 200);
- assert.notEqual(JSON.parse(response.responseText)['status'], 'OK');
- assert.equal(JSON.parse(response.responseText)['failureStored'], false);
- assert.equal(JSON.parse(response.responseText)['processedRuns'], 0);
-
- queryAndFetchAll('SELECT COUNT(*) from reports', [], function (rows) {
- assert.equal(rows[0].count, 0);
- notifyDone();
- });
- });
- });
- });
-
- it("should store a report from a valid builder", function () {
- addBuilder(emptyReport, function () {
- postJSON('/api/report/', emptyReport, function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
- assert.equal(JSON.parse(response.responseText)['failureStored'], false);
- assert.equal(JSON.parse(response.responseText)['processedRuns'], 1);
- queryAndFetchAll('SELECT COUNT(*) from reports', [], function (rows) {
- assert.equal(rows[0].count, 1);
- notifyDone();
- });
- });
- });
- });
-
- it("should treat the slave password as the builder password if there is no matching slave", function () {
- addBuilder(emptyReport, function () {
- emptyReport[0]['slavePassword'] = emptyReport[0]['builderPassword'];
- delete emptyReport[0]['builderPassword'];
- postJSON('/api/report/', emptyReport, function (response) {
- emptyReport[0]['builderPassword'] = emptyReport[0]['slavePassword'];
- delete emptyReport[0]['slavePassword'];
-
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
- assert.equal(JSON.parse(response.responseText)['failureStored'], false);
- assert.equal(JSON.parse(response.responseText)['processedRuns'], 1);
- queryAndFetchAll('SELECT COUNT(*) from reports', [], function (rows) {
- assert.equal(rows[0].count, 1);
- notifyDone();
- });
- });
- });
- });
-
- it("should store a report from a valid slave", function () {
- addSlave(emptySlaveReport, function () {
- postJSON('/api/report/', emptySlaveReport, function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
- assert.equal(JSON.parse(response.responseText)['failureStored'], false);
- assert.equal(JSON.parse(response.responseText)['processedRuns'], 1);
- queryAndFetchAll('SELECT COUNT(*) from reports', [], function (rows) {
- assert.equal(rows[0].count, 1);
- notifyDone();
- });
- });
- });
- });
-
- it("should store the builder name but not the builder password", function () {
- addBuilder(emptyReport, function () {
- postJSON('/api/report/', emptyReport, function (response) {
- queryAndFetchAll('SELECT report_content from reports', [], function (rows) {
- var storedContent = JSON.parse(rows[0].report_content);
- assert.equal(storedContent['builderName'], emptyReport[0]['builderName']);
- assert(!('builderPassword' in storedContent));
- notifyDone();
- });
- });
- });
- });
-
- it("should add a slave if there isn't one and the report was authenticated by a builder", function () {
- addBuilder(emptyReport, function () {
- postJSON('/api/report/', emptyReport, function (response) {
- queryAndFetchAll('SELECT * from build_slaves', [], function (rows) {
- assert.strictEqual(rows[0].slave_name, emptyReport[0].slaveName);
- notifyDone();
- });
- });
- });
- });
-
- it("should add a builder if there isn't one and the report was authenticated by a slave", function () {
- addSlave(emptySlaveReport, function () {
- postJSON('/api/report/', emptySlaveReport, function (response) {
- queryAndFetchAll('SELECT * from builders', [], function (rows) {
- assert.strictEqual(rows[0].builder_name, emptyReport[0].builderName);
- notifyDone();
- });
- });
- });
- });
-
- it("should add a build", function () {
- addBuilder(emptyReport, function () {
- postJSON('/api/report/', emptyReport, function (response) {
- queryAndFetchAll('SELECT * from builds', [], function (rows) {
- assert.strictEqual(rows[0].build_number, 123);
- notifyDone();
- });
- });
- });
- });
-
- it("should add the platform", function () {
- addBuilder(emptyReport, function () {
- postJSON('/api/report/', emptyReport, function (response) {
- queryAndFetchAll('SELECT * from platforms', [], function (rows) {
- assert.strictEqual(rows[0].platform_name, 'Mountain Lion');
- notifyDone();
- });
- });
- });
- });
-
- it("should add repositories and build revisions", function () {
- addBuilder(emptyReport, function () {
- postJSON('/api/report/', emptyReport, function (response) {
- queryAndFetchAll('SELECT * FROM repositories', [], function (rows) {
- assert.deepEqual(rows.map(function (row) { return row['repository_name']; }), ['OS X', 'WebKit']);
-
- var repositoryIdToName = {};
- rows.forEach(function (row) { repositoryIdToName[row['repository_id']] = row['repository_name']; });
- queryAndFetchAll('SELECT * FROM build_commits, commits WHERE build_commit = commit_id', [], function (rows) {
- var repositoryNameToRevisionRow = {};
- rows.forEach(function (row) {
- repositoryNameToRevisionRow[repositoryIdToName[row['commit_repository']]] = row;
- });
- assert.equal(repositoryNameToRevisionRow['OS X']['commit_revision'], '10.8.2 12C60');
- assert.equal(repositoryNameToRevisionRow['WebKit']['commit_revision'], '141977');
- assert.equal(repositoryNameToRevisionRow['WebKit']['commit_time'].toString(),
- new Date('2013-02-06 08:55:20.9').toString());
- notifyDone();
- });
- });
- });
- });
- });
-
- it("should not create a duplicate build for the same build number if build times are close", function () {
- addBuilder(emptyReport, function () {
- emptyReport[0]['buildTime'] = '2013-02-28T10:12:04';
- postJSON('/api/report/', emptyReport, function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
-
- emptyReport[0]['buildTime'] = '2013-02-28T10:22:03';
- postJSON('/api/report/', emptyReport, function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
-
- queryAndFetchAll('SELECT * FROM builds', [], function (rows) {
- assert.equal(rows.length, 1);
- notifyDone();
- });
- });
- });
- });
- });
-
- it("should create distinct builds for the same build number if build times are far apart", function () {
- addBuilder(emptyReport, function () {
- emptyReport[0]['buildTime'] = '2013-02-28T10:12:03';
- postJSON('/api/report/', emptyReport, function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
-
- queryAndFetchAll('SELECT * FROM builds', [], function (rows) {
- assert.equal(rows.length, 1);
-
- emptyReport[0]['buildTime'] = '2014-01-20T22:23:34';
- postJSON('/api/report/', emptyReport, function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
-
- queryAndFetchAll('SELECT * FROM builds', [], function (rows) {
- assert.equal(rows.length, 2);
- notifyDone();
- });
- });
- });
- });
- });
- });
-
- it("should reject a report with mismatching revision info", function () {
- addBuilder(emptyReport, function () {
- emptyReport[0]['revisions'] = {
- "WebKit": {
- "revision": "141977",
- "timestamp": "2013-02-06T08:55:20.96Z"
- }
- }
- postJSON('/api/report/', emptyReport, function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
-
- emptyReport[0]['revisions'] = {
- "WebKit": {
- "revision": "150000",
- "timestamp": "2013-05-13T10:50:29.6Z"
- }
- }
- postJSON('/api/report/', emptyReport, function (response) {
- assert.equal(response.statusCode, 200);
- assert.notEqual(JSON.parse(response.responseText)['status'], 'OK');
- assert(response.responseText.indexOf('141977') >= 0);
- assert(response.responseText.indexOf('150000') >= 0);
- assert.equal(JSON.parse(response.responseText)['failureStored'], true);
- assert.equal(JSON.parse(response.responseText)['processedRuns'], 0);
- notifyDone();
- });
- });
- });
- });
-
- var reportWithTwoLevelsOfAggregations = [{
- "buildNumber": "123",
- "buildTime": "2013-02-28T10:12:03.388304",
- "builderName": "someBuilder",
- "builderPassword": "somePassword",
- "platform": "Mountain Lion",
- "tests": {
- "DummyPageLoading": {
- "metrics": {"Time": { "aggregators" : ["Arithmetic"], "current": [300, 310, 320, 330] }},
- "tests": {
- "apple.com": {
- "metrics": {"Time": { "current": [500, 510, 520, 530] }},
- "url": "http://www.apple.com"
- },
- "webkit.org": {
- "metrics": {"Time": { "current": [100, 110, 120, 130] }},
- "url": "http://www.webkit.org"
- }
- }
- },
- "DummyBenchmark": {
- "metrics": {"Time": ["Arithmetic"]},
- "tests": {
- "DOM": {
- "metrics": {"Time": ["Geometric", "Arithmetic"]},
- "tests": {
- "ModifyNodes": {"metrics": {"Time": { "current": [[11, 12, 13, 14, 15], [16, 17, 18, 19, 20], [21, 22, 23, 24, 25]] }}},
- "TraverseNodes": {"metrics": {"Time": { "current": [[31, 32, 33, 34, 35], [36, 37, 38, 39, 40], [41, 42, 43, 44, 45]] }}}
- }
- },
- "CSS": {"metrics": {"Time": { "current": [[101, 102, 103, 104, 105], [106, 107, 108, 109, 110], [111, 112, 113, 114, 115]] }}}
- }
- }
- },
- "revisions": {
- "OS X": {
- "revision": "10.8.2 12C60"
- },
- "WebKit": {
- "revision": "141977",
- "timestamp": "2013-02-06T08:55:20.9Z"
- }
- }}];
-
- function addBuilderAndMeanAggregators(report, callback) {
- addBuilder(report, function () {
- queryAndFetchAll('INSERT INTO aggregators (aggregator_name, aggregator_definition) values ($1, $2)',
- ['Arithmetic', 'values.reduce(function (a, b) { return a + b; }) / values.length'], function () {
- queryAndFetchAll('INSERT INTO aggregators (aggregator_name, aggregator_definition) values ($1, $2)',
- ['Geometric', 'Math.pow(values.reduce(function (a, b) { return a * b; }), 1 / values.length)'], callback);
- });
- });
- }
-
- function fetchRunForMetric(testName, metricName,callback) {
- queryAndFetchAll('SELECT * FROM test_runs WHERE run_config IN'
- + '(SELECT config_id FROM test_configurations, test_metrics, tests WHERE config_metric = metric_id AND metric_test = test_id AND'
- + 'test_name = $1 AND metric_name = $2)',
- ['Arithmetic', 'values.reduce(function (a, b) { return a + b; }) / values.length'], function () {
- queryAndFetchAll('INSERT INTO aggregators (aggregator_name, aggregator_definition) values ($1, $2)',
- ['Geometric', 'Math.pow(values.reduce(function (a, b) { return a * b; }), 1 / values.length)'], callback);
- });
- }
-
- it("should not reject when aggregators are missing", function () {
- addBuilder(reportWithTwoLevelsOfAggregations, function () {
- postJSON('/api/report/', reportWithTwoLevelsOfAggregations, function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
- assert.equal(JSON.parse(response.responseText)['failureStored'], false);
- notifyDone();
- });
- });
- });
-
- it("should add tests", function () {
- addBuilderAndMeanAggregators(reportWithTwoLevelsOfAggregations, function () {
- postJSON('/api/report/', reportWithTwoLevelsOfAggregations, function (response) {
- assert.equal(response.statusCode, 200);
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
- assert.equal(JSON.parse(response.responseText)['failureStored'], false);
- queryAndFetchAll('SELECT * FROM tests', [], function (rows) {
- assert.deepEqual(rows.map(function (row) { return row['test_name']; }).sort(),
- ['CSS', 'DOM', 'DummyBenchmark', 'DummyPageLoading', 'ModifyNodes', 'TraverseNodes', 'apple.com', 'webkit.org']);
- emptyReport[0].tests = {};
- notifyDone();
- });
- });
- });
- });
-
- it("should add metrics", function () {
- addBuilderAndMeanAggregators(reportWithTwoLevelsOfAggregations, function () {
- postJSON('/api/report/', reportWithTwoLevelsOfAggregations, function (response) {
- queryAndFetchAll('SELECT * FROM tests, test_metrics LEFT JOIN aggregators ON metric_aggregator = aggregator_id WHERE metric_test = test_id',
- [], function (rows) {
- var testNameToMetrics = {};
- rows.forEach(function (row) {
- if (!(row['test_name'] in testNameToMetrics))
- testNameToMetrics[row['test_name']] = new Array;
- testNameToMetrics[row['test_name']].push([row['metric_name'], row['aggregator_name']]);
- });
- assert.deepEqual(testNameToMetrics['CSS'], [['Time', null]]);
- assert.deepEqual(testNameToMetrics['DOM'].sort(), [['Time', 'Arithmetic'], ['Time', 'Geometric']]);
- assert.deepEqual(testNameToMetrics['DummyBenchmark'], [['Time', 'Arithmetic']]);
- assert.deepEqual(testNameToMetrics['DummyPageLoading'], [['Time', 'Arithmetic']]);
- assert.deepEqual(testNameToMetrics['ModifyNodes'], [['Time', null]]);
- assert.deepEqual(testNameToMetrics['TraverseNodes'], [['Time', null]]);
- assert.deepEqual(testNameToMetrics['apple.com'], [['Time', null]]);
- assert.deepEqual(testNameToMetrics['webkit.org'], [['Time', null]]);
- notifyDone();
- });
- });
- });
- });
-
- function fetchTestRunIterationsForMetric(testName, metricName, callback) {
- queryAndFetchAll('SELECT * FROM tests, test_metrics, test_configurations, test_runs WHERE metric_test = test_id AND config_metric = metric_id'
- + ' AND run_config = config_id AND test_name = $1 AND metric_name = $2', [testName, metricName], function (runRows) {
- assert.equal(runRows.length, 1);
- var run = runRows[0];
- queryAndFetchAll('SELECT * FROM run_iterations WHERE iteration_run = $1 ORDER BY iteration_order', [run['run_id']], function (iterationRows) {
- callback(run, iterationRows);
- });
- });
- }
-
- it("should store run values", function () {
- addBuilderAndMeanAggregators(reportWithTwoLevelsOfAggregations, function () {
- postJSON('/api/report/', reportWithTwoLevelsOfAggregations, function (response) {
- fetchTestRunIterationsForMetric('apple.com', 'Time', function (run, iterations) {
- var runId = run['run_id'];
- assert.deepEqual(iterations, [
- {iteration_run: runId, iteration_order: 0, iteration_group: null, iteration_value: 500, iteration_relative_time: null},
- {iteration_run: runId, iteration_order: 1, iteration_group: null, iteration_value: 510, iteration_relative_time: null},
- {iteration_run: runId, iteration_order: 2, iteration_group: null, iteration_value: 520, iteration_relative_time: null},
- {iteration_run: runId, iteration_order: 3, iteration_group: null, iteration_value: 530, iteration_relative_time: null}]);
- var sum = 500 + 510 + 520 + 530;
- assert.equal(run['run_mean_cache'], sum / iterations.length);
- assert.equal(run['run_sum_cache'], sum);
- assert.equal(run['run_square_sum_cache'], 500 * 500 + 510 * 510 + 520 * 520 + 530 * 530);
-
- fetchTestRunIterationsForMetric('CSS', 'Time', function (run, iterations) {
- var runId = run['run_id'];
- assert.deepEqual(iterations, [
- {iteration_run: runId, iteration_order: 0, iteration_group: 0, iteration_value: 101, iteration_relative_time: null},
- {iteration_run: runId, iteration_order: 1, iteration_group: 0, iteration_value: 102, iteration_relative_time: null},
- {iteration_run: runId, iteration_order: 2, iteration_group: 0, iteration_value: 103, iteration_relative_time: null},
- {iteration_run: runId, iteration_order: 3, iteration_group: 0, iteration_value: 104, iteration_relative_time: null},
- {iteration_run: runId, iteration_order: 4, iteration_group: 0, iteration_value: 105, iteration_relative_time: null},
- {iteration_run: runId, iteration_order: 5, iteration_group: 1, iteration_value: 106, iteration_relative_time: null},
- {iteration_run: runId, iteration_order: 6, iteration_group: 1, iteration_value: 107, iteration_relative_time: null},
- {iteration_run: runId, iteration_order: 7, iteration_group: 1, iteration_value: 108, iteration_relative_time: null},
- {iteration_run: runId, iteration_order: 8, iteration_group: 1, iteration_value: 109, iteration_relative_time: null},
- {iteration_run: runId, iteration_order: 9, iteration_group: 1, iteration_value: 110, iteration_relative_time: null},
- {iteration_run: runId, iteration_order: 10, iteration_group: 2, iteration_value: 111, iteration_relative_time: null},
- {iteration_run: runId, iteration_order: 11, iteration_group: 2, iteration_value: 112, iteration_relative_time: null},
- {iteration_run: runId, iteration_order: 12, iteration_group: 2, iteration_value: 113, iteration_relative_time: null},
- {iteration_run: runId, iteration_order: 13, iteration_group: 2, iteration_value: 114, iteration_relative_time: null},
- {iteration_run: runId, iteration_order: 14, iteration_group: 2, iteration_value: 115, iteration_relative_time: null}]);
- var sum = 0;
- var squareSum = 0;
- for (var value = 101; value <= 115; ++value) {
- sum += value;
- squareSum += value * value;
- }
- assert.equal(run['run_mean_cache'], sum / iterations.length);
- assert.equal(run['run_sum_cache'], sum);
- assert.equal(run['run_square_sum_cache'], squareSum);
- notifyDone();
- });
- });
- });
- });
- });
-
- it("should store aggregated run values", function () {
- addBuilderAndMeanAggregators(reportWithTwoLevelsOfAggregations, function () {
- postJSON('/api/report/', reportWithTwoLevelsOfAggregations, function (response) {
- fetchTestRunIterationsForMetric('DummyPageLoading', 'Time', function (run, iterations) {
- var runId = run['run_id'];
- var expectedValues = [(500 + 100) / 2, (510 + 110) / 2, (520 + 120) / 2, (530 + 130) / 2];
- assert.deepEqual(iterations, [
- {iteration_run: runId, iteration_order: 0, iteration_group: null, iteration_value: expectedValues[0], iteration_relative_time: null},
- {iteration_run: runId, iteration_order: 1, iteration_group: null, iteration_value: expectedValues[1], iteration_relative_time: null},
- {iteration_run: runId, iteration_order: 2, iteration_group: null, iteration_value: expectedValues[2], iteration_relative_time: null},
- {iteration_run: runId, iteration_order: 3, iteration_group: null, iteration_value: expectedValues[3], iteration_relative_time: null}]);
- var sum = expectedValues.reduce(function (sum, value) { return sum + value; }, 0);
- assert.equal(run['run_mean_cache'], sum / iterations.length);
- assert.equal(run['run_sum_cache'], sum);
- assert.equal(run['run_square_sum_cache'], expectedValues.reduce(function (sum, value) { return sum + value * value; }, 0));
- notifyDone();
- });
- });
- });
- });
-
- it("should be able to compute the aggregation of aggregated values", function () {
- addBuilderAndMeanAggregators(reportWithTwoLevelsOfAggregations, function () {
- postJSON('/api/report/', reportWithTwoLevelsOfAggregations, function (response) {
- fetchTestRunIterationsForMetric('DummyBenchmark', 'Time', function (run, iterations) {
- var expectedIterations = [];
- var sum = 0;
- var squareSum = 0;
- for (var i = 0; i < 15; ++i) {
- var value = i + 1;
- var DOMMean = ((10 + value) + (30 + value)) / 2;
- var expectedValue = (DOMMean + 100 + value) / 2;
- sum += expectedValue;
- squareSum += expectedValue * expectedValue;
- expectedIterations.push({iteration_run: run['run_id'],
- iteration_order: i,
- iteration_group: Math.floor(i / 5),
- iteration_value: expectedValue,
- iteration_relative_time: null});
- }
- assert.deepEqual(iterations, expectedIterations);
- assert.equal(run['run_mean_cache'], sum / iterations.length);
- assert.equal(run['run_sum_cache'], sum);
- assert.equal(run['run_square_sum_cache'], squareSum);
- notifyDone();
- });
- });
- });
- });
-
- var reportWithSameSubtestName = [{
- "buildNumber": "123",
- "buildTime": "2013-02-28T10:12:03.388304",
- "builderName": "someBuilder",
- "builderPassword": "somePassword",
- "platform": "Mountain Lion",
- "tests": {
- "Suite1": {
- "tests": {
- "test1": {
- "metrics": {"Time": { "current": [1, 2, 3, 4, 5] }}
- },
- "test2": {
- "metrics": {"Time": { "current": [6, 7, 8, 9, 10] }}
- }
- }
- },
- "Suite2": {
- "tests": {
- "test1": {
- "metrics": {"Time": { "current": [11, 12, 13, 14, 15] }}
- },
- "test2": {
- "metrics": {"Time": { "current": [16, 17, 18, 19, 20] }}
- }
- }
- }
- }}];
-
- it("should be able to add a report with same subtest name", function () {
- addBuilderAndMeanAggregators(reportWithSameSubtestName, function () {
- postJSON('/api/report/', reportWithSameSubtestName, function (response) {
- assert.equal(response.statusCode, 200);
- try {
- JSON.parse(response.responseText);
- } catch (error) {
- assert.fail(error, null, response.responseText);
- }
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
- assert.equal(JSON.parse(response.responseText)['failureStored'], false);
- notifyDone();
- });
- });
- });
-
- it("should be able to reuse the same test rows", function () {
- addBuilderAndMeanAggregators(reportWithSameSubtestName, function () {
- postJSON('/api/report/', reportWithSameSubtestName, function (response) {
- assert.equal(response.statusCode, 200);
- queryAndFetchAll('SELECT * FROM tests', [], function (testRows) {
- assert.equal(testRows.length, 6);
- reportWithSameSubtestName.buildNumber = "125";
- reportWithSameSubtestName.buildTime = "2013-02-28T12:17:24.1";
-
- postJSON('/api/report/', reportWithSameSubtestName, function (response) {
- assert.equal(response.statusCode, 200);
- queryAndFetchAll('SELECT * FROM tests', [], function (testRows) {
- assert.equal(testRows.length, 6);
- notifyDone();
- });
- });
- });
- });
- });
- });
-
- var reportWithSameSingleValue = [{
- "buildNumber": "123",
- "buildTime": "2013-02-28T10:12:03.388304",
- "builderName": "someBuilder",
- "builderPassword": "somePassword",
- "platform": "Mountain Lion",
- "tests": {
- "suite": {
- "metrics": {"Combined": ["Arithmetic"]},
- "tests": {
- "test1": {
- "metrics": {"Combined": { "current": 3 }}
- },
- "test2": {
- "metrics": {"Combined": { "current": 7 }}
- }
- }
- },
- }}];
-
- it("should be able to add a report with single value results", function () {
- addBuilderAndMeanAggregators(reportWithSameSingleValue, function () {
- postJSON('/api/report/', reportWithSameSingleValue, function (response) {
- assert.equal(response.statusCode, 200);
- try {
- JSON.parse(response.responseText);
- } catch (error) {
- assert.fail(error, null, response.responseText);
- }
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
- assert.equal(JSON.parse(response.responseText)['failureStored'], false);
- fetchTestRunIterationsForMetric('test1', 'Combined', function (run, iterations) {
- assert.equal(run['run_iteration_count_cache'], 1);
- assert.equal(run['run_mean_cache'], 3);
- assert.equal(run['run_sum_cache'], 3);
- assert.equal(run['run_square_sum_cache'], 9);
- fetchTestRunIterationsForMetric('suite', 'Combined', function (run, iterations) {
- assert.equal(run['run_iteration_count_cache'], 1);
- assert.equal(run['run_mean_cache'], 5);
- assert.equal(run['run_sum_cache'], 5);
- assert.equal(run['run_square_sum_cache'], 25);
- notifyDone();
- });
- });
- });
- });
- });
-
- var reportWithSameValuePairs = [{
- "buildNumber": "123",
- "buildTime": "2013-02-28T10:12:03.388304",
- "builderName": "someBuilder",
- "builderPassword": "somePassword",
- "platform": "Mountain Lion",
- "tests": {
- "test": {
- "metrics": {"FrameRate": { "current": [[[0, 4], [100, 5], [205, 3]]] }}
- },
- },
- }];
-
- it("should be able to add a report with (relative time, value) pairs", function () {
- addBuilderAndMeanAggregators(reportWithSameValuePairs, function () {
- postJSON('/api/report/', reportWithSameValuePairs, function (response) {
- assert.equal(response.statusCode, 200);
- try {
- JSON.parse(response.responseText);
- } catch (error) {
- assert.fail(error, null, response.responseText);
- }
- assert.equal(JSON.parse(response.responseText)['status'], 'OK');
- assert.equal(JSON.parse(response.responseText)['failureStored'], false);
- fetchTestRunIterationsForMetric('test', 'FrameRate', function (run, iterations) {
- assert.equal(run['run_iteration_count_cache'], 3);
- assert.equal(run['run_mean_cache'], 4);
- assert.equal(run['run_sum_cache'], 12);
- assert.equal(run['run_square_sum_cache'], 16 + 25 + 9);
- var runId = run['run_id'];
- assert.deepEqual(iterations, [
- {iteration_run: runId, iteration_order: 0, iteration_group: null, iteration_value: 4, iteration_relative_time: 0},
- {iteration_run: runId, iteration_order: 1, iteration_group: null, iteration_value: 5, iteration_relative_time: 100},
- {iteration_run: runId, iteration_order: 2, iteration_group: null, iteration_value: 3, iteration_relative_time: 205}]);
- notifyDone();
- });
- });
- });
- });
-
- var reportsUpdatingDifferentTests = [
- [{
- "buildNumber": "123",
- "buildTime": "2013-02-28T10:12:03",
- "builderName": "someBuilder",
- "builderPassword": "somePassword",
- "platform": "Mountain Lion",
- "tests": {"test1": {"metrics": {"Time": {"current": 3}}}}
- }],
- [{
- "buildNumber": "124",
- "buildTime": "2013-02-28T11:31:21",
- "builderName": "someBuilder",
- "builderPassword": "somePassword",
- "platform": "Mountain Lion",
- "tests": {"test2": {"metrics": {"Time": {"current": 3}}}}
- }],
- [{
- "buildNumber": "125",
- "buildTime": "2013-02-28T12:45:34",
- "builderName": "someBuilder",
- "builderPassword": "somePassword",
- "platform": "Mountain Lion",
- "tests": {"test1": {"metrics": {"Time": {"current": 3}}}}
- }],
- ];
-
- function fetchTestConfig(testName, metricName, callback) {
- queryAndFetchAll('SELECT * FROM tests, test_metrics, test_configurations WHERE test_id = metric_test AND metric_id = config_metric'
- + ' AND test_name = $1 AND metric_name = $2', [testName, metricName], function (runRows) {
- assert.equal(runRows.length, 1);
- callback(runRows[0]);
- });
- }
-
- it("should update the last modified date of test configurations with new runs", function () {
- addBuilder(reportsUpdatingDifferentTests[0], function () {
- postJSON('/api/report/', reportsUpdatingDifferentTests[0], function (response) {
- assert.equal(response.statusCode, 200);
- fetchTestConfig('test1', 'Time', function (originalConfig) {
- postJSON('/api/report/', reportsUpdatingDifferentTests[2], function (response) {
- assert.equal(response.statusCode, 200);
- fetchTestConfig('test1', 'Time', function (config) {
- assert.notEqual(+originalConfig['config_runs_last_modified'], +config['config_runs_last_modified']);
- notifyDone();
- });
- });
- });
- });
- });
- });
-
- it("should update the last modified date of unrelated test configurations", function () {
- addBuilder(reportsUpdatingDifferentTests[0], function () {
- postJSON('/api/report/', reportsUpdatingDifferentTests[0], function (response) {
- assert.equal(response.statusCode, 200);
- fetchTestConfig('test1', 'Time', function (originalConfig) {
- postJSON('/api/report/', reportsUpdatingDifferentTests[1], function (response) {
- assert.equal(response.statusCode, 200);
- fetchTestConfig('test1', 'Time', function (config) {
- assert.equal(+originalConfig['config_runs_last_modified'], +config['config_runs_last_modified']);
- notifyDone();
- });
- });
- });
- });
- });
- });
-});
"use strict";
-var pg = require('pg');
-var config = require('./config.js');
+const pg = require('pg');
+const config = require('./config.js');
class Database {
constructor(databaseName)
let connectionString = `tcp://${username}:${password}@${host}:${port}/${this._databaseName}`;
let client = new pg.Client(connectionString);
- if (!options || !options.keepAlive) {
- client.on('drain', function () {
- client.end();
- });
- }
+ if (!options || !options.keepAlive)
+ client.on('drain', this.disconnect.bind(this));
this._client = client;
});
}
+ selectAll(table, columnToSortBy)
+ {
+ return this.selectRows(table, {}, {sortBy: columnToSortBy});
+ }
+
+ selectFirstRow(table, params, columnToSortBy)
+ {
+ return this.selectRows(table, params, {sortBy: columnToSortBy, limit: 1}).then(function (rows) {
+ return rows[0];
+ });
+ }
+
+ selectRows(table, params, options)
+ {
+ let prefix = '';
+ if (table in tableToPrefixMap)
+ prefix = tableToPrefixMap[table] + '_';
+
+ options = options || {};
+
+ let columnNames = [];
+ let placeholders = [];
+ let values = [];
+ for (let name in params) {
+ columnNames.push(prefix + name);
+ placeholders.push('$' + columnNames.length);
+ values.push(params[name]);
+ }
+
+ let qualifier = '';
+ if (columnNames.length)
+ qualifier += ` WHERE (${columnNames.join(',')}) = (${placeholders.join(',')})`;
+ qualifier += ` ORDER BY ${prefix}${options.sortBy || 'id'}`;
+ if (options && options.limit)
+ qualifier += ` LIMIT ${options.limit}`;
+
+ return this.query(`SELECT * FROM ${table}${qualifier}`, values).then(function (result) {
+ return result.rows.map(function (row) {
+ let formattedResult = {};
+ for (let columnName in row) {
+ let formattedName = columnName;
+ if (formattedName.startsWith(prefix))
+ formattedName = formattedName.substring(prefix.length);
+ formattedResult[formattedName] = row[columnName];
+ }
+ return formattedResult;
+ });
+ });
+ }
+
insert(table, parameters)
{
let columnNames = [];
}
}
-let tableToPrefixMap = {
+const tableToPrefixMap = {
'aggregators': 'aggregator',
'analysis_tasks': 'task',
'analysis_test_groups': 'testgroup',
'build_triggerables': 'triggerable',
'build_requests': 'request',
'build_slaves': 'slave',
+ 'builds': 'build',
'builders': 'builder',
'commits': 'commit',
+ 'committers': 'committer',
'test_configurations': 'config',
'test_metrics': 'metric',
+ 'test_runs': 'run',
'tests': 'test',
'tracker_repositories': 'tracrepo',
'platforms': 'platform',
+ 'reports': 'report',
'repositories': 'repository',
'root_sets': 'rootset',
'roots': 'root',
+ 'run_iterations': 'iteration',
}
if (typeof module != 'undefined')