Added: trunk/Websites/perf.webkit.org/public/include/commit-updater.php (0 => 239485)
--- trunk/Websites/perf.webkit.org/public/include/commit-updater.php (rev 0)
+++ trunk/Websites/perf.webkit.org/public/include/commit-updater.php 2018-12-21 02:05:39 UTC (rev 239485)
@@ -0,0 +1,228 @@
+<?php
+
+class CommitUpdater {
+ private $db;
+ private $top_level_repository_id_by_name;
+ private $owned_repository_by_name_and_owner_id;
+ private $commits_by_repository_id;
+ private $authors_by_repository_id;
+
+ function __construct($db)
+ {
+ $this->db = $db;
+ $this->top_level_repository_id_by_name = array();
+ $this->owned_repository_by_name_and_owner_id = array();
+ $this->commits_by_repository_id = array();
+ $this->authors_by_repository_id = array();
+ }
+
+ function report_commits(&$commit_info_list, $should_insert)
+ {
+ $update_list = &$this->construct_update_list($commit_info_list, $should_insert);
+ $this->db->begin_transaction();
+
+ foreach ($update_list as &$update)
+ $this->update_commit($update, NULL, NULL, $should_insert);
+
+ $this->db->commit_transaction();
+ }
+
+ private function update_commit(&$update, $owner_commit_id, $owner_repository_id, $should_insert)
+ {
+ $commit_data = &$this->resolve_fields_from_database($update, $owner_repository_id, $should_insert);
+
+ $commit_select_query = array('repository' => $commit_data['repository'], 'revision' => $commit_data['revision']);
+ if ($should_insert) {
+ $commit_id = $this->db->update_or_insert_row('commits', 'commit', $commit_select_query, $commit_data);
+ if (!$commit_id)
+ $this->exit_with_error('FailedToInsertCommit', array('commit' => $commit_data));
+
+ if ($owner_commit_id)
+ $this->db->select_or_insert_row('commit_ownerships', 'commit', array('owner' => $owner_commit_id, 'owned' => $commit_id), NULL, '*');
+ } else {
+ $commit_id = $this->db->update_row('commits', 'commit', $commit_select_query, $commit_data);
+ if (!$commit_id)
+ $this->exit_with_error('FailedToUpdateCommit', array('commit' => $commit_select_query, 'commitUpdate' => $commit_data));
+
+ if ($owner_commit_id)
+ $this->exit_with_error('AttemptToUpdateOwnedCommits', array('commit' => $commit_select_query));
+ }
+
+ if (!array_key_exists('owned_commits', $update))
+ return;
+
+ foreach ($update['owned_commits'] as &$owned_commit_update)
+ $this->update_commit($owned_commit_update, $commit_id, $commit_data['repository'], $should_insert);
+ }
+
+ private function &resolve_fields_from_database(&$update, $owner_repository_id, $should_insert)
+ {
+ $commit_data = &$update['commit'];
+
+ $repository_id = $this->resolve_repository($update['repository'], $owner_repository_id, $should_insert);
+ $commit_data['repository'] = $repository_id;
+
+ if (array_key_exists('previous_commit', $update))
+ $commit_data['previous_commit'] = $this->resolve_previous_commit($repository_id, $update['previous_commit']);
+
+ if (array_key_exists('author', $update))
+ $commit_data['committer'] = $this->resolve_committer($repository_id, $update['author']);
+
+ return $commit_data;
+ }
+
+ private function &construct_update_list(&$commit_info_list, $should_insert)
+ {
+ $update_list = array();
+
+ foreach ($commit_info_list as &$commit_info) {
+ self::validate_commits($commit_info);
+ $commit_data = &self::construct_commit_data($commit_info, NULL, $should_insert);
+ $has_update = count($commit_data) > 1;
+
+ $update = array('commit' => &$commit_data, 'repository' => &$commit_info['repository']);
+ if (array_key_exists('previousCommit', $commit_info)) {
+ $has_update = true;
+ $update['previous_commit'] = &$commit_info['previousCommit'];
+ }
+
+ if (array_key_exists('author', $commit_info)) {
+ $has_update = true;
+ $update['author'] = &$commit_info['author'];
+ }
+
+ if (array_key_exists('ownedCommits', $commit_info) && !$should_insert)
+ $this->exit_with_error('AttemptToUpdateOwnedCommits', array('commit' => $commit_info));
+
+ if (!$should_insert && !$has_update)
+ $this->exit_with_error('NothingToUpdate', array('commit' => $commit_data));
+
+ if (!array_key_exists('ownedCommits', $commit_info)) {
+ array_push($update_list, $update);
+ continue;
+ }
+
+ $owned_commit_update_list = array();
+ foreach($commit_info['ownedCommits'] as $owned_repository_name => &$owned_commit_info) {
+ $owned_commit = &self::construct_commit_data($owned_commit_info, $owned_repository_name, $should_insert);
+ $owned_commit_update = array('commit' => &$owned_commit, 'repository' => $owned_repository_name);
+
+ if (array_key_exists('previousCommit', $owned_commit_info))
+ $owned_commit_update['previous_commit'] = $owned_commit_info['previousCommit'];
+
+ if (array_key_exists('author', $owned_commit_info))
+ $owned_commit_update['author'] = &$owned_commit_info['author'];
+ array_push($owned_commit_update_list, $owned_commit_update);
+ }
+ if (count($owned_commit_update_list))
+ $update['owned_commits'] = $owned_commit_update_list;
+
+ array_push($update_list, $update);
+ }
+
+ return $update_list;
+ }
+
+ private static function &construct_commit_data(&$commit_info, $owned_repository_name, $should_insert)
+ {
+ $commit_data = array();
+ $commit_data['revision'] = $commit_info['revision'];
+
+ if (array_key_exists('message', $commit_info))
+ $commit_data['message'] = $commit_info['message'];
+
+ if (array_key_exists('order', $commit_info))
+ $commit_data['order'] = $commit_info['order'];
+
+ if (array_key_exists('testability', $commit_info))
+ $commit_data['testability'] = $commit_info['testability'];
+
+ if (array_key_exists('time', $commit_info)) {
+ if ($owned_repository_name)
+ exit_with_error('OwnedCommitShouldNotContainTimestamp', array('commit' => $commit_info));
+ $commit_data['time'] = $commit_info['time'];
+ }
+
+ if ($should_insert)
+ $commit_data['reported'] = true;
+
+ return $commit_data;
+ }
+
+ private static function validate_commits(&$commit_info)
+ {
+ if (!array_key_exists('repository', $commit_info))
+ exit_with_error('MissingRepositoryName', array('commit' => $commit_info));
+ if (!array_key_exists('revision', $commit_info))
+ exit_with_error('MissingRevision', array('commit' => $commit_info));
+ require_format('Revision', $commit_info['revision'], '/^[A-Za-z0-9 \.]+$/');
+ if (array_key_exists('author', $commit_info) && !is_array($commit_info['author']))
+ exit_with_error('InvalidAuthorFormat', array('commit' => $commit_info));
+ if (array_key_exists('previousCommit', $commit_info))
+ require_format('Revision', $commit_info['previousCommit'], '/^[A-Za-z0-9 \.]+$/');
+ }
+
+ private function resolve_repository($repository_name, $owner_repository_id, $should_insert)
+ {
+ if ($owner_repository_id)
+ $repository_id_by_name = &array_ensure_item_has_array($this->owned_repository_by_name_and_owner_id, $owner_repository_id);
+ else
+ $repository_id_by_name = &$this->top_level_repository_id_by_name;
+
+ if (array_key_exists($repository_name, $repository_id_by_name))
+ return $repository_id_by_name[$repository_name];
+
+ if ($should_insert) {
+ $repository_id = $this->db->select_or_insert_row('repositories', 'repository', array('name' => $repository_name, 'owner' => $owner_repository_id));
+ if (!$repository_id)
+ $this->exit_with_error('FailedToInsertRepository', array('repository' => $repository_name, 'owner' => $owner_repository_id));
+ } else {
+ $repository = $this->db->select_first_row('repositories', 'repository', array('name' => $repository_name, 'owner' => $owner_repository_id));
+ if (!$repository)
+ $this->exit_with_error('InvalidRepository', array('repository' => $repository_name));
+ $repository_id = $repository['repository_id'];
+ }
+
+ $repository_id_by_name[$repository_name] = $repository_id;
+ return $repository_id;
+ }
+
+ private function resolve_previous_commit($repository_id, $revision)
+ {
+ $commit_id_by_revision = &array_ensure_item_has_array($this->commits_by_repository_id, $revision);
+ if (array_key_exists($revision, $commit_id_by_revision))
+ return $commit_id_by_revision[$revision];
+
+ $previous_commit = $this->db->select_first_row('commits', 'commit', array('repository' => $repository_id, 'revision' => $revision));
+ if (!$previous_commit)
+ $this->exit_with_error('FailedToFindPreviousCommit', array('revision' => $revision));
+
+ $commit_id_by_revision[$revision] = $previous_commit['commit_id'];
+ return $previous_commit['commit_id'];
+ }
+
+ private function resolve_committer($repository_id, $author)
+ {
+ $committer_id_by_account = &array_ensure_item_has_array($this->authors_by_repository_id, $repository_id);
+ $account = array_get($author, 'account');
+ if (array_key_exists($account, $committer_id_by_account))
+ return $committer_id_by_account[$account];
+
+ $committer_data = array('repository' => $repository_id, 'account' => $account);
+ $name = array_get($author, 'name');
+ if ($name)
+ $committer_data['name'] = $name;
+ $committer_id = $this->db->update_or_insert_row('committers', 'committer', array('repository' => $repository_id, 'account' => $account), $committer_data);
+
+ if (!$committer_id)
+ $this->exit_with_error('FailedToInsertCommitter', array('committer' => $committer_data));
+ $committer_id_by_account[$account] = $committer_id;
+ return $committer_id;
+ }
+
+ private function exit_with_error($message, $details)
+ {
+ $this->db->rollback_transaction();
+ exit_with_error($message, $details);
+ }
+}
\ No newline at end of file
Modified: trunk/Websites/perf.webkit.org/server-tests/api-report-commits-tests.js (239484 => 239485)
--- trunk/Websites/perf.webkit.org/server-tests/api-report-commits-tests.js 2018-12-21 01:44:07 UTC (rev 239484)
+++ trunk/Websites/perf.webkit.org/server-tests/api-report-commits-tests.js 2018-12-21 02:05:39 UTC (rev 239485)
@@ -6,7 +6,7 @@
const addSlaveForReport = require('./resources/common-operations.js').addSlaveForReport;
const prepareServerTest = require('./resources/common-operations.js').prepareServerTest;
-describe("/api/report-commits/", function () {
+describe("/api/report-commits/ with insert=true", function () {
prepareServerTest(this);
const emptyReport = {
@@ -296,7 +296,7 @@
"message": "WebKit Commit",
}
]
- }
+ };
it("should distinguish between repositories with the same name but with a different owner.", () => {
return addSlaveForReport(sameRepositoryNameInOwnedCommitAndMajorCommit).then(() => {
@@ -336,7 +336,7 @@
}
}
]
- }
+ };
it("should accept inserting one commit with some owned commits", () => {
const db = TestServer.database();
@@ -591,3 +591,187 @@
});
});
});
+
+describe("/api/report-commits/ with insert=false", function () {
+ prepareServerTest(this);
+
+ const subversionCommits = {
+ "slaveName": "someSlave",
+ "slavePassword": "somePassword",
+ "commits": [
+ {
+ "repository": "WebKit",
+ "revision": "210948",
+ "time": "2017-01-20T02:52:34.577Z",
+ "author": {"name": "Zalan Bujtas", "account": "[email protected]"},
+ "message": "a message",
+ },
+ {
+ "repository": "WebKit",
+ "revision": "210949",
+ "time": "2017-01-20T03:23:50.645Z",
+ "author": {"name": "Chris Dumez", "account": "[email protected]"},
+ "message": "some message",
+ }
+ ],
+ "insert": true,
+ };
+
+ const commitsUpdate = {
+ "slaveName": "someSlave",
+ "slavePassword": "somePassword",
+ "commits": [
+ {
+ "repository": "WebKit",
+ "revision": "210948",
+ "testability": "Breaks builds",
+ "message": "another message",
+ "order": 210948,
+ "time": "2017-01-20T03:52:34.577Z",
+ "author": {"name": "Chris Dumez", "account": "[email protected]"},
+ },
+ {
+ "repository": "WebKit",
+ "revision": "210949",
+ "previousCommit": "210948",
+ "testability": "Crashes WebKit",
+ "message": "another message",
+ "order": 210949,
+ "time": "2017-01-20T04:23:50.645Z",
+ "author": {"name": "Zalan Bujtas", "account": "[email protected]"},
+ }
+ ],
+ "insert": false,
+ };
+
+
+ const commitsUpdateWithMissingRevision = {
+ "slaveName": "someSlave",
+ "slavePassword": "somePassword",
+ "commits": [
+ {
+ "repository": "WebKit",
+ "testability": "Breaks builds"
+ }
+ ],
+ "insert": false,
+ };
+
+ const commitsUpdateWithMissingRepository = {
+ "slaveName": "someSlave",
+ "slavePassword": "somePassword",
+ "commits": [
+ {
+ "revision": "210948",
+ "testability": "Breaks builds"
+ }
+ ],
+ "insert": false,
+ };
+
+ const commitsUpdateWithoutAnyUpdate = {
+ "slaveName": "someSlave",
+ "slavePassword": "somePassword",
+ "commits": [
+ {
+ "repository": "WebKit",
+ "revision": "210948"
+ }
+ ],
+ "insert": false,
+ };
+
+ const commitsUpdateWithOwnedCommitsUpdate = {
+ "slaveName": "someSlave",
+ "slavePassword": "somePassword",
+ "commits": [
+ {
+ "repository": "WebKit",
+ "revision": "210948",
+ "ownedCommits": [],
+ }
+ ],
+ "insert": false,
+ };
+
+ async function initialReportCommits()
+ {
+ await addSlaveForReport(subversionCommits);
+ await TestServer.remoteAPI().postJSON('/api/report-commits/', subversionCommits);
+ const result = await TestServer.remoteAPI().getJSON('/api/commits/WebKit/');
+ assert.equal(result['status'], 'OK');
+ const commits = result['commits'];
+ assert.equal(commits.length, 2);
+ assert.equal(commits[0].testability, null);
+ assert.equal(commits[1].testability, null);
+
+ return commits;
+ }
+
+ async function setUpTestsWithExpectedStatus(commitsUpdate, expectedStatus)
+ {
+ await initialReportCommits();
+ let result = await TestServer.remoteAPI().postJSON('/api/report-commits/', commitsUpdate);
+ assert.equal(result['status'], expectedStatus);
+ result = await TestServer.remoteAPI().getJSON('/api/commits/WebKit/');
+ return result['commits'];
+ }
+
+ async function testWithExpectedFailure(commitsUpdate, expectedFailureName)
+ {
+ const commits = await setUpTestsWithExpectedStatus(commitsUpdate, expectedFailureName);
+ assert.equal(commits.length, 2);
+ assert.equal(commits[0].testability, null);
+ assert.equal(commits[1].testability, null);
+ }
+
+ it('should fail with "MissingRevision" if commit update does not have commit revision specified', async () => {
+ await testWithExpectedFailure(commitsUpdateWithMissingRevision, 'MissingRevision');
+ });
+
+ it('should fail with "MissingRepositoryName" if commit update does not have commit revision specified', async () => {
+ await testWithExpectedFailure(commitsUpdateWithMissingRepository, 'MissingRepositoryName');
+ });
+
+ it('should fail with "NothingToUpdate" if commit update does not have commit revision specified', async () => {
+ await testWithExpectedFailure(commitsUpdateWithoutAnyUpdate, 'NothingToUpdate');
+ });
+
+ it('should fail with "AttemptToUpdateOwnedCommits" when trying to update owned commits info', async () => {
+ await testWithExpectedFailure(commitsUpdateWithOwnedCommitsUpdate, 'AttemptToUpdateOwnedCommits');
+ });
+
+ it('should not set reported to true if it was not set to true before', async () => {
+ const database = TestServer.database();
+ await initialReportCommits();
+
+ let commits = await database.selectAll('commits');
+ assert.equal(commits[0].reported, true);
+ assert.equal(commits[1].reported, true);
+
+ await database.query('UPDATE commits SET commit_reported=false');
+ let result = await TestServer.remoteAPI().postJSON('/api/report-commits/', commitsUpdate);
+ assert.equal(result['status'], 'OK');
+
+ commits = await database.selectAll('commits');
+ assert.equal(commits[0].reported, false);
+ assert.equal(commits[1].reported, false);
+ });
+
+ it("should be able to update commits message, time, order, author and previous commit", async () => {
+ const commits = await setUpTestsWithExpectedStatus(commitsUpdate, 'OK');
+
+ assert.equal(commits.length, 2);
+ assert.equal(commits[0].testability, 'Breaks builds');
+ assert.equal(commits[1].testability, 'Crashes WebKit');
+ assert.equal(commits[0].message, 'another message');
+ assert.equal(commits[1].message, 'another message');
+ assert.equal(commits[0].authorName, 'Chris Dumez');
+ assert.equal(commits[1].authorName, 'Zalan Bujtas');
+ assert.equal(commits[0].order, 210948);
+ assert.equal(commits[1].order, 210949);
+ assert.equal(commits[0].time, 1484884354577);
+ assert.equal(commits[1].time, 1484886230645);
+ assert.equal(commits[1].previousCommit, commits[0].id);
+ });
+});
\ No newline at end of file
Modified: trunk/Websites/perf.webkit.org/server-tests/tools-os-build-fetcher-tests.js (239484 => 239485)
--- trunk/Websites/perf.webkit.org/server-tests/tools-os-build-fetcher-tests.js 2018-12-21 01:44:07 UTC (rev 239484)
+++ trunk/Websites/perf.webkit.org/server-tests/tools-os-build-fetcher-tests.js 2018-12-21 02:05:39 UTC (rev 239485)
@@ -134,42 +134,60 @@
});
describe('OSBuilderFetcher._commitsForAvailableBuilds', () => {
- it('should only return commits whose orders are higher than specified order', () => {
+ it('should compatible with command output only contains lines of revision', async () => {
const logger = new MockLogger;
const fetcher = new OSBuildFetcher({}, null, null, MockSubprocess, logger);
const waitForInvocationPromise = MockSubprocess.waitForInvocation();
- const fetchCommitsPromise = fetcher._commitsForAvailableBuilds('OSX', ['list', 'build1'], '^\\.*$', 1604000000, 1606000000);
+ const fetchCommitsPromise = fetcher._commitsForAvailableBuilds(['list', 'build1'], '^\\.*$');
- return waitForInvocationPromise.then(() => {
- assert.equal(MockSubprocess.invocations.length, 1);
- assert.deepEqual(MockSubprocess.invocations[0].command, ['list', 'build1']);
- MockSubprocess.invocations[0].resolve('16D321\n16E321z\n\n16F321');
- return fetchCommitsPromise;
- }).then((results) => {
- assert.equal(results.length, 2);
- assert.deepEqual(results[0], {repository: 'OSX', order: 1604032126, revision: '16E321z'});
- assert.deepEqual(results[1], {repository: 'OSX', order: 1605032100, revision: '16F321'});
- });
+ await waitForInvocationPromise;
+ assert.equal(MockSubprocess.invocations.length, 1);
+ assert.deepEqual(MockSubprocess.invocations[0].command, ['list', 'build1']);
+
+ const expectedResults = {allRevisions: ["16D321", "16E321z", "16F321"], commitsWithTestability: {}};
+ await MockSubprocess.invocations[0].resolve('16D321\n16E321z\n\n16F321');
+ const buildInfo = await fetchCommitsPromise;
+ assert.deepEqual(expectedResults, buildInfo);
});
- it('should only return commits whose orders are higher than minOrder and lower than the maxOrder', () => {
+ it('should parse the command output as JSON format', async () => {
const logger = new MockLogger;
const fetcher = new OSBuildFetcher({}, null, null, MockSubprocess, logger);
const waitForInvocationPromise = MockSubprocess.waitForInvocation();
- const fetchCommitsPromise = fetcher._commitsForAvailableBuilds('OSX', ['list', 'build1'], '^\\.*$', 1604000000, 1605000000);
+ const fetchCommitsPromise = fetcher._commitsForAvailableBuilds(['list', 'build1']);
- return waitForInvocationPromise.then(() => {
- assert.equal(MockSubprocess.invocations.length, 1);
- assert.deepEqual(MockSubprocess.invocations[0].command, ['list', 'build1']);
- MockSubprocess.invocations[0].resolve('16D321\n16E321z\n\n16F321');
- return fetchCommitsPromise;
- }).then((results) => {
- assert.equal(results.length, 1);
- assert.deepEqual(results[0], {repository: 'OSX', order: 1604032126, revision: '16E321z'});
- });
+ await waitForInvocationPromise;
+ assert.equal(MockSubprocess.invocations.length, 1);
+ assert.deepEqual(MockSubprocess.invocations[0].command, ['list', 'build1']);
+
+ const outputObject = {allRevisions: ["16D321", "16E321z", "16F321"], commitsWithTestability: {"16D321": "Panic"}};
+ await MockSubprocess.invocations[0].resolve(JSON.stringify(outputObject));
+ const buildInfo = await fetchCommitsPromise;
+ assert.deepEqual(outputObject, buildInfo);
});
});
+
+ describe('OSBuilderFetcher._commitsWithinRange', () => {
+ it('should only return commits whose orders are higher than specified order', async () => {
+ const logger = new MockLogger;
+ const fetcher = new OSBuildFetcher({}, null, null, MockSubprocess, logger);
+ const results = fetcher._commitsWithinRange(["16D321", "16E321z", "16F321"], "OSX", 1604000000, 1606000000);
+ assert.equal(results.length, 2);
+ assert.deepEqual(results[0], {repository: 'OSX', order: 1604032126, revision: '16E321z'});
+ assert.deepEqual(results[1], {repository: 'OSX', order: 1605032100, revision: '16F321'});
+
+ });
+
+ it('should only return commits whose orders are higher than minOrder and lower than the maxOrder', async () => {
+ const logger = new MockLogger;
+ const fetcher = new OSBuildFetcher({}, null, null, MockSubprocess, logger);
+ const results = fetcher._commitsWithinRange(["16D321", "16E321z", "16F321"], "OSX", 1604000000, 1605000000);
+ assert.equal(results.length, 1);
+ assert.deepEqual(results[0], {repository: 'OSX', order: 1604032126, revision: '16E321z'});
+ });
+ });
+
describe('OSBuildFetcher._addOwnedCommitsForBuild', () => {
it('should add owned-commit info for commits', () => {
const logger = new MockLogger;
@@ -239,9 +257,9 @@
});
});
})
- })
+ });
- describe('OSBuildFetcher.fetchAndReportNewBuilds', () => {
+ describe('OSBuildFetcher.fetchReportAndUpdateBuilds', () => {
const invocations = MockSubprocess.invocations;
beforeEach(function () {
@@ -252,11 +270,12 @@
TestServer.database().disconnect();
});
- it('should report all build commits with owned-commits', () => {
+ it('should be backward compatible and report all build commits with owned-commits', () => {
const logger = new MockLogger;
const fetcher = new OSBuildFetcher(config, TestServer.remoteAPI(), slaveAuth, MockSubprocess, logger);
const db = TestServer.database();
- let fetchAndReportPromise = null;
+
+ let fetchReportAndUpdateBuildsPromise = null;
let fetchAvailableBuildsPromise = null;
return addSlaveForReport(emptyReport).then(() => {
@@ -307,10 +326,9 @@
assert.deepEqual(invocations[0].command, ['list', 'ownedCommit', 'for', 'revision', 'Sierra16E34']);
invocations[0].resolve(JSON.stringify(anotherownedCommitWithWebKitAndJavaScriptCore));
return fetchAvailableBuildsPromise;
- }).then((results) => {
- assert.equal(results.length, 3);
+ }).then(() => {
MockSubprocess.reset();
- fetchAndReportPromise = fetcher.fetchAndReportNewBuilds();
+ fetchReportAndUpdateBuildsPromise = fetcher.fetchReportAndUpdateBuilds();
return MockSubprocess.waitForInvocation();
}).then(() => {
assert.equal(invocations.length, 1);
@@ -337,10 +355,142 @@
invocations[0].resolve(JSON.stringify(anotherownedCommitWithWebKitAndJavaScriptCore));
assert.deepEqual(invocations[0].command, ['list', 'ownedCommit', 'for', 'revision', 'Sierra16E34']);
- return fetchAndReportPromise;
+ return fetchReportAndUpdateBuildsPromise;
+ }).then(() => {
+ return Promise.all([
+ db.selectRows('repositories', {'name': 'WebKit'}),
+ db.selectRows('repositories', {'name': '_javascript_Core'}),
+ db.selectRows('commits', {'revision': 'Sierra16D69'}),
+ db.selectRows('commits', {'revision': 'Sierra16E33h'}),
+ db.selectRows('commits', {'revision': 'Sierra16E34'})]);
+ }).then((results) => {
+ const webkitRepository = results[0];
+ const jscRepository = results[1];
+ const osxCommit16D69 = results[2];
+ const osxCommit16E33h = results[3];
+ const osxCommit16E34 = results[4];
+
+ assert.equal(webkitRepository.length, 1);
+ assert.equal(webkitRepository[0]['owner'], 10);
+ assert.equal(jscRepository.length, 1);
+ assert.equal(jscRepository[0]['owner'], 10);
+
+ assert.equal(osxCommit16D69.length, 1);
+ assert.equal(osxCommit16D69[0]['repository'], 10);
+ assert.equal(osxCommit16D69[0]['order'], 1603006900);
+
+ assert.equal(osxCommit16E33h.length, 1);
+ assert.equal(osxCommit16E33h[0]['repository'], 10);
+ assert.equal(osxCommit16E33h[0]['order'], 1604003308);
+
+ assert.equal(osxCommit16E34.length, 1);
+ assert.equal(osxCommit16E34[0]['repository'], 10);
+ assert.equal(osxCommit16E34[0]['order'], 1604003400);
+
+ return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1603000000&to=1603099900');
}).then((result) => {
- assert.equal(result['status'], 'OK');
+ assert.equal(result['commits'].length, 1);
+ assert.equal(result['commits'][0]['revision'], 'Sierra16D69');
+ assert.equal(result['commits'][0]['order'], 1603006900);
+
+ return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1604000000&to=1604099900');
+ }).then((result) => {
+ assert.equal(result['commits'].length, 1);
+ assert.equal(result['commits'][0]['revision'], 'Sierra16E34');
+ assert.equal(result['commits'][0]['order'], 1604003400);
+ });
+ });
+
+ it('should report all build commits with owned-commits', () => {
+ const logger = new MockLogger;
+ const fetcher = new OSBuildFetcher(config, TestServer.remoteAPI(), slaveAuth, MockSubprocess, logger);
+ const db = TestServer.database();
+ const resultsForSierraD = {allRevisions: ["Sierra16D68", "Sierra16D69"], commitsWithTestability: {}};
+ const resultsForSierraE = {allRevisions: ["Sierra16E32", "Sierra16E33", "Sierra16E33h", "Sierra16E34"], commitsWithTestability: {}};
+
+ let fetchReportAndUpdateBuildsPromise = null;
+ let fetchAvailableBuildsPromise = null;
+
+ return addSlaveForReport(emptyReport).then(() => {
return Promise.all([
+ db.insert('repositories', {'id': 10, 'name': 'OSX'}),
+ db.insert('commits', {'repository': 10, 'revision': 'Sierra16D67', 'order': 1603006700, 'reported': true}),
+ db.insert('commits', {'repository': 10, 'revision': 'Sierra16D68', 'order': 1603006800, 'reported': true}),
+ db.insert('commits', {'repository': 10, 'revision': 'Sierra16D69', 'order': 1603006900, 'reported': false}),
+ db.insert('commits', {'repository': 10, 'revision': 'Sierra16E32', 'order': 1604003200, 'reported': true}),
+ db.insert('commits', {'repository': 10, 'revision': 'Sierra16E33', 'order': 1604003300, 'reported': true}),
+ db.insert('commits', {'repository': 10, 'revision': 'Sierra16E33g', 'order': 1604003307, 'reported': true})]);
+ }).then(() => {
+ return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1603000000&to=1603099900');
+ }).then((result) => {
+ assert.equal(result['commits'].length, 1);
+ assert.equal(result['commits'][0]['revision'], 'Sierra16D68');
+ assert.equal(result['commits'][0]['order'], 1603006800);
+ return TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1604000000&to=1604099900');
+ }).then((result) => {
+ assert.equal(result['commits'].length, 1);
+ assert.equal(result['commits'][0]['revision'], 'Sierra16E33g');
+ assert.equal(result['commits'][0]['order'], 1604003307);
+ const waitForInvocationPromise = MockSubprocess.waitForInvocation();
+ fetchAvailableBuildsPromise = fetcher._fetchAvailableBuilds();
+ return waitForInvocationPromise;
+ }).then(() => {
+ assert.equal(invocations.length, 1);
+ assert.deepEqual(invocations[0].command, ['list', 'all osx 16Dxx builds']);
+ invocations[0].resolve(JSON.stringify(resultsForSierraD));
+ return MockSubprocess.resetAndWaitForInvocation();
+ }).then(() => {
+ assert.equal(invocations.length, 1);
+ assert.deepEqual(invocations[0].command, ['list', 'ownedCommit', 'for', 'revision', 'Sierra16D69']);
+ invocations[0].resolve(JSON.stringify(ownedCommitWithWebKit));
+ return MockSubprocess.resetAndWaitForInvocation();
+ }).then(() => {
+ assert.equal(invocations.length, 1);
+ assert.deepEqual(invocations[0].command, ['list', 'all osx 16Exx builds']);
+ invocations[0].resolve(JSON.stringify(resultsForSierraE));
+ return MockSubprocess.resetAndWaitForInvocation();
+ }).then(() => {
+ assert.equal(invocations.length, 1);
+ assert.deepEqual(invocations[0].command, ['list', 'ownedCommit', 'for', 'revision', 'Sierra16E33h']);
+ invocations[0].resolve(JSON.stringify(anotherownedCommitWithWebKit));
+ return MockSubprocess.resetAndWaitForInvocation();
+ }).then(() => {
+ assert.equal(invocations.length, 1);
+ assert.deepEqual(invocations[0].command, ['list', 'ownedCommit', 'for', 'revision', 'Sierra16E34']);
+ invocations[0].resolve(JSON.stringify(anotherownedCommitWithWebKitAndJavaScriptCore));
+ return fetchAvailableBuildsPromise;
+ }).then(() => {
+ MockSubprocess.reset();
+ fetchReportAndUpdateBuildsPromise = fetcher.fetchReportAndUpdateBuilds();
+ return MockSubprocess.waitForInvocation();
+ }).then(() => {
+ assert.equal(invocations.length, 1);
+ assert.deepEqual(invocations[0].command, ['list', 'all osx 16Dxx builds']);
+ invocations[0].resolve(JSON.stringify(resultsForSierraD));
+ return MockSubprocess.resetAndWaitForInvocation();
+ }).then(() => {
+ assert.equal(invocations.length, 1);
+ assert.deepEqual(invocations[0].command, ['list', 'ownedCommit', 'for', 'revision', 'Sierra16D69']);
+ invocations[0].resolve(JSON.stringify(ownedCommitWithWebKit));
+ return MockSubprocess.resetAndWaitForInvocation();
+ }).then(() => {
+ assert.equal(invocations.length, 1);
+ assert.deepEqual(invocations[0].command, ['list', 'all osx 16Exx builds']);
+ invocations[0].resolve(JSON.stringify(resultsForSierraE));
+ return MockSubprocess.resetAndWaitForInvocation();
+ }).then(() => {
+ assert.equal(invocations.length, 1);
+ assert.deepEqual(invocations[0].command, ['list', 'ownedCommit', 'for', 'revision', 'Sierra16E33h']);
+ invocations[0].resolve(JSON.stringify(anotherownedCommitWithWebKit));
+ return MockSubprocess.resetAndWaitForInvocation();
+ }).then(() => {
+ assert.equal(invocations.length, 1);
+ invocations[0].resolve(JSON.stringify(anotherownedCommitWithWebKitAndJavaScriptCore));
+ assert.deepEqual(invocations[0].command, ['list', 'ownedCommit', 'for', 'revision', 'Sierra16E34']);
+
+ return fetchReportAndUpdateBuildsPromise;
+ }).then(() => {
+ return Promise.all([
db.selectRows('repositories', {'name': 'WebKit'}),
db.selectRows('repositories', {'name': '_javascript_Core'}),
db.selectRows('commits', {'revision': 'Sierra16D69'}),
@@ -384,11 +534,119 @@
});
});
+ it('should update testability warning for commits', async () => {
+ const logger = new MockLogger;
+ const fetcher = new OSBuildFetcher(config, TestServer.remoteAPI(), slaveAuth, MockSubprocess, logger);
+ const db = TestServer.database();
+ const resultsForSierraD = {allRevisions: ["Sierra16D68", "Sierra16D69"], commitsWithTestability: {"Sierra16D68": "Panic", "Sierra16D69": "Spin CPU"}};
+ const resultsForSierraE = {allRevisions: ["Sierra16E32", "Sierra16E33", "Sierra16E33h", "Sierra16E34"], commitsWithTestability: {"Sierra16E31": "WebKit crashes"}};
+
+ await addSlaveForReport(emptyReport);
+
+ await Promise.all([
+ db.insert('repositories', {'id': 10, 'name': 'OSX'}),
+ db.insert('commits', {'repository': 10, 'revision': 'Sierra16D67', 'order': 1603006700, 'reported': true}),
+ db.insert('commits', {'repository': 10, 'revision': 'Sierra16D68', 'order': 1603006800, 'reported': true}),
+ db.insert('commits', {'repository': 10, 'revision': 'Sierra16D69', 'order': 1603006900, 'reported': false}),
+ db.insert('commits', {'repository': 10, 'revision': 'Sierra16E31', 'order': 1604003100, 'reported': true}),
+ db.insert('commits', {'repository': 10, 'revision': 'Sierra16E32', 'order': 1604003200, 'reported': true}),
+ db.insert('commits', {'repository': 10, 'revision': 'Sierra16E33', 'order': 1604003300, 'reported': true}),
+ db.insert('commits', {'repository': 10, 'revision': 'Sierra16E33g', 'order': 1604003307, 'reported': true})]);
+
+ let result = await TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1603000000&to=1603099900');
+
+ assert.equal(result['commits'].length, 1);
+ assert.equal(result['commits'][0]['revision'], 'Sierra16D68');
+ assert.equal(result['commits'][0]['order'], 1603006800);
+ result = await TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1604000000&to=1604099900');
+
+ assert.equal(result['commits'].length, 1);
+ assert.equal(result['commits'][0]['revision'], 'Sierra16E33g');
+ assert.equal(result['commits'][0]['order'], 1604003307);
+
+ const fetchReportAndUpdatePromise = fetcher.fetchReportAndUpdateBuilds();
+ await MockSubprocess.waitForInvocation();
+
+ assert.equal(invocations.length, 1);
+ assert.deepEqual(invocations[0].command, ['list', 'all osx 16Dxx builds']);
+ invocations[0].resolve(JSON.stringify(resultsForSierraD));
+ await MockSubprocess.resetAndWaitForInvocation();
+
+ assert.equal(invocations.length, 1);
+ assert.deepEqual(invocations[0].command, ['list', 'ownedCommit', 'for', 'revision', 'Sierra16D69']);
+ invocations[0].resolve(JSON.stringify(ownedCommitWithWebKit));
+ await MockSubprocess.resetAndWaitForInvocation();
+
+ assert.equal(invocations.length, 1);
+ assert.deepEqual(invocations[0].command, ['list', 'all osx 16Exx builds']);
+ invocations[0].resolve(JSON.stringify(resultsForSierraE));
+
+ await MockSubprocess.resetAndWaitForInvocation();
+
+ assert.equal(invocations.length, 1);
+ assert.deepEqual(invocations[0].command, ['list', 'ownedCommit', 'for', 'revision', 'Sierra16E33h']);
+ invocations[0].resolve(JSON.stringify(anotherownedCommitWithWebKit));
+ await MockSubprocess.resetAndWaitForInvocation();
+ assert.equal(invocations.length, 1);
+ invocations[0].resolve(JSON.stringify(anotherownedCommitWithWebKitAndJavaScriptCore));
+ assert.deepEqual(invocations[0].command, ['list', 'ownedCommit', 'for', 'revision', 'Sierra16E34']);
+
+ await fetchReportAndUpdatePromise;
+
+ const webkitRepository = await db.selectRows('repositories', {'name': 'WebKit'});
+ const jscRepository = await db.selectRows('repositories', {'name': '_javascript_Core'});
+ const osxCommit16D68 = await db.selectRows('commits', {'revision': 'Sierra16D68'});
+ const osxCommit16D69 = await db.selectRows('commits', {'revision': 'Sierra16D69'});
+ const osxCommit16E31 = await db.selectRows('commits', {'revision': 'Sierra16E31'});
+ const osxCommit16E33h = await db.selectRows('commits', {'revision': 'Sierra16E33h'});
+ const osxCommit16E34 = await db.selectRows('commits', {'revision': 'Sierra16E34'});
+
+ assert.equal(webkitRepository.length, 1);
+ assert.equal(webkitRepository[0]['owner'], 10);
+ assert.equal(jscRepository.length, 1);
+ assert.equal(jscRepository[0]['owner'], 10);
+
+ assert.equal(osxCommit16D68.length, 1);
+ assert.equal(osxCommit16D68[0]['repository'], 10);
+ assert.equal(osxCommit16D68[0]['order'], 1603006800);
+ assert.equal(osxCommit16D68[0]['testability'], "Panic");
+
+ assert.equal(osxCommit16D69.length, 1);
+ assert.equal(osxCommit16D69[0]['repository'], 10);
+ assert.equal(osxCommit16D69[0]['order'], 1603006900);
+ assert.equal(osxCommit16D69[0]['testability'], "Spin CPU");
+
+ assert.equal(osxCommit16E31.length, 1);
+ assert.equal(osxCommit16E31[0]['repository'], 10);
+ assert.equal(osxCommit16E31[0]['order'], 1604003100);
+ assert.equal(osxCommit16E31[0]['testability'], "WebKit crashes");
+
+ assert.equal(osxCommit16E33h.length, 1);
+ assert.equal(osxCommit16E33h[0]['repository'], 10);
+ assert.equal(osxCommit16E33h[0]['order'], 1604003308);
+
+ assert.equal(osxCommit16E34.length, 1);
+ assert.equal(osxCommit16E34[0]['repository'], 10);
+ assert.equal(osxCommit16E34[0]['order'], 1604003400);
+
+ result = await TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1603000000&to=1603099900');
+ assert.equal(result['commits'].length, 1);
+ assert.equal(result['commits'][0]['revision'], 'Sierra16D69');
+ assert.equal(result['commits'][0]['order'], 1603006900);
+
+ result = await TestServer.remoteAPI().getJSON('/api/commits/OSX/last-reported?from=1604000000&to=1604099900');
+ assert.equal(result['commits'].length, 1);
+ assert.equal(result['commits'][0]['revision'], 'Sierra16E34');
+ assert.equal(result['commits'][0]['order'], 1604003400);
+ });
+
it('should report commits without owned-commits if "ownedCommitCommand" is not specified in config', async () => {
const logger = new MockLogger;
const fetcher = new OSBuildFetcher(configWithoutOwnedCommitCommand, TestServer.remoteAPI(), slaveAuth, MockSubprocess, logger);
const db = TestServer.database();
+ const resultsForSierraD = {allRevisions: ["Sierra16D68", "Sierra16D69"], commitsWithTestability: {}};
+ const resultsForSierraE = {allRevisions: ["Sierra16E32", "Sierra16E33", "Sierra16E33h", "Sierra16E34"], commitsWithTestability: {}};
await addSlaveForReport(emptyReport);
await Promise.all([
@@ -411,19 +669,18 @@
assert.equal(result['commits'][0]['order'], 1604003307);
const waitForInvocationPromise = MockSubprocess.waitForInvocation();
- const fetchAndReportPromise = fetcher.fetchAndReportNewBuilds();
+ const fetchReportAndUpdatePromise = fetcher.fetchReportAndUpdateBuilds();
await waitForInvocationPromise;
assert.equal(invocations.length, 1);
assert.deepEqual(invocations[0].command, ['list', 'all osx 16Dxx builds']);
- invocations[0].resolve('\n\nSierra16D68\nSierra16D69');
+ invocations[0].resolve(JSON.stringify(resultsForSierraD));
await MockSubprocess.resetAndWaitForInvocation();
assert.equal(invocations.length, 1);
assert.deepEqual(invocations[0].command, ['list', 'all osx 16Exx builds']);
- invocations[0].resolve('\n\nSierra16E32\nSierra16E33\nSierra16E33h\nSierra16E34');
+ invocations[0].resolve(JSON.stringify(resultsForSierraE));
- result = await fetchAndReportPromise;
- assert.equal(result['status'], 'OK');
+ result = await fetchReportAndUpdatePromise;
const results = await Promise.all([
db.selectRows('repositories', {'name': 'WebKit'}),
db.selectRows('repositories', {'name': '_javascript_Core'}),
@@ -467,6 +724,8 @@
const logger = new MockLogger;
const fetcher = new OSBuildFetcher(configWithoutOwnedCommitCommand, TestServer.remoteAPI(), slaveAuth, MockSubprocess, logger);
const db = TestServer.database();
+ const resultsForSierraD = {allRevisions: ["Sierra16D68", "Sierra16D69", "Sierra16D10000"], commitsWithTestability: {}};
+ const resultsForSierraE = {allRevisions: ["Sierra16E32", "Sierra16E33", "Sierra16E33h", "Sierra16E34", "Sierra16E10000"], commitsWithTestability: {}};
await addSlaveForReport(emptyReport);
await Promise.all([
@@ -488,20 +747,19 @@
assert.equal(result['commits'][0]['revision'], 'Sierra16E33g');
assert.equal(result['commits'][0]['order'], 1604003307);
const waitForInvocationPromise = MockSubprocess.waitForInvocation();
- const fetchAndReportPromise = fetcher.fetchAndReportNewBuilds();
+ const fetchReportAndUpdatePromise = fetcher.fetchReportAndUpdateBuilds();
await waitForInvocationPromise;
assert.equal(invocations.length, 1);
assert.deepEqual(invocations[0].command, ['list', 'all osx 16Dxx builds']);
- invocations[0].resolve('\n\nSierra16D68\nSierra16D69\nSierra16D10000');
+ invocations[0].resolve(JSON.stringify(resultsForSierraD));
await MockSubprocess.resetAndWaitForInvocation();
assert.equal(invocations.length, 1);
assert.deepEqual(invocations[0].command, ['list', 'all osx 16Exx builds']);
- invocations[0].resolve('\n\nSierra16E32\nSierra16E33\nSierra16E33h\nSierra16E34\nSierra16E10000');
+ invocations[0].resolve(JSON.stringify(resultsForSierraE));
- result = await fetchAndReportPromise;
- assert.equal(result['status'], 'OK');
+ result = await fetchReportAndUpdatePromise;
const results = await Promise.all([
db.selectRows('repositories', {'name': 'WebKit'}),
db.selectRows('repositories', {'name': '_javascript_Core'}),
@@ -545,6 +803,7 @@
const logger = new MockLogger;
const fetcher = new OSBuildFetcher(configTrackingOneOS, TestServer.remoteAPI(), slaveAuth, MockSubprocess, logger);
const db = TestServer.database();
+ const resultsForSierraD = {allRevisions: ["Sierra16D68", "Sierra16D69", "Sierra16D100", "Sierra16D100a"], commitsWithTestability: {}};
await addSlaveForReport(emptyReport);
await db.insert('repositories', {'id': 10, 'name': 'OSX'});
@@ -556,14 +815,13 @@
assert.equal(result['commits'][0]['order'], 1603010000);
const waitForInvocationPromise = MockSubprocess.waitForInvocation();
- const fetchAndReportPromise = fetcher.fetchAndReportNewBuilds();
+ const fetchAndReportPromise = fetcher.fetchReportAndUpdateBuilds();
await waitForInvocationPromise;
assert.equal(invocations.length, 1);
assert.deepEqual(invocations[0].command, ['list', 'all osx 16Dxx builds']);
- invocations[0].resolve('\n\nSierra16D68\nSierra16D69\nSierra16D100\nSierra16D100a\n');
+ invocations[0].resolve(JSON.stringify(resultsForSierraD));
result = await fetchAndReportPromise;
- assert.equal(result['status'], 'OK');
const results = await Promise.all([
db.selectRows('repositories', {'name': 'WebKit'}),
db.selectRows('repositories', {'name': '_javascript_Core'}),
@@ -594,6 +852,7 @@
const logger = new MockLogger;
const fetcher = new OSBuildFetcher(configTrackingOneOS, TestServer.remoteAPI(), slaveAuth, MockSubprocess, logger);
const db = TestServer.database();
+ const resultsForSierraD = {allRevisions: ["Sierra16D68", "Sierra16D69", "Sierra16D100", "Sierra16D101"], commitsWithTestability: {}};
await addSlaveForReport(emptyReport);
await db.insert('repositories', {'id': 10, 'name': 'OSX'});
@@ -602,14 +861,13 @@
assert.equal(result['commits'].length, 0);
const waitForInvocationPromise = MockSubprocess.waitForInvocation();
- const fetchAndReportPromise = fetcher.fetchAndReportNewBuilds();
+ const fetchReportAndUpdatePromise = fetcher.fetchReportAndUpdateBuilds();
await waitForInvocationPromise;
assert.equal(invocations.length, 1);
assert.deepEqual(invocations[0].command, ['list', 'all osx 16Dxx builds']);
- invocations[0].resolve('\n\nSierra16D68\nSierra16D69\nSierra16D100\nSierra16D101\n');
+ invocations[0].resolve(JSON.stringify(resultsForSierraD));
- result = await fetchAndReportPromise;
- assert.equal(result['status'], 'OK');
+ result = await fetchReportAndUpdatePromise;
const results = await Promise.all([
db.selectRows('repositories', {'name': 'WebKit'}),
db.selectRows('repositories', {'name': '_javascript_Core'}),
@@ -646,6 +904,8 @@
const logger = new MockLogger;
const fetcher = new OSBuildFetcher(config, TestServer.remoteAPI(), slaveAuth, MockSubprocess, logger);
const db = TestServer.database();
+ const resultsForSierraD = {allRevisions: ["Sierra16D68", "Sierra16D69"], commitsWithTestability: {}};
+ const resultsForSierraE = {allRevisions: ["Sierra16E32", "Sierra16E33", "Sierra16E33h", "Sierra16E34"], commitsWithTestability: {}};
let fetchAndReportPromise = null;
return addSlaveForReport(emptyReport).then(() => {
@@ -671,12 +931,12 @@
assert.equal(result['commits'][0]['order'], 1604003307);
const waitForInvocationPromise = MockSubprocess.waitForInvocation();
- fetchAndReportPromise = fetcher.fetchAndReportNewBuilds();
+ fetchAndReportPromise = fetcher.fetchReportAndUpdateBuilds();
return waitForInvocationPromise;
}).then(() => {
assert.equal(invocations.length, 1);
assert.deepEqual(invocations[0].command, ['list', 'all osx 16Dxx builds']);
- invocations[0].resolve('\n\nSierra16D68\nSierra16D69\n');
+ invocations[0].resolve(JSON.stringify(resultsForSierraD));
return MockSubprocess.resetAndWaitForInvocation();
}).then(() => {
assert.equal(invocations.length, 1);
@@ -686,7 +946,7 @@
}).then(() => {
assert.equal(invocations.length, 1);
assert.deepEqual(invocations[0].command, ['list', 'all osx 16Exx builds']);
- invocations[0].resolve('\n\nSierra16E32\nSierra16E33\nSierra16E33h\nSierra16E34');
+ invocations[0].resolve(JSON.stringify(resultsForSierraE));
return MockSubprocess.resetAndWaitForInvocation();
}).then(() => {
assert.deepEqual(invocations[0].command, ['list', 'ownedCommit', 'for', 'revision', 'Sierra16E33h']);