Diff
Modified: trunk/Websites/perf.webkit.org/ChangeLog (198641 => 198642)
--- trunk/Websites/perf.webkit.org/ChangeLog 2016-03-24 19:56:58 UTC (rev 198641)
+++ trunk/Websites/perf.webkit.org/ChangeLog 2016-03-24 20:17:01 UTC (rev 198642)
@@ -1,5 +1,82 @@
2016-03-23 Ryosuke Niwa <[email protected]>
+ Add mocha server tests for /api/build-requests
+ https://bugs.webkit.org/show_bug.cgi?id=155831
+
+ Reviewed by Chris Dumez.
+
+ Added the new mocha.js based server-tests for /api/build-requests. The new harness automatically:
+ - starts a new Apache instance
+ - switches the database during testing via setting an environmental variable
+ - backups and restores public/data directory during testing
+
+ As a result, developer no longer has to manually setup Apache, edit config.json manually to use
+ a testing database, or run /api/manifest.php to re-generate the manifest file after testing.
+
+ This patch also makes ID resolution optional on /api/build-requests so that v3 model based syncing
+ scripts can re-use the same code as the v3 UI to process the JSON. tools/sync-with-buildbot.py has
+ been modified to use this option (useLegacyIdResolution).
+
+ * config.json: Added configurations for the test httpd server.
+ * init-database.sql: Don't error when tables and types don't exist (when database is empty).
+ * public/api/build-requests.php:
+ (main): Made the ID resolution optional with useLegacyIdResolution. Also removed "updates" from the
+ results JSON since it's never used.
+ * public/include/build-requests-fetcher.php:
+ (BuildRequestsFetcher::__construct):
+ (BuildRequestsFetcher::fetch_roots_for_set_if_needed): Fixed the bug that we would include the same
+ commit multiple times for each root set.
+ * public/include/db.php:
+ (config): If present, use ORG_WEBKIT_PERF_CONFIG_PATH instead of Websites/perf.webkit.org/config.json.
+ * server-tests: Added.
+ * server-tests/api-build-requests-tests.js: Added. Tests for /api/build-requests.
+ (.addMockData):
+ * server-tests/resources: Added.
+ * server-tests/resources/test-server.conf: Added. Apache configuration file for testing.
+ * server-tests/resources/test-server.js: Added.
+ (TestSever): Added.
+ (TestSever.prototype.start): Added.
+ (TestSever.prototype.stop): Added.
+ (TestSever.prototype.remoteAPI): Added. Configures RemoteAPI to be used with the test sever.
+ (TestSever.prototype.database): Added. Returns Database configured to use the test database.
+ (TestSever.prototype._constructTestConfig): Creates config.json for testing. The file is generated by
+ _start and db.php's config() reads it from the environmental variable: ORG_WEBKIT_PERF_CONFIG_PATH.
+ (TestSever.prototype._ensureDataDirectory): Renames public/data to public/original-data if exists,
+ and creates a new empty public/data.
+ (TestSever.prototype._restoreDataDirectory): Deletes public/data and renames public/original-data
+ back to public/data.
+ (TestSever.prototype._ensureTestDatabase): Drops the test database if exists and creates a new one.
+ (TestSever.prototype.initDatabase): Run init-database.sql to start each test with a consistent state.
+ (TestSever.prototype._executePgsqlCommand): Executes a postgres command line tool such as psql.
+ (TestSever.prototype._determinePgsqlDirectory): Finds the directory that contains psql.
+ (TestSever.prototype._startApache): Starts an Apache instance for testing.
+ (TestSever.prototype._stopApache): Stops the Apache instance for testing.
+ (TestSever.prototype._waitForPid): Waits for the Apache pid file to appear or disappear.
+ (before): Start the test server at the beginning.
+ (beforeEach): Re-initialize all tables before each test.
+ (after): Stop the test server at the end.
+ * tools/js/config.js:
+ (Config.prototype.path):
+ (Config.prototype.serverRoot): Added. The path to Websites/perf.webkit.org/public/.
+ (Config.prototype.pathFromRoot): Added. Resolves a path from Websites/perf.webkit.org.
+ * tools/js/database.js:
+ (Database): Now optionally takes the database name to use a different database during testing.
+ (Database.prototype.connect):
+ (Database.prototype.query): Added.
+ (Database.prototype.insert): Added.
+ (tableToPrefixMap): Maps table name to its prefix. Used by Database.insert.
+ * tools/js/remote.js: Added.
+ (RemoteAPI): Added. This is node.js equivalent of RemoteAPI in public/v3/remote.js.
+ (RemoteAPI.prototype.configure): Added.
+ (RemoteAPI.prototype.fetchJSON): Added.
+ (RemoteAPI.prototype.fetchJSONWithStatus): Added.
+ (RemoteAPI.prototype.sendHttpRequest): Added.
+ * tools/sync-with-buildbot.py:
+ (main): Use useLegacyIdResolution as this script relies on the legacy behavior.
+ * unit-tests/checkconfig.js: pg was never directly used in this test.
+
+2016-03-23 Ryosuke Niwa <[email protected]>
+
Delete a file that was supposed to be removed in r198614 for real.
* unit-tests/resources/v3-models.js: Removed.
Modified: trunk/Websites/perf.webkit.org/config.json (198641 => 198642)
--- trunk/Websites/perf.webkit.org/config.json 2016-03-24 19:56:58 UTC (rev 198641)
+++ trunk/Websites/perf.webkit.org/config.json 2016-03-24 20:17:01 UTC (rev 198642)
@@ -14,9 +14,14 @@
"maintenanceMode": false,
"maintenanceDirectory": "reported/",
"testServer": {
- "hostname": "localhost",
- "port": 80
+ "port": 8180,
+ "config": "/tmp/org.webkit.perf.test-config.json",
+ "httpdConfig": "server-tests/resources/test-server.conf",
+ "httpdPID": "/tmp/org.webkit.perf.test-httpd.pid",
+ "httpdErrorLog": "server-tests/resources/test-server.log",
+ "httpdMutexDir": "/tmp/org.webkit.perf.tests/"
},
+ "testDatabaseName": "test-db",
"clusterStart": [2000, 1, 1, 0, 0],
"clusterSize": [0, 2, 0],
"cacheDirectory": "public/data/remote-cache/",
Modified: trunk/Websites/perf.webkit.org/init-database.sql (198641 => 198642)
--- trunk/Websites/perf.webkit.org/init-database.sql 2016-03-24 19:56:58 UTC (rev 198641)
+++ trunk/Websites/perf.webkit.org/init-database.sql 2016-03-24 20:17:01 UTC (rev 198642)
@@ -1,34 +1,34 @@
-DROP TABLE run_iterations CASCADE;
-DROP TABLE test_runs CASCADE;
-DROP TABLE test_configurations CASCADE;
-DROP TYPE test_configuration_type CASCADE;
-DROP TABLE aggregators CASCADE;
-DROP TABLE builds CASCADE;
-DROP TABLE committers CASCADE;
-DROP TABLE commits CASCADE;
-DROP TABLE build_commits CASCADE;
-DROP TABLE build_slaves CASCADE;
-DROP TABLE builders CASCADE;
-DROP TABLE repositories CASCADE;
-DROP TABLE platforms CASCADE;
-DROP TABLE test_metrics CASCADE;
-DROP TABLE tests CASCADE;
-DROP TABLE reports CASCADE;
-DROP TABLE tracker_repositories CASCADE;
-DROP TABLE bug_trackers CASCADE;
-DROP TABLE task_commits CASCADE;
-DROP TABLE analysis_tasks CASCADE;
-DROP TABLE analysis_strategies CASCADE;
-DROP TYPE analysis_task_result_type CASCADE;
-DROP TABLE build_triggerables CASCADE;
-DROP TABLE triggerable_configurations CASCADE;
-DROP TABLE triggerable_repositories CASCADE;
-DROP TABLE bugs CASCADE;
-DROP TABLE analysis_test_groups CASCADE;
-DROP TABLE root_sets CASCADE;
-DROP TABLE roots CASCADE;
-DROP TABLE build_requests CASCADE;
-DROP TYPE build_request_status_type CASCADE;
+DROP TABLE IF EXISTS run_iterations CASCADE;
+DROP TABLE IF EXISTS test_runs CASCADE;
+DROP TABLE IF EXISTS test_configurations CASCADE;
+DROP TYPE IF EXISTS test_configuration_type CASCADE;
+DROP TABLE IF EXISTS aggregators CASCADE;
+DROP TABLE IF EXISTS builds CASCADE;
+DROP TABLE IF EXISTS committers CASCADE;
+DROP TABLE IF EXISTS commits CASCADE;
+DROP TABLE IF EXISTS build_commits CASCADE;
+DROP TABLE IF EXISTS build_slaves CASCADE;
+DROP TABLE IF EXISTS builders CASCADE;
+DROP TABLE IF EXISTS repositories CASCADE;
+DROP TABLE IF EXISTS platforms CASCADE;
+DROP TABLE IF EXISTS test_metrics CASCADE;
+DROP TABLE IF EXISTS tests CASCADE;
+DROP TABLE IF EXISTS reports CASCADE;
+DROP TABLE IF EXISTS tracker_repositories CASCADE;
+DROP TABLE IF EXISTS bug_trackers CASCADE;
+DROP TABLE IF EXISTS task_commits CASCADE;
+DROP TABLE IF EXISTS analysis_tasks CASCADE;
+DROP TABLE IF EXISTS analysis_strategies CASCADE;
+DROP TYPE IF EXISTS analysis_task_result_type CASCADE;
+DROP TABLE IF EXISTS build_triggerables CASCADE;
+DROP TABLE IF EXISTS triggerable_configurations CASCADE;
+DROP TABLE IF EXISTS triggerable_repositories CASCADE;
+DROP TABLE IF EXISTS bugs CASCADE;
+DROP TABLE IF EXISTS analysis_test_groups CASCADE;
+DROP TABLE IF EXISTS root_sets CASCADE;
+DROP TABLE IF EXISTS roots CASCADE;
+DROP TABLE IF EXISTS build_requests CASCADE;
+DROP TYPE IF EXISTS build_request_status_type CASCADE;
CREATE TABLE platforms (
Modified: trunk/Websites/perf.webkit.org/public/api/build-requests.php (198641 => 198642)
--- trunk/Websites/perf.webkit.org/public/api/build-requests.php 2016-03-24 19:56:58 UTC (rev 198641)
+++ trunk/Websites/perf.webkit.org/public/api/build-requests.php 2016-03-24 20:17:01 UTC (rev 198642)
@@ -30,11 +30,11 @@
$requests_fetcher->fetch_incomplete_requests_for_triggerable($triggerable['triggerable_id']);
}
+ $resolve_id = array_get($_GET, 'useLegacyIdResolution');
exit_with_success(array(
- 'buildRequests' => $requests_fetcher->results_with_resolved_ids(),
+ 'buildRequests' => $resolve_id ? $requests_fetcher->results_with_resolved_ids() : $requests_fetcher->results(),
'rootSets' => $requests_fetcher->root_sets(),
'roots' => $requests_fetcher->roots(),
- 'updates' => $updates,
));
}
Modified: trunk/Websites/perf.webkit.org/public/include/build-requests-fetcher.php (198641 => 198642)
--- trunk/Websites/perf.webkit.org/public/include/build-requests-fetcher.php 2016-03-24 19:56:58 UTC (rev 198641)
+++ trunk/Websites/perf.webkit.org/public/include/build-requests-fetcher.php 2016-03-24 20:17:01 UTC (rev 198642)
@@ -7,6 +7,7 @@
$this->db = $db;
$this->rows = null;
$this->root_sets = array();
+ $this->roots_by_id = array();
$this->roots = array();
$this->root_sets_by_id = array();
}
@@ -96,11 +97,18 @@
$revision = $row['commit_revision'];
$commit_time = $row['commit_time'];
array_push($root_ids, $row['commit_id']);
+
+ $root_id = $row['commit_id'];
+ if (array_key_exists($root_id, $this->roots_by_id))
+ continue;
+
array_push($this->roots, array(
- 'id' => $row['commit_id'],
+ 'id' => $root_id,
'repository' => $repository_id,
'revision' => $revision,
'time' => Database::to_js_time($commit_time)));
+
+ $this->roots_by_id[$root_id] = TRUE;
}
$this->root_sets_by_id[$root_set_id] = TRUE;
Modified: trunk/Websites/perf.webkit.org/public/include/db.php (198641 => 198642)
--- trunk/Websites/perf.webkit.org/public/include/db.php 2016-03-24 19:56:58 UTC (rev 198641)
+++ trunk/Websites/perf.webkit.org/public/include/db.php 2016-03-24 20:17:01 UTC (rev 198642)
@@ -27,12 +27,16 @@
$_config = NULL;
-define('CONFIG_DIR', dirname(__FILE__) . '/../../');
+define('CONFIG_DIR', realpath(dirname(__FILE__) . '/../../'));
function config($key, $default = NULL) {
global $_config;
- if (!$_config)
- $_config = json_decode(file_get_contents(CONFIG_DIR . 'config.json'), true);
+ if (!$_config) {
+ $file_path = getenv('ORG_WEBKIT_PERF_CONFIG_PATH');
+ if (!$file_path)
+ $file_path = CONFIG_DIR . 'config.json';
+ $_config = json_decode(file_get_contents($file_path), true);
+ }
return array_get($_config, $key, $default);
}
Added: trunk/Websites/perf.webkit.org/server-tests/api-build-requests-tests.js (0 => 198642)
--- trunk/Websites/perf.webkit.org/server-tests/api-build-requests-tests.js (rev 0)
+++ trunk/Websites/perf.webkit.org/server-tests/api-build-requests-tests.js 2016-03-24 20:17:01 UTC (rev 198642)
@@ -0,0 +1,174 @@
+'use strict';
+
+let assert = require('assert');
+let TestServer = require('./resources/test-server.js');
+
+describe('/api/build-requests', function () {
+ this.timeout(10000);
+
+ it('should return "TriggerableNotFound" when the database is empty', function (done) {
+ TestServer.remoteAPI().fetchJSON('/api/build-requests/build-webkit').then(function (content) {
+ assert.equal(content['status'], 'TriggerableNotFound');
+ done();
+ }).catch(done);
+ });
+
+ it('should return an empty list when there are no build requests', function (done) {
+ TestServer.database().connect().then(function () {
+ return TestServer.database().insert('build_triggerables', {name: 'build-webkit'});
+ }).then(function () {
+ return TestServer.remoteAPI().fetchJSON('/api/build-requests/build-webkit');
+ }).then(function (content) {
+ assert.equal(content['status'], 'OK');
+ assert.deepEqual(content['buildRequests'], []);
+ assert.deepEqual(content['rootSets'], []);
+ assert.deepEqual(content['roots'], []);
+ assert.deepEqual(Object.keys(content).sort(), ['buildRequests', 'rootSets', 'roots', 'status']);
+ done();
+ }).catch(done);
+ });
+
+ function addMockData(db)
+ {
+ return Promise.all([
+ db.insert('build_triggerables', {id: 1, name: 'build-webkit'}),
+ db.insert('repositories', {id: 9, name: 'OS X'}),
+ db.insert('repositories', {id: 11, name: 'WebKit'}),
+ db.insert('commits', {id: 87832, repository: 9, revision: '10.11 15A284'}),
+ db.insert('commits', {id: 93116, repository: 11, revision: '191622', time: new Date(1445945816878)}),
+ db.insert('commits', {id: 96336, repository: 11, revision: '192736', time: new Date(1448225325650)}),
+ db.insert('platforms', {id: 65, name: 'some platform'}),
+ db.insert('tests', {id: 200, name: 'some test'}),
+ db.insert('test_metrics', {id: 300, test: 200, name: 'some metric'}),
+ db.insert('root_sets', {id: 401}),
+ db.insert('roots', {set: 401, commit: 87832}),
+ db.insert('roots', {set: 401, commit: 93116}),
+ db.insert('root_sets', {id: 402}),
+ db.insert('roots', {set: 402, commit: 87832}),
+ db.insert('roots', {set: 402, commit: 96336}),
+ db.insert('analysis_tasks', {id: 500, platform: 65, metric: 300, name: 'some task'}),
+ db.insert('analysis_test_groups', {id: 600, task: 500, name: 'some test group'}),
+ db.insert('build_requests', {id: 700, triggerable: 1, platform: 65, test: 200, group: 600, order: 0, root_set: 401}),
+ db.insert('build_requests', {id: 701, triggerable: 1, platform: 65, test: 200, group: 600, order: 1, root_set: 402}),
+ db.insert('build_requests', {id: 702, triggerable: 1, platform: 65, test: 200, group: 600, order: 2, root_set: 401}),
+ db.insert('build_requests', {id: 703, triggerable: 1, platform: 65, test: 200, group: 600, order: 3, root_set: 402}),
+ ]);
+ }
+
+ it('should return build requets associated with a given triggerable with appropriate roots and rootSets', function (done) {
+ let db = TestServer.database();
+ db.connect().then(function () {
+ return addMockData(db);
+ }).then(function () {
+ return TestServer.remoteAPI().fetchJSONWithStatus('/api/build-requests/build-webkit');
+ }).then(function (content) {
+ assert.deepEqual(Object.keys(content).sort(), ['buildRequests', 'rootSets', 'roots', 'status']);
+
+ assert.equal(content['rootSets'].length, 2);
+ assert.equal(content['rootSets'][0].id, 401);
+ assert.deepEqual(content['rootSets'][0].roots, ['87832', '93116']);
+ assert.equal(content['rootSets'][1].id, 402);
+ assert.deepEqual(content['rootSets'][1].roots, ['87832', '96336']);
+
+ assert.equal(content['roots'].length, 3);
+ assert.equal(content['roots'][0].id, 87832);
+ assert.equal(content['roots'][0].repository, '9');
+ assert.equal(content['roots'][0].revision, '10.11 15A284');
+ assert.equal(content['roots'][1].id, 93116);
+ assert.equal(content['roots'][1].repository, '11');
+ assert.equal(content['roots'][1].revision, '191622');
+ assert.equal(content['roots'][2].id, 96336);
+ assert.equal(content['roots'][2].repository, '11');
+ assert.equal(content['roots'][2].revision, '192736');
+
+ assert.equal(content['buildRequests'].length, 4);
+ assert.deepEqual(content['buildRequests'][0].id, 700);
+ assert.deepEqual(content['buildRequests'][0].order, 0);
+ assert.deepEqual(content['buildRequests'][0].platform, '65');
+ assert.deepEqual(content['buildRequests'][0].rootSet, 401);
+ assert.deepEqual(content['buildRequests'][0].status, 'pending');
+ assert.deepEqual(content['buildRequests'][0].test, '200');
+
+ assert.deepEqual(content['buildRequests'][1].id, 701);
+ assert.deepEqual(content['buildRequests'][1].order, 1);
+ assert.deepEqual(content['buildRequests'][1].platform, '65');
+ assert.deepEqual(content['buildRequests'][1].rootSet, 402);
+ assert.deepEqual(content['buildRequests'][1].status, 'pending');
+ assert.deepEqual(content['buildRequests'][1].test, '200');
+
+ assert.deepEqual(content['buildRequests'][2].id, 702);
+ assert.deepEqual(content['buildRequests'][2].order, 2);
+ assert.deepEqual(content['buildRequests'][2].platform, '65');
+ assert.deepEqual(content['buildRequests'][2].rootSet, 401);
+ assert.deepEqual(content['buildRequests'][2].status, 'pending');
+ assert.deepEqual(content['buildRequests'][2].test, '200');
+
+ assert.deepEqual(content['buildRequests'][3].id, 703);
+ assert.deepEqual(content['buildRequests'][3].order, 3);
+ assert.deepEqual(content['buildRequests'][3].platform, '65');
+ assert.deepEqual(content['buildRequests'][3].rootSet, 402);
+ assert.deepEqual(content['buildRequests'][3].status, 'pending');
+ assert.deepEqual(content['buildRequests'][3].test, '200');
+ done();
+ }).catch(done);
+ });
+
+ it('should return support useLegacyIdResolution option', function (done) {
+ let db = TestServer.database();
+ db.connect().then(function () {
+ return addMockData(db);
+ }).then(function () {
+ return TestServer.remoteAPI().fetchJSONWithStatus('/api/build-requests/build-webkit?useLegacyIdResolution=true');
+ }).then(function (content) {
+ assert.deepEqual(Object.keys(content).sort(), ['buildRequests', 'rootSets', 'roots', 'status']);
+
+ assert.equal(content['rootSets'].length, 2);
+ assert.equal(content['rootSets'][0].id, 401);
+ assert.deepEqual(content['rootSets'][0].roots, ['87832', '93116']);
+ assert.equal(content['rootSets'][1].id, 402);
+ assert.deepEqual(content['rootSets'][1].roots, ['87832', '96336']);
+
+ assert.equal(content['roots'].length, 3);
+ assert.equal(content['roots'][0].id, 87832);
+ assert.equal(content['roots'][0].repository, 'OS X');
+ assert.equal(content['roots'][0].revision, '10.11 15A284');
+ assert.equal(content['roots'][1].id, 93116);
+ assert.equal(content['roots'][1].repository, 'WebKit');
+ assert.equal(content['roots'][1].revision, '191622');
+ assert.equal(content['roots'][2].id, 96336);
+ assert.equal(content['roots'][2].repository, 'WebKit');
+ assert.equal(content['roots'][2].revision, '192736');
+
+ assert.equal(content['buildRequests'].length, 4);
+ assert.deepEqual(content['buildRequests'][0].id, 700);
+ assert.deepEqual(content['buildRequests'][0].order, 0);
+ assert.deepEqual(content['buildRequests'][0].platform, 'some platform');
+ assert.deepEqual(content['buildRequests'][0].rootSet, 401);
+ assert.deepEqual(content['buildRequests'][0].status, 'pending');
+ assert.deepEqual(content['buildRequests'][0].test, ['some test']);
+
+ assert.deepEqual(content['buildRequests'][1].id, 701);
+ assert.deepEqual(content['buildRequests'][1].order, 1);
+ assert.deepEqual(content['buildRequests'][1].platform, 'some platform');
+ assert.deepEqual(content['buildRequests'][1].rootSet, 402);
+ assert.deepEqual(content['buildRequests'][1].status, 'pending');
+ assert.deepEqual(content['buildRequests'][1].test, ['some test']);
+
+ assert.deepEqual(content['buildRequests'][2].id, 702);
+ assert.deepEqual(content['buildRequests'][2].order, 2);
+ assert.deepEqual(content['buildRequests'][2].platform, 'some platform');
+ assert.deepEqual(content['buildRequests'][2].rootSet, 401);
+ assert.deepEqual(content['buildRequests'][2].status, 'pending');
+ assert.deepEqual(content['buildRequests'][2].test, ['some test']);
+
+ assert.deepEqual(content['buildRequests'][3].id, 703);
+ assert.deepEqual(content['buildRequests'][3].order, 3);
+ assert.deepEqual(content['buildRequests'][3].platform, 'some platform');
+ assert.deepEqual(content['buildRequests'][3].rootSet, 402);
+ assert.deepEqual(content['buildRequests'][3].status, 'pending');
+ assert.deepEqual(content['buildRequests'][3].test, ['some test']);
+ done();
+ }).catch(done);
+ });
+
+});
Added: trunk/Websites/perf.webkit.org/server-tests/resources/test-server.conf (0 => 198642)
--- trunk/Websites/perf.webkit.org/server-tests/resources/test-server.conf (rev 0)
+++ trunk/Websites/perf.webkit.org/server-tests/resources/test-server.conf 2016-03-24 20:17:01 UTC (rev 198642)
@@ -0,0 +1,57 @@
+ServerRoot "/usr"
+Listen 8080
+
+LoadModule authn_core_module libexec/apache2/mod_authn_core.so
+LoadModule authz_core_module libexec/apache2/mod_authz_core.so
+LoadModule deflate_module libexec/apache2/mod_deflate.so
+LoadModule log_config_module libexec/apache2/mod_log_config.so
+LoadModule log_forensic_module libexec/apache2/mod_log_forensic.so
+LoadModule env_module libexec/apache2/mod_env.so
+LoadModule headers_module libexec/apache2/mod_headers.so
+LoadModule setenvif_module libexec/apache2/mod_setenvif.so
+LoadModule mime_module libexec/apache2/mod_mime.so
+LoadModule unixd_module libexec/apache2/mod_unixd.so
+LoadModule status_module libexec/apache2/mod_status.so
+LoadModule negotiation_module libexec/apache2/mod_negotiation.so
+LoadModule dir_module libexec/apache2/mod_dir.so
+LoadModule alias_module libexec/apache2/mod_alias.so
+LoadModule rewrite_module libexec/apache2/mod_rewrite.so
+LoadModule php5_module libexec/apache2/libphp5.so
+
+<Directory />
+ Options Indexes FollowSymLinks MultiViews
+ AllowOverride None
+ Require all granted
+</Directory>
+
+<IfModule dir_module>
+ DirectoryIndex index.html index.php
+</IfModule>
+
+LogLevel warn
+
+<IfModule log_config_module>
+ LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
+ LogFormat "%h %l %u %t \"%r\" %>s %b" common
+
+ <IfModule logio_module>
+ LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
+ </IfModule>
+
+ CustomLog |/usr/bin/tee common
+ ErrorLog |/usr/bin/tee
+</IfModule>
+
+<IfModule mime_module>
+ AddType text/html .html
+ AddType text/html .htm
+ AddType text/css .css
+ AddType text/_javascript_ .js
+</IfModule>
+
+<IfModule php5_module>
+ AddType application/x-httpd-php .php
+ AddType application/x-httpd-php-source .phps
+</IfModule>
+
+Include /private/etc/apache2/extra/httpd-mpm.conf
Added: trunk/Websites/perf.webkit.org/server-tests/resources/test-server.js (0 => 198642)
--- trunk/Websites/perf.webkit.org/server-tests/resources/test-server.js (rev 0)
+++ trunk/Websites/perf.webkit.org/server-tests/resources/test-server.js 2016-03-24 20:17:01 UTC (rev 198642)
@@ -0,0 +1,228 @@
+'use strict';
+
+let assert = require('assert');
+let childProcess = require('child_process');
+let fs = require('fs');
+let path = require('path');
+
+let Config = require('../../tools/js/config.js');
+let Database = require('../../tools/js/database.js');
+let RemoteAPI = require('../../tools/js/remote.js').RemoteAPI;
+
+let TestServer = (new class TestServer {
+ constructor()
+ {
+ this._pidFile = null;
+ this._testConfigPath = Config.path('testServer.config');
+ this._dataDirectory = Config.path('dataDirectory');
+ this._backupDataPath = null;
+ this._pidWaitStart = null;
+ this._shouldLog = false;
+ this._pgsqlDirectory = null;
+ this._server = null;
+
+ this._databaseName = Config.value('testDatabaseName');
+ this._databaseUser = Config.value('database.username');
+ this._databaseHost = Config.value('database.host');
+ this._databasePort = Config.value('database.port');
+ this._database = null;
+ }
+
+ start()
+ {
+ let testConfigContent = this._constructTestConfig(this._dataDirectory);
+ fs.writeFileSync(this._testConfigPath, JSON.stringify(testConfigContent, null, ' '));
+
+ this._ensureTestDatabase();
+ this._ensureDataDirectory();
+
+ return this._startApache();
+ }
+
+ stop()
+ {
+ this._restoreDataDirectory();
+
+ return this._stopApache();
+ }
+
+ remoteAPI()
+ {
+ assert(this._server);
+ RemoteAPI.configure(this._server);
+ return RemoteAPI;
+ }
+
+ database()
+ {
+ assert(this._databaseName);
+ if (!this._database)
+ this._database = new Database(this._databaseName);
+ return this._database;
+ }
+
+ _constructTestConfig(dataDirectory)
+ {
+ return {
+ 'siteTitle': 'Test Dashboard',
+ 'debug': true,
+ 'jsonCacheMaxAge': 600,
+ 'dataDirectory': dataDirectory,
+ 'database': {
+ 'host': Config.value('database.host'),
+ 'port': Config.value('database.port'),
+ 'username': Config.value('database.username'),
+ 'password': Config.value('database.password'),
+ 'name': Config.value('testDatabaseName'),
+ },
+ 'universalSlavePassword': null,
+ 'maintenanceMode': false,
+ 'clusterStart': [2000, 1, 1, 0, 0],
+ 'clusterSize': [0, 2, 0],
+ 'defaultDashboard': [[]],
+ 'dashboards': {}
+ }
+ }
+
+ _ensureDataDirectory()
+ {
+
+ let backupPath = path.resolve(this._dataDirectory, '../original-data');
+ if (fs.existsSync(this._dataDirectory)) {
+ assert.ok(!fs.existsSync(backupPath), `Both ${this._dataDirectory} and ${backupPath} exist. Cannot make a backup of data`);
+ fs.rename(this._dataDirectory, backupPath);
+ this._backupDataPath = backupPath;
+ } else {
+ if (fs.existsSync(backupPath)) // Assume this is a backup from the last failed run
+ this._backupDataPath = backupPath;
+ fs.mkdirSync(this._dataDirectory, 0o755);
+ }
+ }
+
+ _restoreDataDirectory()
+ {
+ childProcess.execFileSync('rm', ['-rf', this._dataDirectory]);
+ if (this._backupDataPath)
+ fs.rename(this._backupDataPath, this._dataDirectory);
+ }
+
+ _ensureTestDatabase()
+ {
+ this._executePgsqlCommand('dropdb');
+ this._executePgsqlCommand('createdb');
+ this._executePgsqlCommand('psql', ['--command', `grant all privileges on database "${this._databaseName}" to "${this._databaseUser}";`]);
+ this.initDatabase();
+ }
+
+ initDatabase()
+ {
+ if (this._database)
+ this._database.disconnect();
+ this._database = null;
+
+ let initFilePath = Config.pathFromRoot('init-database.sql');
+ this._executePgsqlCommand('psql', ['--username', this._databaseUser, '--file', initFilePath],
+ {stdio: ['ignore', 'ignore', 'ignore']});
+ }
+
+ _executePgsqlCommand(command, args, options)
+ {
+ if (!this._pgsqlDirectory)
+ this._pgsqlDirectory = this._determinePgsqlDirectory();
+ childProcess.execFileSync(path.resolve(this._pgsqlDirectory, command),
+ [this._databaseName, '--host', this._databaseHost, '--port', this._databasePort].concat(args || []), options);
+ }
+
+ _determinePgsqlDirectory()
+ {
+ try {
+ let initdbLocation = childProcess.execFileSync('which', ['initdb']);
+ return path.dirname(initdbLocation);
+ } catch (error) {
+ let serverPgsqlLocation = '/Applications/Server.app/Contents/ServerRoot/usr/bin/';
+ childProcess.execFileSync(path.resolve(serverPgsqlLocation, 'initdb'), ['--version']);
+ return serverPgsqlLocation;
+ }
+ }
+
+ _startApache()
+ {
+ let pidFile = Config.path('testServer.httpdPID');
+ let httpdConfig = Config.path('testServer.httpdConfig');
+ let port = Config.value('testServer.port');
+ let errorLog = Config.path('testServer.httpdErrorLog');
+ let mutexFile = Config.path('testServer.httpdMutexDir');
+
+ if (!fs.existsSync(mutexFile))
+ fs.mkdirSync(mutexFile, 0o755);
+
+ let args = [
+ '-f', httpdConfig,
+ '-c', `SetEnv ORG_WEBKIT_PERF_CONFIG_PATH ${this._testConfigPath}`,
+ '-c', `Listen ${port}`,
+ '-c', `PidFile ${pidFile}`,
+ '-c', `ErrorLog ${errorLog}`,
+ '-c', `Mutex file:${mutexFile}`,
+ '-c', `DocumentRoot ${Config.serverRoot()}`];
+
+ if (this._shouldLog)
+ console.log(args);
+
+ childProcess.execFileSync('httpd', args);
+
+ this._server = {
+ scheme: 'http',
+ host: 'localhost',
+ port: port,
+ }
+ this._pidWaitStart = Date.now();
+ this._pidFile = pidFile;
+ return new Promise(this._waitForPid.bind(this, true));
+ }
+
+ _stopApache()
+ {
+ if (!this._pidFile)
+ return;
+
+ let pid = fs.readFileSync(this._pidFile, 'utf-8').trim();
+
+ if (this._shouldLog)
+ console.log('Stopping', pid);
+
+ childProcess.execFileSync('kill', ['-TERM', pid]);
+
+ return new Promise(this._waitForPid.bind(this, false));
+ }
+
+ _waitForPid(shouldExist, resolve, reject)
+ {
+ if (fs.existsSync(this._pidFile) != shouldExist) {
+ if (Date.now() - this._pidWaitStart > 5000)
+ reject();
+ else
+ setTimeout(this._waitForPid.bind(this, shouldExist, resolve, reject), 100);
+ return;
+ }
+ resolve();
+ }
+});
+
+
+before(function () {
+ this.timeout(5000);
+ return TestServer.start();
+});
+
+beforeEach(function () {
+ this.timeout(5000);
+ return TestServer.initDatabase();
+});
+
+after(function () {
+ this.timeout(5000);
+ return TestServer.stop();
+});
+
+if (typeof module != 'undefined')
+ module.exports = TestServer;
Modified: trunk/Websites/perf.webkit.org/tools/js/config.js (198641 => 198642)
--- trunk/Websites/perf.webkit.org/tools/js/config.js 2016-03-24 19:56:58 UTC (rev 198641)
+++ trunk/Websites/perf.webkit.org/tools/js/config.js 2016-03-24 20:17:01 UTC (rev 198642)
@@ -28,10 +28,9 @@
return content;
}
- path(key)
- {
- return path.resolve(this._rootDirectory, this.value(key));
- }
+ path(key) { return path.resolve(this._rootDirectory, this.value(key)); }
+ serverRoot() { return path.resolve(this._rootDirectory, 'public'); }
+ pathFromRoot(relativePathFromRoot) { return path.resolve(this._rootDirectory, relativePathFromRoot); }
});
if (typeof module != 'undefined')
Modified: trunk/Websites/perf.webkit.org/tools/js/database.js (198641 => 198642)
--- trunk/Websites/perf.webkit.org/tools/js/database.js 2016-03-24 19:56:58 UTC (rev 198641)
+++ trunk/Websites/perf.webkit.org/tools/js/database.js 2016-03-24 20:17:01 UTC (rev 198642)
@@ -4,9 +4,10 @@
var config = require('./config.js');
class Database {
- constructor()
+ constructor(databaseName)
{
this._client = null;
+ this._databaseName = databaseName || config.value('database.name');
}
connect(options)
@@ -17,10 +18,9 @@
let password = config.value('database.password');
let host = config.value('database.host');
let port = config.value('database.port');
- let name = config.value('database.name');
// No need to worry about escaping strings since they are only set by someone who can write to config.json.
- let connectionString = `tcp://${username}:${password}@${host}:${port}/${name}`;
+ let connectionString = `tcp://${username}:${password}@${host}:${port}/${this._databaseName}`;
let client = new pg.Client(connectionString);
if (!options || !options.keepAlive) {
@@ -47,7 +47,50 @@
this._client = null;
}
}
+
+ query(statement, parameters)
+ {
+ console.assert(this._client);
+ var client = this._client;
+ return new Promise(function (resolve, reject) {
+ client.query(statement, parameters || [], function (error, result) {
+ if (error)
+ reject(error);
+ else
+ resolve(result);
+ });
+ });
+ }
+
+ insert(table, parameters)
+ {
+ let columnNames = [];
+ let placeholders = [];
+ let values = [];
+ for (let name in parameters) {
+ values.push(parameters[name]);
+ if (table in tableToPrefixMap)
+ name = tableToPrefixMap[table] + '_' + name;
+ columnNames.push(name);
+ placeholders.push(`\$${placeholders.length + 1}`);
+ }
+ return this.query(`INSERT INTO ${table} (${columnNames}) VALUES (${placeholders})`, values);
+ }
}
+let tableToPrefixMap = {
+ 'analysis_tasks': 'task',
+ 'analysis_test_groups': 'testgroup',
+ 'build_triggerables': 'triggerable',
+ 'build_requests': 'request',
+ 'commits': 'commit',
+ 'test_metrics': 'metric',
+ 'tests': 'test',
+ 'platforms': 'platform',
+ 'repositories': 'repository',
+ 'root_sets': 'rootset',
+ 'roots': 'root',
+}
+
if (typeof module != 'undefined')
module.exports = Database;
Added: trunk/Websites/perf.webkit.org/tools/js/remote.js (0 => 198642)
--- trunk/Websites/perf.webkit.org/tools/js/remote.js (rev 0)
+++ trunk/Websites/perf.webkit.org/tools/js/remote.js 2016-03-24 20:17:01 UTC (rev 198642)
@@ -0,0 +1,79 @@
+'use strict';
+
+let assert = require('assert');
+let http = require('http');
+let https = require('https');
+
+let RemoteAPI = new (class RemoteAPI {
+ constructor()
+ {
+ this._server = {
+ scheme: 'http',
+ host: 'localhost',
+ }
+ }
+
+ configure(server)
+ {
+ assert(server.scheme === 'http' || server.scheme === 'https');
+ assert.equal(typeof(server.host), 'string');
+ assert(!server.port || typeof(server.port) == 'number');
+ assert(!server.auth || typeof(server.auth) == 'object');
+ this._server = server;
+ }
+
+ fetchJSON(path, data)
+ {
+ let contentType = null;
+ if (data) {
+ contentType = 'application/json';
+ data = ""
+ }
+ return this.sendHttpRequest(path, 'GET', contentType, data).then(function (result) {
+ return JSON.parse(result.responseText);
+ });
+ }
+
+ fetchJSONWithStatus(path, data)
+ {
+ return this.fetchJSON(path, data).then(function (result) {
+ if (result['status'] != 'OK')
+ return Promise.reject(result);
+ return result;
+ });
+ }
+
+ sendHttpRequest(path, method, contentType, content)
+ {
+ let server = this._server;
+ return new Promise(function (resolve, reject) {
+ let options = {
+ hostname: server.host,
+ port: server.port || 80,
+ auth: server.auth,
+ method: method,
+ path: path,
+ };
+
+ let request = (server.scheme == 'http' ? http : https).request(options, function (response) {
+ let responseText = '';
+ response.setEncoding('utf8');
+ response.on('data', function (chunk) { responseText += chunk; });
+ response.on('end', function () { resolve({statusCode: response.statusCode, responseText: responseText}); });
+ });
+
+ request.on('error', reject);
+
+ if (contentType)
+ request.setHeader('Content-Type', contentType);
+
+ if (content)
+ request.write(content);
+
+ request.end();
+ });
+ }
+})
+
+if (typeof module != 'undefined')
+ module.exports.RemoteAPI = RemoteAPI;
Modified: trunk/Websites/perf.webkit.org/tools/sync-with-buildbot.py (198641 => 198642)
--- trunk/Websites/perf.webkit.org/tools/sync-with-buildbot.py 2016-03-24 19:56:58 UTC (rev 198641)
+++ trunk/Websites/perf.webkit.org/tools/sync-with-buildbot.py 2016-03-24 20:17:01 UTC (rev 198642)
@@ -41,7 +41,7 @@
'slaveName': server_config['slave']['name'],
'slavePassword': server_config['slave']['password']}
- build_requests_url = server_config['server']['url'] + '/api/build-requests/' + args.triggerable
+ build_requests_url = server_config['server']['url'] + '/api/build-requests/' + args.triggerable + '?useLegacyIdResolution=true'
response = update_and_fetch_build_requests(build_requests_url, payload)
open_requests = response.get('buildRequests', [])
Modified: trunk/Websites/perf.webkit.org/unit-tests/checkconfig.js (198641 => 198642)
--- trunk/Websites/perf.webkit.org/unit-tests/checkconfig.js 2016-03-24 19:56:58 UTC (rev 198641)
+++ trunk/Websites/perf.webkit.org/unit-tests/checkconfig.js 2016-03-24 20:17:01 UTC (rev 198642)
@@ -2,7 +2,6 @@
var assert = require('assert');
var fs = require('fs');
-var pg = require('pg');
var Config = require('../tools/js/config.js');
var Database = require('../tools/js/database.js');