Diff
Modified: trunk/Websites/perf.webkit.org/ChangeLog (269870 => 269871)
--- trunk/Websites/perf.webkit.org/ChangeLog 2020-11-16 20:25:21 UTC (rev 269870)
+++ trunk/Websites/perf.webkit.org/ChangeLog 2020-11-16 20:32:08 UTC (rev 269871)
@@ -1,3 +1,38 @@
+2020-10-30 Dewei Zhu <dewei_...@apple.com>
+
+ Performance dashboard should avoid building same configuration under one analysis task.
+ https://bugs.webkit.org/show_bug.cgi?id=218413
+
+ Reviewed by Ryosuke Niwa.
+
+ Add logic in syncing script to reuse already built roots from same build request under current analysis task.
+ If there is another same build request scheduled/running, will defer scheduling current build request.
+
+ * public/admin/platforms.php: Fixed newer version of PHP warns on accessing invalid key in an array.
+ * public/admin/tests.php: Added a null check against $selected_parent.
+ * public/api/build-requests.php: Extended this API to allow reusing roots from existing build request.
+ * public/v3/models/build-request.js:
+ (BuildRequest.prototype.async findBuildRequestWithSameRoots): Find build type build request with same commit set
+ which can/will be reused under current analysis task.
+ * public/v3/models/commit-set.js:
+ (CommitSet.prototype.equalsIgnoringRoot): Added a helper function which checks commit set equality ignoring the root.
+ (CommitSet.prototype.equals): Use '_equalsOptionallyIgnoringRoot' as underlying implementation.
+ (CommitSet.prototype._equalsOptionallyIgnoringRoot): Implementation for both equals and equalsIngnoringRoot.
+ * server-tests/api-build-requests-tests.js: Added unit tests.
+ * server-tests/resources/mock-data.js: Added new mock data for new unit tests.
+ (MockData.addMockConfiguration):
+ (MockData.addMockData):
+ (MockData.set addMockBuildRequestsWithRoots):
+ (MockData.set addTwoMockTestGroupWithOwnedCommits):
+ (MockData.mockTestSyncConfigWithPatchAcceptingBuilder):
+ * server-tests/tools-buildbot-triggerable-tests.js: Added unit tests.
+ * tools/js/buildbot-triggerable.js:
+ (BuildbotTriggerable.prototype.async syncOnce):
+ (BuildbotTriggerable.prototype.async _scheduleRequest): A helper function to reuse the roots if there are built
+ roots available for same build type build request. If there is another build scheduled for same build request,
+ will defer scheduling the build request.
+ * unit-tests/build-request-tests.js: Add unit tests.
+
2020-10-27 Dewei Zhu <dewei_...@apple.com>
Refactor 'platforms' table to contain group information.
Modified: trunk/Websites/perf.webkit.org/public/admin/platforms.php (269870 => 269871)
--- trunk/Websites/perf.webkit.org/public/admin/platforms.php 2020-11-16 20:25:21 UTC (rev 269870)
+++ trunk/Websites/perf.webkit.org/public/admin/platforms.php 2020-11-16 20:32:08 UTC (rev 269871)
@@ -102,7 +102,7 @@
{
global $platform_group_options;
$id = intval($platform_row['platform_id']);
- $platform_group_id = $platform_row['platform_group'];
+ $platform_group_id = array_get($platform_row, 'platform_group');
$content = <<< END
<form method="POST"><input type="hidden" name="id" value="$id">
<select name="group">
Modified: trunk/Websites/perf.webkit.org/public/admin/tests.php (269870 => 269871)
--- trunk/Websites/perf.webkit.org/public/admin/tests.php 2020-11-16 20:25:21 UTC (rev 269870)
+++ trunk/Websites/perf.webkit.org/public/admin/tests.php 2020-11-16 20:32:08 UTC (rev 269871)
@@ -64,7 +64,7 @@
<?php
foreach ($test_name_resolver->tests() as $test) {
- if ($test['test_parent'] != $selected_parent['test_id'])
+ if ($selected_parent && $test['test_parent'] != $selected_parent['test_id'])
continue;
$test_id = $test['test_id'];
Modified: trunk/Websites/perf.webkit.org/public/api/build-requests.php (269870 => 269871)
--- trunk/Websites/perf.webkit.org/public/api/build-requests.php 2020-11-16 20:25:21 UTC (rev 269870)
+++ trunk/Websites/perf.webkit.org/public/api/build-requests.php 2020-11-16 20:32:08 UTC (rev 269871)
@@ -47,6 +47,7 @@
$status = $info['status'];
$url = "" 'url');
$status_description = array_get($info, 'statusDescription');
+ $build_request_for_root_reuse_id = array_get($info, 'buildRequestForRootReuse');
$request_row = $db->select_first_row('build_requests', 'request', array('id' => $id));
if ($status == 'failedIfNotCompleted') {
if (!$request_row) {
@@ -68,6 +69,23 @@
exit_with_error('UnknownBuildRequestStatus', array('buildRequest' => $id, 'status' => $status));
}
$db->update_row('build_requests', 'request', array('id' => $id), array('status' => $status, 'url' => $url, 'status_description' => $status_description));
+ if ($build_request_for_root_reuse_id) {
+ $build_request_for_root_reuse_id = intval($build_request_for_root_reuse_id);
+ $build_request_for_root_reuse = $db->select_first_row('build_requests', 'request', array('id' => $build_request_for_root_reuse_id));
+ if (!$build_request_for_root_reuse) {
+ $db->rollback_transaction();
+ exit_with_error('FailedToFindbuildRequestForRootReuse', array('buildRequest' => $build_request_for_root_reuse_id));
+ }
+ if ($build_request_for_root_reuse['request_status'] != 'completed') {
+ $db->rollback_transaction();
+ exit_with_error('CanOnlyReuseCompletedBuildRequest', array('buildRequest' => $build_request_for_root_reuse_id, 'status' => $build_request_for_root_reuse['request_status']));
+ }
+ $error = reuse_roots_in_commit_set($db, $build_request_for_root_reuse['request_commit_set'], $request_row['request_commit_set']);
+ if ($error) {
+ $db->rollback_transaction();
+ exit_with_error($error['status'], $error['details']);
+ }
+ }
if ($status != 'failed')
continue;
}
@@ -82,8 +100,60 @@
$db->commit_transaction();
}
+function reuse_roots_in_commit_set($db, $source_id, $destination_id) {
+ $commit_set_items_source = $db->query_and_fetch_all('SELECT * FROM commit_set_items WHERE commitset_set = $1 AND commitset_commit IS NOT NULL', array($source_id));
+ $commit_set_items_destination = $db->query_and_fetch_all('SELECT * FROM commit_set_items WHERE commitset_set = $1 AND commitset_commit IS NOT NULL', array($destination_id));
+ if (count($commit_set_items_source) != count($commit_set_items_destination)) {
+ return array('status' => 'CannotReuseRootWithNonMatchingCommitSets', 'details' => array('sourceCommitSet' => $source_id,
+ 'destinationCommitSet' => $destination_id));
+ }
+
+ $root_file_ids = array();
+ foreach ($commit_set_items_destination as &$destination_item) {
+ $source_item = NULL;
+ foreach ($commit_set_items_source as &$item) {
+ if ($destination_item['commitset_commit'] == $item['commitset_commit']
+ && $destination_item['commitset_patch_file'] == $item['commitset_patch_file']
+ && $destination_item['commitset_commit_owner'] == $item['commitset_commit_owner']
+ && $destination_item['commitset_requires_build'] == $item['commitset_requires_build']) {
+ $source_item = $item;
+ break;
+ }
+ }
+ if (!$source_item)
+ return array('status' => 'NoMatchingCommitSetItem', 'details' => array('commitSet' => $source_id));
+
+ $root_file_id = $source_item['commitset_root_file'];
+ if (!$root_file_id) {
+ if (!$db->is_true($source_item['commitset_requires_build']))
+ continue;
+ return array('status' => 'MissingRootFileFromSourceCommitSet', 'details' => array('commitSet' => $destination_id, 'rootFile' => $root_file_id));
+ }
+
+ $root_file_row = $db->select_first_row('uploaded_files', 'file', array('id' => $root_file_id));
+ if ($root_file_row['file_deleted_at'])
+ return array('status' => 'CannotReuseDeletedRoot', 'details' => array('commitSet' => $destination_id, 'rootFile' => $root_file_id));
+
+ $db->update_row('commit_set_items', 'commitset', array('set' => $destination_id, 'commit' => $destination_item['commitset_commit']),
+ array('root_file' => $root_file_id), 'set');
+
+ array_push($root_file_ids, $root_file_id);
+ }
+
+ // There could be a race that those root files get purged after updating commit_set_items.
+ // Add another round of check to ensure they are not deleted by the end of this function to
+ // mitigate the race condition.
+ foreach ($root_file_ids as &$root_file_id) {
+ $root_file_row = $db->select_first_row('uploaded_files', 'file', array('id' => $root_file_id));
+ if ($root_file_row['file_deleted_at'])
+ return array('status' => 'CannotReuseDeletedRoot', 'details' => array('commitSet' => $destination_id, 'rootFile' => $root_file_id));
+ }
+
+ return NULL;
+}
+
main(array_get($_GET, 'id'),
array_key_exists('PATH_INFO', $_SERVER) ? explode('/', trim($_SERVER['PATH_INFO'], '/')) : array(),
file_get_contents("php://input"));
-?>
+?>
\ No newline at end of file
Modified: trunk/Websites/perf.webkit.org/public/v3/models/build-request.js (269870 => 269871)
--- trunk/Websites/perf.webkit.org/public/v3/models/build-request.js 2020-11-16 20:25:21 UTC (rev 269870)
+++ trunk/Websites/perf.webkit.org/public/v3/models/build-request.js 2020-11-16 20:32:08 UTC (rev 269871)
@@ -87,8 +87,45 @@
buildId() { return this._buildId; }
createdAt() { return this._createdAt; }
+ async findBuildRequestWithSameRoots()
+ {
+ if (!this.isBuild())
+ return null;
+ let scheduledBuildRequest = null;
+ let runningBuildRequest = null;
+ // Set ignoreCache = true as latest status of test groups is expected.
+ const allTestGroupsInTask = await TestGroup.fetchForTask(this.analysisTaskId(), true);
+ for (const group of allTestGroupsInTask) {
+ if (group.id() == this.testGroupId())
+ continue;
+ if (group.isHidden())
+ continue;
+ for (const buildRequest of group.buildRequests()) {
+ if (!buildRequest.isBuild())
+ continue;
+ if (!this.platform().isInSameGroupAs(buildRequest.platform()))
+ continue;
+ if (!buildRequest.commitSet().equalsIgnoringRoot(this.commitSet()))
+ continue;
+ if (!buildRequest.commitSet().areAllRootsAvailable())
+ continue;
+ if (buildRequest.hasCompleted())
+ return buildRequest;
+ if (buildRequest.isScheduled()
+ && (!scheduledBuildRequest || buildRequest.createdAt() < scheduledBuildRequest.createdAt())) {
+ scheduledBuildRequest = buildRequest;
+ }
+ if (buildRequest.status() == 'running'
+ && (!runningBuildRequest || buildRequest.createdAt() < runningBuildRequest.createdAt())) {
+ runningBuildRequest = buildRequest;
+ }
+ }
+ }
+ return runningBuildRequest || scheduledBuildRequest;
+ }
- static formatTimeInterval(intervalInMillionSeconds) {
+ static formatTimeInterval(intervalInMillionSeconds)
+ {
let intervalInSeconds = intervalInMillionSeconds / 1000;
const units = [
{unit: 'week', length: 7 * 24 * 3600},
Modified: trunk/Websites/perf.webkit.org/public/v3/models/commit-set.js (269870 => 269871)
--- trunk/Websites/perf.webkit.org/public/v3/models/commit-set.js 2020-11-16 20:25:21 UTC (rev 269870)
+++ trunk/Websites/perf.webkit.org/public/v3/models/commit-set.js 2020-11-16 20:32:08 UTC (rev 269871)
@@ -74,6 +74,11 @@
commitsWithTestability() { return this.commits().filter((commit) => !!commit.testability()); }
commits() { return Array.from(this._repositoryToCommitMap.values()); }
+ areAllRootsAvailable()
+ {
+ return this.allRootFiles().every(rootFile => !rootFile.deletedAt() || this.customRoots().find(rootFile));
+ }
+
revisionForRepository(repository)
{
var commit = this._repositoryToCommitMap.get(repository);
@@ -102,8 +107,18 @@
return this._latestCommitTime;
}
+ equalsIgnoringRoot(other)
+ {
+ return this._equalsOptionallyIgnoringRoot(other, true);
+ }
+
equals(other)
{
+ return this._equalsOptionallyIgnoringRoot(other, false);
+ }
+
+ _equalsOptionallyIgnoringRoot(other, ignoringRoot)
+ {
if (this._repositories.length != other._repositories.length)
return false;
for (const [repository, commit] of this._repositoryToCommitMap) {
@@ -111,7 +126,7 @@
return false;
if (this.patchForRepository(repository) != other.patchForRepository(repository))
return false;
- if (this.rootForRepository(repository) != other.rootForRepository(repository))
+ if (this.rootForRepository(repository) != other.rootForRepository(repository) && !ignoringRoot)
return false;
if (this.ownerCommitForRepository(repository) != other.ownerCommitForRepository(repository))
return false;
Modified: trunk/Websites/perf.webkit.org/public/v3/models/platform.js (269870 => 269871)
--- trunk/Websites/perf.webkit.org/public/v3/models/platform.js 2020-11-16 20:25:21 UTC (rev 269870)
+++ trunk/Websites/perf.webkit.org/public/v3/models/platform.js 2020-11-16 20:32:08 UTC (rev 269871)
@@ -24,6 +24,13 @@
return map ? map[name] : null;
}
+ isInSameGroupAs(other)
+ {
+ if (!this.group() && !other.group())
+ return this == other;
+ return this.group() == other.group();
+ }
+
hasTest(test)
{
if (!this._containingTests) {
Modified: trunk/Websites/perf.webkit.org/server-tests/api-build-requests-tests.js (269870 => 269871)
--- trunk/Websites/perf.webkit.org/server-tests/api-build-requests-tests.js 2020-11-16 20:25:21 UTC (rev 269870)
+++ trunk/Websites/perf.webkit.org/server-tests/api-build-requests-tests.js 2020-11-16 20:32:08 UTC (rev 269871)
@@ -5,6 +5,8 @@
let MockData = require('./resources/mock-data.js');
let TestServer = require('./resources/test-server.js');
const prepareServerTest = require('./resources/common-operations.js').prepareServerTest;
+const assertThrows = require('../server-tests/resources/common-operations').assertThrows;
+const crypto = require('crypto');
describe('/api/build-requests', function () {
prepareServerTest(this);
@@ -64,6 +66,416 @@
});
});
+ it('reuse roots from existing build requests if the commits sets are equal except the existence of roots', async () => {
+ await MockData.addMockBuildRequestsWithRoots(TestServer.database());
+ let content = await TestServer.remoteAPI().getJSONWithStatus('/api/build-requests/build-webkit');
+
+ assert.deepEqual(Object.keys(content).sort(), ['buildRequests', 'commitSets', 'commits', 'status', 'uploadedFiles']);
+ assert.equal(content['commitSets'].length, 4);
+ assert.equal(content['commitSets'][0].id, 500);
+ assert.equal(content['commitSets'][2].id, 600);
+
+ assert.deepEqual(content['commitSets'][0].revisionItems, [
+ {commit: '87832', commitOwner: null, patch: null, requiresBuild: false, rootFile: null},
+ {commit: '93116', commitOwner: null, patch: 100, requiresBuild: true, rootFile: 101}]);
+ assert.deepEqual(content['commitSets'][2].revisionItems, [
+ {commit: '87832', commitOwner: null, patch: null, requiresBuild: false, rootFile: null},
+ {commit: '93116', commitOwner: null, patch: 100, requiresBuild: true, rootFile: null}]);
+
+ assert.equal(content['buildRequests'].length, 8);
+ assert.equal(content['buildRequests'][0].id, 800);
+ assert.equal(content['buildRequests'][0].commitSet, 500);
+ assert.equal(content['buildRequests'][0].status, 'completed');
+ assert.equal(content['buildRequests'][0].url, 'http://build.webkit.org/buids/1');
+ assert.equal(content['buildRequests'][4].id, 900);
+ assert.equal(content['buildRequests'][4].commitSet, 600);
+ assert.equal(content['buildRequests'][4].status, 'pending');
+ assert.equal(content['buildRequests'][4].url, null);
+
+ const updates = {900: {
+ status: content['buildRequests'][0].status, url: content['buildRequests'][0].url, buildRequestForRootReuse: content['buildRequests'][0].id}};
+ const response = await TestServer.remoteAPI().postJSONWithStatus('/api/build-requests/build-webkit', {
+ 'slaveName': 'sync-slave',
+ 'slavePassword': 'password',
+ 'buildRequestUpdates': updates
+ });
+
+ content = await TestServer.remoteAPI().getJSONWithStatus('/api/build-requests/build-webkit');
+ assert.equal(content['commitSets'].length, 4);
+ assert.equal(content['commitSets'][0].id, 500);
+ assert.equal(content['commitSets'][2].id, 600);
+ assert.deepEqual(content['commitSets'][0].revisionItems, [
+ {commit: '87832', commitOwner: null, patch: null, requiresBuild: false, rootFile: null},
+ {commit: '93116', commitOwner: null, patch: 100, requiresBuild: true, rootFile: 101}]);
+ assert.deepEqual(content['commitSets'][2].revisionItems, [
+ {commit: '87832', commitOwner: null, patch: null, requiresBuild: false, rootFile: null},
+ {commit: '93116', commitOwner: null, patch: 100, requiresBuild: true, rootFile: 101}]);
+
+ assert.equal(content['buildRequests'].length, 8);
+ assert.equal(content['buildRequests'][0].id, 800);
+ assert.equal(content['buildRequests'][0].commitSet, 500);
+ assert.equal(content['buildRequests'][0].status, 'completed');
+ assert.equal(content['buildRequests'][0].url, 'http://build.webkit.org/buids/1');
+ assert.equal(content['buildRequests'][4].id, 900);
+ assert.equal(content['buildRequests'][4].commitSet, 600);
+ assert.equal(content['buildRequests'][4].status, 'completed');
+ assert.equal(content['buildRequests'][4].url, 'http://build.webkit.org/buids/1');
+ });
+
+ it('should reuse root built for owned commit if same completed build request exists', async () => {
+ await MockData.addTwoMockTestGroupWithOwnedCommits(TestServer.database());
+ let content = await TestServer.remoteAPI().getJSONWithStatus('/api/build-requests/build-webkit');
+ assert.deepEqual(Object.keys(content).sort(), ['buildRequests', 'commitSets', 'commits', 'status', 'uploadedFiles']);
+ assert.equal(content['commitSets'].length, 4);
+ assert.equal(content['commitSets'][0].id, 403);
+ assert.equal(content['commitSets'][2].id, 405);
+
+ assert.deepEqual(content['commitSets'][0].revisionItems, [
+ {commit: '87832', commitOwner: null, patch: null, requiresBuild: false, rootFile: null},
+ {commit: '93116', commitOwner: null, patch: null, requiresBuild: false, rootFile: null},
+ {commit: '1797', commitOwner: '93116', patch: null, requiresBuild: true, rootFile: 101}]);
+ assert.deepEqual(content['commitSets'][2].revisionItems, [
+ {commit: '87832', commitOwner: null, patch: null, requiresBuild: false, rootFile: null},
+ {commit: '93116', commitOwner: null, patch: null, requiresBuild: false, rootFile: null},
+ {commit: '1797', commitOwner: '93116', patch: null, requiresBuild: true, rootFile: null}]);
+
+ assert.equal(content['buildRequests'].length, 8);
+ assert.equal(content['buildRequests'][0].id, 704);
+ assert.equal(content['buildRequests'][0].commitSet, 403);
+ assert.equal(content['buildRequests'][0].status, 'completed');
+ assert.equal(content['buildRequests'][0].url, 'http://build.webkit.org/buids/1');
+ assert.equal(content['buildRequests'][4].id, 708);
+ assert.equal(content['buildRequests'][4].commitSet, 405);
+ assert.equal(content['buildRequests'][4].status, 'pending');
+ assert.equal(content['buildRequests'][4].url, null);
+
+ const updates = {708: {
+ status: content['buildRequests'][0].status, url: content['buildRequests'][0].url, buildRequestForRootReuse: 704}};
+
+ const response = await TestServer.remoteAPI().postJSONWithStatus('/api/build-requests/build-webkit', {
+ 'slaveName': 'sync-slave',
+ 'slavePassword': 'password',
+ 'buildRequestUpdates': updates
+ });
+
+ content = await TestServer.remoteAPI().getJSONWithStatus('/api/build-requests/build-webkit');
+ assert.deepEqual(Object.keys(content).sort(), ['buildRequests', 'commitSets', 'commits', 'status', 'uploadedFiles']);
+ assert.equal(content['commitSets'].length, 4);
+ assert.equal(content['commitSets'][0].id, 403);
+ assert.equal(content['commitSets'][2].id, 405);
+
+ assert.deepEqual(content['commitSets'][0].revisionItems, [
+ {commit: '87832', commitOwner: null, patch: null, requiresBuild: false, rootFile: null},
+ {commit: '93116', commitOwner: null, patch: null, requiresBuild: false, rootFile: null},
+ {commit: '1797', commitOwner: '93116', patch: null, requiresBuild: true, rootFile: 101}]);
+ assert.deepEqual(content['commitSets'][2].revisionItems, [
+ {commit: '87832', commitOwner: null, patch: null, requiresBuild: false, rootFile: null},
+ {commit: '93116', commitOwner: null, patch: null, requiresBuild: false, rootFile: null},
+ {commit: '1797', commitOwner: '93116', patch: null, requiresBuild: true, rootFile: 101}]);
+
+ assert.equal(content['buildRequests'].length, 8);
+ assert.equal(content['buildRequests'][0].id, 704);
+ assert.equal(content['buildRequests'][0].commitSet, 403);
+ assert.equal(content['buildRequests'][0].status, 'completed');
+ assert.equal(content['buildRequests'][0].url, 'http://build.webkit.org/buids/1');
+ assert.equal(content['buildRequests'][4].id, 708);
+ assert.equal(content['buildRequests'][4].commitSet, 405);
+ assert.equal(content['buildRequests'][4].status, 'completed');
+ assert.equal(content['buildRequests'][0].url, 'http://build.webkit.org/buids/1');
+ });
+
+ it('reuse roots from existing build requests if the commits sets are equal except the existence of custom roots', async () => {
+ const db = TestServer.database();
+ await MockData.addMockBuildRequestsWithRoots(db);
+ await Promise.all([
+ db.insert('uploaded_files', {id: 103, filename: 'custom-root-103', extension: '.tgz', size: 1, sha256: crypto.createHash('sha256').update('custom-root-103').digest('hex')}),
+ db.insert('commit_set_items', {set: 500, commit: null, patch_file: null, requires_build: false, root_file: 103}),
+ db.insert('uploaded_files', {id: 104, filename: 'custom-root-104', extension: '.tgz', size: 1, sha256: crypto.createHash('sha256').update('custom-root-104').digest('hex')}),
+ db.insert('commit_set_items', {set: 600, commit: null, patch_file: null, requires_build: false, root_file: 104}),
+ ]);
+ let content = await TestServer.remoteAPI().getJSONWithStatus('/api/build-requests/build-webkit');
+
+ assert.deepEqual(Object.keys(content).sort(), ['buildRequests', 'commitSets', 'commits', 'status', 'uploadedFiles']);
+ assert.equal(content['commitSets'].length, 4);
+ assert.equal(content['commitSets'][0].id, 500);
+ assert.equal(content['commitSets'][2].id, 600);
+
+ assert.deepEqual(content['commitSets'][0].revisionItems, [
+ {commit: '87832', commitOwner: null, patch: null, requiresBuild: false, rootFile: null},
+ {commit: '93116', commitOwner: null, patch: 100, requiresBuild: true, rootFile: 101}]);
+ assert.deepEqual(content['commitSets'][2].revisionItems, [
+ {commit: '87832', commitOwner: null, patch: null, requiresBuild: false, rootFile: null},
+ {commit: '93116', commitOwner: null, patch: 100, requiresBuild: true, rootFile: null}]);
+ assert.deepEqual(content['commitSets'][0].customRoots, [103]);
+ assert.deepEqual(content['commitSets'][2].customRoots, [104]);
+
+ assert.equal(content['buildRequests'].length, 8);
+ assert.equal(content['buildRequests'][0].id, 800);
+ assert.equal(content['buildRequests'][0].commitSet, 500);
+ assert.equal(content['buildRequests'][0].status, 'completed');
+ assert.equal(content['buildRequests'][0].url, 'http://build.webkit.org/buids/1');
+ assert.equal(content['buildRequests'][4].id, 900);
+ assert.equal(content['buildRequests'][4].commitSet, 600);
+ assert.equal(content['buildRequests'][4].status, 'pending');
+ assert.equal(content['buildRequests'][4].url, null);
+
+ const updates = {900: {
+ status: content['buildRequests'][0].status, url: content['buildRequests'][0].url, buildRequestForRootReuse: content['buildRequests'][0].id}};
+ const response = await TestServer.remoteAPI().postJSONWithStatus('/api/build-requests/build-webkit', {
+ 'slaveName': 'sync-slave',
+ 'slavePassword': 'password',
+ 'buildRequestUpdates': updates
+ });
+
+ content = await TestServer.remoteAPI().getJSONWithStatus('/api/build-requests/build-webkit');
+ assert.equal(content['commitSets'].length, 4);
+ assert.equal(content['commitSets'][0].id, 500);
+ assert.equal(content['commitSets'][2].id, 600);
+ assert.deepEqual(content['commitSets'][0].revisionItems, [
+ {commit: '87832', commitOwner: null, patch: null, requiresBuild: false, rootFile: null},
+ {commit: '93116', commitOwner: null, patch: 100, requiresBuild: true, rootFile: 101}]);
+ assert.deepEqual(content['commitSets'][2].revisionItems, [
+ {commit: '87832', commitOwner: null, patch: null, requiresBuild: false, rootFile: null},
+ {commit: '93116', commitOwner: null, patch: 100, requiresBuild: true, rootFile: 101}]);
+ assert.deepEqual(content['commitSets'][0].customRoots, [103]);
+ assert.deepEqual(content['commitSets'][2].customRoots, [104]);
+
+ assert.equal(content['buildRequests'].length, 8);
+ assert.equal(content['buildRequests'][0].id, 800);
+ assert.equal(content['buildRequests'][0].commitSet, 500);
+ assert.equal(content['buildRequests'][0].status, 'completed');
+ assert.equal(content['buildRequests'][0].url, 'http://build.webkit.org/buids/1');
+ assert.equal(content['buildRequests'][4].id, 900);
+ assert.equal(content['buildRequests'][4].commitSet, 600);
+ assert.equal(content['buildRequests'][4].status, 'completed');
+ assert.equal(content['buildRequests'][4].url, 'http://build.webkit.org/buids/1');
+ });
+
+ it('should fail request with "CannotReuseDeletedRoot" if any root to reuse is deleted', async () => {
+ await MockData.addMockBuildRequestsWithRoots(TestServer.database());
+ await TestServer.database().query("UPDATE uploaded_files SET file_deleted_at = now() at time zone 'utc' WHERE file_id=101");
+ const content = await TestServer.remoteAPI().getJSONWithStatus('/api/build-requests/build-webkit');
+
+ assert.deepEqual(Object.keys(content).sort(), ['buildRequests', 'commitSets', 'commits', 'status', 'uploadedFiles']);
+ assert.equal(content['commitSets'].length, 4);
+ assert.equal(content['commitSets'][0].id, 500);
+ assert.equal(content['commitSets'][2].id, 600);
+
+ assert.deepEqual(content['commitSets'][0].revisionItems, [
+ {commit: '87832', commitOwner: null, patch: null, requiresBuild: false, rootFile: null},
+ {commit: '93116', commitOwner: null, patch: 100, requiresBuild: true, rootFile: 101}]);
+ assert.deepEqual(content['commitSets'][2].revisionItems, [
+ {commit: '87832', commitOwner: null, patch: null, requiresBuild: false, rootFile: null},
+ {commit: '93116', commitOwner: null, patch: 100, requiresBuild: true, rootFile: null}]);
+
+ assert.equal(content['buildRequests'].length, 8);
+ assert.equal(content['buildRequests'][0].id, 800);
+ assert.equal(content['buildRequests'][0].commitSet, 500);
+ assert.equal(content['buildRequests'][0].status, 'completed');
+ assert.equal(content['buildRequests'][0].url, 'http://build.webkit.org/buids/1');
+ assert.equal(content['buildRequests'][4].id, 900);
+ assert.equal(content['buildRequests'][4].commitSet, 600);
+ assert.equal(content['buildRequests'][4].status, 'pending');
+ assert.equal(content['buildRequests'][4].url, null);
+
+ const updates = {900: {
+ status: content['buildRequests'][0].status, url: content['buildRequests'][0].url, buildRequestForRootReuse: content['buildRequests'][0].id}};
+ await assertThrows('CannotReuseDeletedRoot', () => TestServer.remoteAPI().postJSONWithStatus('/api/build-requests/build-webkit', {
+ 'slaveName': 'sync-slave',
+ 'slavePassword': 'password',
+ 'buildRequestUpdates': updates
+ }));
+ });
+
+ it('should fail request with "CannotReuseDeletedRoot" if any root to reuse is deleted while updating commit set items ', async () => {
+ await MockData.addMockBuildRequestsWithRoots(TestServer.database());
+ await TestServer.database().query(`CREATE OR REPLACE FUNCTION emunlate_file_purge() RETURNS TRIGGER AS $emunlate_file_purge$
+ BEGIN
+ UPDATE uploaded_files SET file_deleted_at = (CURRENT_TIMESTAMP AT TIME ZONE 'UTC') WHERE file_id = NEW.commitset_root_file;
+ RETURN NULL;
+ END;
+ $emunlate_file_purge$ LANGUAGE plpgsql;`);
+ await TestServer.database().query(`CREATE TRIGGER emunlate_file_purge AFTER UPDATE OF commitset_root_file ON commit_set_items
+ FOR EACH ROW EXECUTE PROCEDURE emunlate_file_purge();`);
+ const content = await TestServer.remoteAPI().getJSONWithStatus('/api/build-requests/build-webkit');
+
+ assert.deepEqual(Object.keys(content).sort(), ['buildRequests', 'commitSets', 'commits', 'status', 'uploadedFiles']);
+ assert.equal(content['commitSets'].length, 4);
+ assert.equal(content['commitSets'][0].id, 500);
+ assert.equal(content['commitSets'][2].id, 600);
+
+ assert.deepEqual(content['commitSets'][0].revisionItems, [
+ {commit: '87832', commitOwner: null, patch: null, requiresBuild: false, rootFile: null},
+ {commit: '93116', commitOwner: null, patch: 100, requiresBuild: true, rootFile: 101}]);
+ assert.deepEqual(content['commitSets'][2].revisionItems, [
+ {commit: '87832', commitOwner: null, patch: null, requiresBuild: false, rootFile: null},
+ {commit: '93116', commitOwner: null, patch: 100, requiresBuild: true, rootFile: null}]);
+
+ assert.equal(content['buildRequests'].length, 8);
+ assert.equal(content['buildRequests'][0].id, 800);
+ assert.equal(content['buildRequests'][0].commitSet, 500);
+ assert.equal(content['buildRequests'][0].status, 'completed');
+ assert.equal(content['buildRequests'][0].url, 'http://build.webkit.org/buids/1');
+ assert.equal(content['buildRequests'][4].id, 900);
+ assert.equal(content['buildRequests'][4].commitSet, 600);
+ assert.equal(content['buildRequests'][4].status, 'pending');
+ assert.equal(content['buildRequests'][4].url, null);
+
+ const updates = {900: {
+ status: content['buildRequests'][0].status, url: content['buildRequests'][0].url, buildRequestForRootReuse: content['buildRequests'][0].id}};
+
+ await assertThrows('CannotReuseDeletedRoot', () => TestServer.remoteAPI().postJSONWithStatus('/api/build-requests/build-webkit', {
+ 'slaveName': 'sync-slave',
+ 'slavePassword': 'password',
+ 'buildRequestUpdates': updates
+ }));
+ });
+
+ it('should fail request with "NoMatchingCommitSetItem" if build request to reuse does not have patch', async () => {
+ await MockData.addMockData(TestServer.database(), ['completed', 'pending', 'pending', 'pending'], true, ['http://build.webkit.org/buids/2', null, null, null]);
+ await MockData.addMockBuildRequestsWithRoots(TestServer.database(), ['running', 'pending', 'pending', 'pending', 'pending', 'pending', 'pending', 'pending'], true, false);
+ const content = await TestServer.remoteAPI().getJSONWithStatus('/api/build-requests/build-webkit');
+
+ assert.equal(content['commitSets'].length, 6);
+ assert.equal(content['commitSets'][0].id, 401);
+ assert.equal(content['commitSets'][4].id, 600);
+
+ assert.deepEqual(content['commitSets'][0].revisionItems, [
+ {commit: '87832', commitOwner: null, patch: null, requiresBuild: false, rootFile: null},
+ {commit: '93116', commitOwner: null, patch: null, requiresBuild: false, rootFile: null}]);
+ assert.deepEqual(content['commitSets'][4].revisionItems, [
+ {commit: '87832', commitOwner: null, patch: null, requiresBuild: false, rootFile: null},
+ {commit: '93116', commitOwner: null, patch: 100, requiresBuild: true, rootFile: null}]);
+
+ assert.equal(content['buildRequests'].length, 12);
+ assert.equal(content['buildRequests'][0].id, 700);
+ assert.equal(content['buildRequests'][0].commitSet, 401);
+ assert.equal(content['buildRequests'][0].status, 'completed');
+ assert.equal(content['buildRequests'][0].url, 'http://build.webkit.org/buids/2');
+ assert.equal(content['buildRequests'][8].id, 900);
+ assert.equal(content['buildRequests'][8].commitSet, 600);
+ assert.equal(content['buildRequests'][8].status, 'pending');
+ assert.equal(content['buildRequests'][8].url, null);
+
+ const updates = {900: {
+ status: content['buildRequests'][0].status, url: content['buildRequests'][0].url, buildRequestForRootReuse: content['buildRequests'][0].id}};
+
+ await assertThrows('NoMatchingCommitSetItem', () => TestServer.remoteAPI().postJSONWithStatus('/api/build-requests/build-webkit', {
+ 'slaveName': 'sync-slave',
+ 'slavePassword': 'password',
+ 'buildRequestUpdates': updates
+ }));
+ });
+
+ it('should fail request with "CannotReuseRootWithNonMatchingCommitSets" if commit sets have different number of entries', async () => {
+ await MockData.addMockData(TestServer.database(), ['completed', 'pending', 'pending', 'pending'], true, ['http://build.webkit.org/buids/2', null, null, null]);
+ await MockData.addMockBuildRequestsWithRoots(TestServer.database(), ['running', 'pending', 'pending', 'pending', 'pending', 'pending', 'pending', 'pending'], true, false);
+ await TestServer.database().insert('commit_set_items', {set: 401, commit: 111168})
+ const content = await TestServer.remoteAPI().getJSONWithStatus('/api/build-requests/build-webkit');
+
+ assert.equal(content['commitSets'].length, 6);
+ assert.equal(content['commitSets'][0].id, 401);
+ assert.equal(content['commitSets'][4].id, 600);
+
+ assert.deepEqual(content['commitSets'][0].revisionItems, [
+ {commit: '87832', commitOwner: null, patch: null, requiresBuild: false, rootFile: null},
+ {commit: '93116', commitOwner: null, patch: null, requiresBuild: false, rootFile: null},
+ {commit: '111168', commitOwner: null, patch: null, requiresBuild: false, rootFile: null}]);
+ assert.deepEqual(content['commitSets'][4].revisionItems, [
+ {commit: '87832', commitOwner: null, patch: null, requiresBuild: false, rootFile: null},
+ {commit: '93116', commitOwner: null, patch: 100, requiresBuild: true, rootFile: null}]);
+
+ assert.equal(content['buildRequests'].length, 12);
+ assert.equal(content['buildRequests'][0].id, 700);
+ assert.equal(content['buildRequests'][0].commitSet, 401);
+ assert.equal(content['buildRequests'][0].status, 'completed');
+ assert.equal(content['buildRequests'][0].url, 'http://build.webkit.org/buids/2');
+ assert.equal(content['buildRequests'][8].id, 900);
+ assert.equal(content['buildRequests'][8].commitSet, 600);
+ assert.equal(content['buildRequests'][8].status, 'pending');
+ assert.equal(content['buildRequests'][8].url, null);
+
+ const updates = {900: {
+ status: content['buildRequests'][0].status, url: content['buildRequests'][0].url, buildRequestForRootReuse: content['buildRequests'][0].id}};
+
+ await assertThrows('CannotReuseRootWithNonMatchingCommitSets', () => TestServer.remoteAPI().postJSONWithStatus('/api/build-requests/build-webkit', {
+ 'slaveName': 'sync-slave',
+ 'slavePassword': 'password',
+ 'buildRequestUpdates': updates
+ }));
+ });
+
+ it('should fail request with "CanOnlyReuseCompletedBuildRequest" if build request to reuse is not completed', async () => {
+ await MockData.addMockBuildRequestsWithRoots(TestServer.database(), ['running', 'pending', 'pending', 'pending', 'pending', 'pending', 'pending', 'pending']);
+ const content = await TestServer.remoteAPI().getJSONWithStatus('/api/build-requests/build-webkit');
+
+ assert.deepEqual(Object.keys(content).sort(), ['buildRequests', 'commitSets', 'commits', 'status', 'uploadedFiles']);
+ assert.equal(content['commitSets'].length, 4);
+ assert.equal(content['commitSets'][0].id, 500);
+ assert.equal(content['commitSets'][2].id, 600);
+
+ assert.deepEqual(content['commitSets'][0].revisionItems, [
+ {commit: '87832', commitOwner: null, patch: null, requiresBuild: false, rootFile: null},
+ {commit: '93116', commitOwner: null, patch: 100, requiresBuild: true, rootFile: 101}]);
+ assert.deepEqual(content['commitSets'][2].revisionItems, [
+ {commit: '87832', commitOwner: null, patch: null, requiresBuild: false, rootFile: null},
+ {commit: '93116', commitOwner: null, patch: 100, requiresBuild: true, rootFile: null}]);
+
+ assert.equal(content['buildRequests'].length, 8);
+ assert.equal(content['buildRequests'][0].id, 800);
+ assert.equal(content['buildRequests'][0].commitSet, 500);
+ assert.equal(content['buildRequests'][0].status, 'running');
+ assert.equal(content['buildRequests'][0].url, 'http://build.webkit.org/buids/1');
+ assert.equal(content['buildRequests'][4].id, 900);
+ assert.equal(content['buildRequests'][4].commitSet, 600);
+ assert.equal(content['buildRequests'][4].status, 'pending');
+ assert.equal(content['buildRequests'][4].url, null);
+
+ const updates = {900: {
+ status: content['buildRequests'][0].status, url: content['buildRequests'][0].url, buildRequestForRootReuse: content['buildRequests'][0].id}};
+ await assertThrows('CanOnlyReuseCompletedBuildRequest', () => TestServer.remoteAPI().postJSONWithStatus('/api/build-requests/build-webkit', {
+ 'slaveName': 'sync-slave',
+ 'slavePassword': 'password',
+ 'buildRequestUpdates': updates
+ }));
+ });
+
+ it('should fail request with "FailedToFindReuseBuildRequest" if the build request to reuse does not exist', async () => {
+ await MockData.addMockBuildRequestsWithRoots(TestServer.database());
+ const content = await TestServer.remoteAPI().getJSONWithStatus('/api/build-requests/build-webkit');
+
+ assert.deepEqual(Object.keys(content).sort(), ['buildRequests', 'commitSets', 'commits', 'status', 'uploadedFiles']);
+ assert.equal(content['commitSets'].length, 4);
+ assert.equal(content['commitSets'][0].id, 500);
+ assert.equal(content['commitSets'][2].id, 600);
+
+ assert.deepEqual(content['commitSets'][0].revisionItems, [
+ {commit: '87832', commitOwner: null, patch: null, requiresBuild: false, rootFile: null},
+ {commit: '93116', commitOwner: null, patch: 100, requiresBuild: true, rootFile: 101}]);
+ assert.deepEqual(content['commitSets'][2].revisionItems, [
+ {commit: '87832', commitOwner: null, patch: null, requiresBuild: false, rootFile: null},
+ {commit: '93116', commitOwner: null, patch: 100, requiresBuild: true, rootFile: null}]);
+
+ assert.equal(content['buildRequests'].length, 8);
+ assert.equal(content['buildRequests'][0].id, 800);
+ assert.equal(content['buildRequests'][0].commitSet, 500);
+ assert.equal(content['buildRequests'][0].status, 'completed');
+ assert.equal(content['buildRequests'][0].url, 'http://build.webkit.org/buids/1');
+ assert.equal(content['buildRequests'][4].id, 900);
+ assert.equal(content['buildRequests'][4].commitSet, 600);
+ assert.equal(content['buildRequests'][4].status, 'pending');
+ assert.equal(content['buildRequests'][4].url, null);
+
+ const updates = {900: {
+ status: content['buildRequests'][0].status, url: content['buildRequests'][0].url, buildRequestForRootReuse: 999}};
+
+ await assertThrows('FailedToFindbuildRequestForRootReuse', () => TestServer.remoteAPI().postJSONWithStatus('/api/build-requests/build-webkit', {
+ 'slaveName': 'sync-slave',
+ 'slavePassword': 'password',
+ 'buildRequestUpdates': updates
+ }));
+ });
+
it('should return build requests associated with a given triggerable with appropriate commits and commitSets', () => {
return MockData.addMockData(TestServer.database()).then(() => {
return TestServer.remoteAPI().getJSONWithStatus('/api/build-requests/build-webkit');
Modified: trunk/Websites/perf.webkit.org/server-tests/resources/mock-data.js (269870 => 269871)
--- trunk/Websites/perf.webkit.org/server-tests/resources/mock-data.js 2020-11-16 20:25:21 UTC (rev 269870)
+++ trunk/Websites/perf.webkit.org/server-tests/resources/mock-data.js 2020-11-16 20:32:08 UTC (rev 269871)
@@ -40,9 +40,9 @@
db.insert('repositories', {id: this.sharedRepositoryId(), name: 'Shared'}),
db.insert('repositories', {id: this.ownedJSCRepositoryId(), owner: this.webkitRepositoryId(), name: '_javascript_Core'}),
db.insert('repositories', {id: this.jscRepositoryId(), name: '_javascript_Core'}),
- db.insert('triggerable_repository_groups', {id: 2001, name: 'webkit-svn', triggerable: 1000}),
+ db.insert('triggerable_repository_groups', {id: 2001, name: 'webkit-svn', triggerable: 1000, accepts_roots: true}),
db.insert('triggerable_repositories', {repository: this.macosRepositoryId(), group: 2001}),
- db.insert('triggerable_repositories', {repository: this.webkitRepositoryId(), group: 2001}),
+ db.insert('triggerable_repositories', {repository: this.webkitRepositoryId(), group: 2001, accepts_patch: true}),
db.insert('commits', {id: 87832, repository: this.macosRepositoryId(), revision: '10.11 15A284'}),
db.insert('commits', {id: 93116, repository: this.webkitRepositoryId(), revision: '191622', time: (new Date(1445945816878)).toISOString()}),
db.insert('commits', {id: 96336, repository: this.webkitRepositoryId(), revision: '192736', time: (new Date(1448225325650)).toISOString()}),
@@ -64,10 +64,12 @@
db.insert('test_runs', {id: 801, config: 301, build: 901, mean_cache: 100}),
]);
},
- addMockData: function (db, statusList, needsNotification=true)
+ addMockData: function (db, statusList, needsNotification=true, urlList=[])
{
if (!statusList)
statusList = ['pending', 'pending', 'pending', 'pending'];
+ if (!urlList)
+ urlList = [null, null, null, null];
return Promise.all([
this.addMockConfiguration(db),
db.insert('commit_sets', {id: 401}),
@@ -80,12 +82,52 @@
start_run: 801, start_run_time: '2015-10-27T12:05:27.1Z',
end_run: 801, end_run_time: '2015-10-27T12:05:27.1Z'}),
db.insert('analysis_test_groups', {id: 600, task: 500, name: 'some test group', initial_repetition_count: 4, needs_notification: needsNotification}),
- db.insert('build_requests', {id: 700, status: statusList[0], triggerable: 1000, repository_group: 2001, platform: 65, test: 200, group: 600, order: 0, commit_set: 401}),
- db.insert('build_requests', {id: 701, status: statusList[1], triggerable: 1000, repository_group: 2001, platform: 65, test: 200, group: 600, order: 1, commit_set: 402}),
- db.insert('build_requests', {id: 702, status: statusList[2], triggerable: 1000, repository_group: 2001, platform: 65, test: 200, group: 600, order: 2, commit_set: 401}),
- db.insert('build_requests', {id: 703, status: statusList[3], triggerable: 1000, repository_group: 2001, platform: 65, test: 200, group: 600, order: 3, commit_set: 402}),
+ db.insert('build_requests', {id: 700, status: statusList[0], url: urlList[0], triggerable: 1000, repository_group: 2001, platform: 65, test: 200, group: 600, order: 0, commit_set: 401}),
+ db.insert('build_requests', {id: 701, status: statusList[1], url: urlList[1], triggerable: 1000, repository_group: 2001, platform: 65, test: 200, group: 600, order: 1, commit_set: 402}),
+ db.insert('build_requests', {id: 702, status: statusList[2], url: urlList[2], triggerable: 1000, repository_group: 2001, platform: 65, test: 200, group: 600, order: 2, commit_set: 401}),
+ db.insert('build_requests', {id: 703, status: statusList[3], url: urlList[3], triggerable: 1000, repository_group: 2001, platform: 65, test: 200, group: 600, order: 3, commit_set: 402}),
]);
},
+ addMockBuildRequestsWithRoots(db, statusList, needsNotification=true, addMockConfiguration=true)
+ {
+ const setupSteps = addMockConfiguration ? [this.addMockConfiguration(db)] : [];
+ if (!statusList)
+ statusList = ['completed', 'running', 'pending', 'pending', 'pending', 'pending', 'pending', 'pending'];
+ return Promise.all([
+ ...setupSteps,
+ db.insert('uploaded_files', {id: 100, filename: 'patch-100', extension: '.txt', size: 1, sha256: crypto.createHash('sha256').update('patch-100').digest('hex')}),
+ db.insert('uploaded_files', {id: 101, filename: 'root-101', extension: '.tgz', size: 1, sha256: crypto.createHash('sha256').update('root-101').digest('hex')}),
+ db.insert('uploaded_files', {id: 102, filename: 'patch-102', extension: '.txt', size: 1, sha256: crypto.createHash('sha256').update('patch-102').digest('hex')}),
+
+ db.insert('commit_sets', {id: 500}),
+ db.insert('commit_set_items', {set: 500, commit: 87832}),
+ db.insert('commit_set_items', {set: 500, commit: 93116, patch_file: 100, requires_build: true, root_file: 101}),
+ db.insert('commit_sets', {id: 501}),
+ db.insert('commit_set_items', {set: 501, commit: 87832}),
+ db.insert('commit_set_items', {set: 501, commit: 96336, patch_file: 102, requires_build: true}),
+
+ db.insert('commit_sets', {id: 600}),
+ db.insert('commit_set_items', {set: 600, commit: 87832}),
+ db.insert('commit_set_items', {set: 600, commit: 93116, patch_file: 100, requires_build: true}),
+ db.insert('commit_sets', {id: 601}),
+ db.insert('commit_set_items', {set: 601, commit: 87832}),
+ db.insert('commit_set_items', {set: 601, commit: 96336, patch_file: 102, requires_build: true}),
+
+ db.insert('analysis_tasks', {id: 600, name: 'another task'}),
+
+ db.insert('analysis_test_groups', {id: 700, task: 600, name: 'test with root built', initial_repetition_count: 1, needs_notification: needsNotification}),
+ db.insert('build_requests', {id: 800, status: statusList[0], triggerable: 1000, repository_group: 2001, platform: 65, group: 700, order: -2, commit_set: 500, url: 'http://build.webkit.org/buids/1'}),
+ db.insert('build_requests', {id: 801, status: statusList[1], triggerable: 1000, repository_group: 2001, platform: 65, group: 700, order: -1, commit_set: 501}),
+ db.insert('build_requests', {id: 802, status: statusList[2], triggerable: 1000, repository_group: 2001, platform: 65, test: 200, group: 700, order: 0, commit_set: 500}),
+ db.insert('build_requests', {id: 803, status: statusList[3], triggerable: 1000, repository_group: 2001, platform: 65, test: 200, group: 700, order: 1, commit_set: 501}),
+
+ db.insert('analysis_test_groups', {id: 701, task: 600, name: 'test will reuse root', initial_repetition_count: 1, needs_notification: needsNotification}),
+ db.insert('build_requests', {id: 900, status: statusList[4], triggerable: 1000, repository_group: 2001, platform: 65, group: 701, order: -2, commit_set: 600}),
+ db.insert('build_requests', {id: 901, status: statusList[5], triggerable: 1000, repository_group: 2001, platform: 65, group: 701, order: -1, commit_set: 601}),
+ db.insert('build_requests', {id: 902, status: statusList[6], triggerable: 1000, repository_group: 2001, platform: 65, test: 200, group: 701, order: 0, commit_set: 600}),
+ db.insert('build_requests', {id: 903, status: statusList[7], triggerable: 1000, repository_group: 2001, platform: 65, test: 200, group: 701, order: 1, commit_set: 601}),
+ ]);
+ },
addAnotherTriggerable(db) {
return Promise.all([
db.insert('build_triggerables', {id: 2345, name: 'build-webkit-jsc'}),
@@ -172,6 +214,45 @@
db.insert('build_requests', {id: 707, status: statusList[3], triggerable: 1000, repository_group: 2001, platform: 65, test: 200, group: 900, order: 3, commit_set: 404}),
]);
},
+ addTwoMockTestGroupWithOwnedCommits(db)
+ {
+ return Promise.all([
+ this.addMockConfiguration(db),
+ this.addAnotherTriggerable(db),
+ db.insert('analysis_tasks', {id: 1080, platform: 65, metric: 300, name: 'some task with component test',
+ start_run: 801, start_run_time: '2015-10-27T12:05:27.1Z',
+ end_run: 801, end_run_time: '2015-10-27T12:05:27.1Z'}),
+
+ db.insert('uploaded_files', {id: 101, filename: 'root-101', size: 1, sha256: crypto.createHash('sha256').update('root-101').digest('hex'), }),
+ db.insert('analysis_test_groups', {id: 900, task: 1080, name: 'some test group with component test(root built)', initial_repetition_count: 1}),
+ db.insert('commit_sets', {id: 403}),
+ db.insert('commit_set_items', {set: 403, commit: 87832}),
+ db.insert('commit_set_items', {set: 403, commit: 93116}),
+ db.insert('commit_set_items', {set: 403, commit: 1797, commit_owner: 93116, requires_build: true, root_file: 101}),
+ db.insert('commit_sets', {id: 404}),
+ db.insert('commit_set_items', {set: 404, commit: 87832}),
+ db.insert('commit_set_items', {set: 404, commit: 96336}),
+ db.insert('commit_set_items', {set: 404, commit: 2017, commit_owner: 96336, requires_build: true}),
+ db.insert('build_requests', {id: 704, status: 'completed', triggerable: 1000, repository_group: 2001, platform: 65, group: 900, order: -2, commit_set: 403, url: 'http://build.webkit.org/buids/1'}),
+ db.insert('build_requests', {id: 705, status: 'pending', triggerable: 1000, repository_group: 2001, platform: 65, group: 900, order: -1, commit_set: 404}),
+ db.insert('build_requests', {id: 706, status: 'pending', triggerable: 1000, repository_group: 2001, platform: 65, test: 200, group: 900, order: 0, commit_set: 403}),
+ db.insert('build_requests', {id: 707, status: 'pending', triggerable: 1000, repository_group: 2001, platform: 65, test: 200, group: 900, order: 1, commit_set: 404}),
+
+ db.insert('analysis_test_groups', {id: 901, task: 1080, name: 'some test group with component test', initial_repetition_count: 1}),
+ db.insert('commit_sets', {id: 405}),
+ db.insert('commit_set_items', {set: 405, commit: 87832}),
+ db.insert('commit_set_items', {set: 405, commit: 93116}),
+ db.insert('commit_set_items', {set: 405, commit: 1797, commit_owner: 93116, requires_build: true}),
+ db.insert('commit_sets', {id: 406}),
+ db.insert('commit_set_items', {set: 406, commit: 87832}),
+ db.insert('commit_set_items', {set: 406, commit: 96336}),
+ db.insert('commit_set_items', {set: 406, commit: 2017, commit_owner: 96336, requires_build: true}),
+ db.insert('build_requests', {id: 708, status: 'pending', triggerable: 1000, repository_group: 2001, platform: 65, group: 901, order: -2, commit_set: 405}),
+ db.insert('build_requests', {id: 709, status: 'pending', triggerable: 1000, repository_group: 2001, platform: 65, group: 901, order: -1, commit_set: 406}),
+ db.insert('build_requests', {id: 710, status: 'pending', triggerable: 1000, repository_group: 2001, platform: 65, test: 200, group: 901, order: 0, commit_set: 405}),
+ db.insert('build_requests', {id: 711, status: 'pending', triggerable: 1000, repository_group: 2001, platform: 65, test: 200, group: 901, order: 1, commit_set: 406}),
+ ]);
+ },
addTestGroupWithOwnerCommitNotInCommitSet(db)
{
return Promise.all([
@@ -219,6 +300,51 @@
]
}
},
+ mockTestSyncConfigWithPatchAcceptingBuilder: function ()
+ {
+ return {
+ 'triggerableName': 'build-webkit',
+ 'lookbackCount': 2,
+ 'buildRequestArgument': 'build-request-id',
+ 'repositoryGroups': {
+ 'webkit-svn': {
+ 'repositories': {'WebKit': {'acceptsPatch': true}, 'macOS': {}},
+ 'acceptsRoots': true,
+ 'testProperties': {
+ 'os': {'revision': 'macOS'},
+ 'wk': {'revision': 'WebKit'},
+ 'roots': {"roots": {}},
+ },
+ 'buildProperties': {
+ 'os': {'revision': 'macOS'},
+ 'wk': {'revision': 'WebKit'},
+ 'wk-patch': {'patch': 'WebKit'},
+ }
+ }
+ },
+ 'types': {
+ 'some-test': {'test': ['some test']}
+ },
+ 'builders': {
+ 'tester': {'builder': 'some tester', properties: {forcescheduler: 'force-some-tester'}},
+ 'builder-1': {'builder': 'some-builder-1', properties: {forcescheduler: 'force-some-builder-1'}},
+ 'builder-2': {'builder': 'some builder 2', properties: {forcescheduler: 'force-some-builder-2'}},
+ },
+ 'testConfigurations': [
+ {
+ 'platforms': ['some platform'],
+ 'types': ['some-test'],
+ 'builders': ['tester'],
+ }
+ ],
+ 'buildConfigurations': [
+ {
+ 'platforms': ['some platform'],
+ 'builders': ['builder-1', 'builder-2'],
+ }
+ ]
+ }
+ },
mockTestSyncConfigWithTwoBuilders: function ()
{
return {
Modified: trunk/Websites/perf.webkit.org/server-tests/tools-buildbot-triggerable-tests.js (269870 => 269871)
--- trunk/Websites/perf.webkit.org/server-tests/tools-buildbot-triggerable-tests.js 2020-11-16 20:25:21 UTC (rev 269870)
+++ trunk/Websites/perf.webkit.org/server-tests/tools-buildbot-triggerable-tests.js 2020-11-16 20:32:08 UTC (rev 269871)
@@ -946,6 +946,302 @@
MockRemoteAPI.requests[2].resolve('OK');
});
});
+
+ it('should reuse the roots from a completed build request with the same commit set', async () => {
+ await MockData.addMockBuildRequestsWithRoots(TestServer.database());
+ await Manifest.fetch();
+ const config = MockData.mockTestSyncConfigWithPatchAcceptingBuilder();
+ const logger = new MockLogger;
+ const slaveInfo = {name: 'sync-slave', password: 'password'};
+ const triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
+ const syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
+ assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURL(), MockData.mockBuildbotBuilders());
+ MockRemoteAPI.reset();
+ await MockRemoteAPI.waitForRequest();
+
+ assert.equal(BuildRequest.all().length, 8);
+ let buildRequest = BuildRequest.findById(800);
+ let anotherBuildRequest = BuildRequest.findById(900);
+ assert.equal(buildRequest.status(), 'completed');
+ assert.equal(anotherBuildRequest.status(), 'pending');
+ assert.equal(buildRequest.statusUrl(), 'http://build.webkit.org/buids/1');
+ assert.equal(anotherBuildRequest.statusUrl(), null);
+ let commitSet = buildRequest.commitSet();
+ let anotherCommitSet = anotherBuildRequest.commitSet();
+ assert.ok(commitSet.equalsIgnoringRoot(anotherCommitSet));
+ assert.ok(!commitSet.equals(anotherCommitSet));
+
+ assert.equal(MockRemoteAPI.requests.length, 3);
+ assert.equal(MockRemoteAPI.requests[0].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[0].url, MockData.pendingBuildsUrl('some tester'));
+ MockRemoteAPI.requests[0].resolve({});
+ assert.equal(MockRemoteAPI.requests[1].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[1].url, MockData.pendingBuildsUrl('some-builder-1'));
+ MockRemoteAPI.requests[1].resolve({});
+ assert.equal(MockRemoteAPI.requests[2].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[2].url, MockData.pendingBuildsUrl('some builder 2'));
+ MockRemoteAPI.requests[2].resolve({});
+ MockRemoteAPI.reset();
+ await MockRemoteAPI.waitForRequest();
+
+ assert.equal(MockRemoteAPI.requests.length, 3);
+ assert.equal(MockRemoteAPI.requests[0].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[0].url, MockData.recentBuildsUrl('some tester', 2));
+ MockRemoteAPI.requests[0].resolve({});
+ assert.equal(MockRemoteAPI.requests[1].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[1].url, MockData.recentBuildsUrl('some-builder-1', 2));
+ MockRemoteAPI.requests[1].resolve({'builds': [MockData.runningBuildData({buildRequestId: 801}), MockData.finishedBuildData({buildRequestId: 800})]});
+ assert.equal(MockRemoteAPI.requests[2].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[2].url, MockData.recentBuildsUrl('some builder 2', 2));
+ MockRemoteAPI.requests[2].resolve({});
+ MockRemoteAPI.reset();
+ await MockRemoteAPI.waitForRequest();
+
+ assert.equal(MockRemoteAPI.requests.length, 3);
+ assert.equal(MockRemoteAPI.requests[0].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[0].url, MockData.pendingBuildsUrl('some tester'));
+ MockRemoteAPI.requests[0].resolve({});
+ assert.equal(MockRemoteAPI.requests[1].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[1].url, MockData.pendingBuildsUrl('some-builder-1'));
+ MockRemoteAPI.requests[1].resolve({});
+ assert.equal(MockRemoteAPI.requests[2].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[2].url, MockData.pendingBuildsUrl('some builder 2'));
+ MockRemoteAPI.requests[2].resolve({});
+ MockRemoteAPI.reset();
+ await MockRemoteAPI.waitForRequest();
+
+ assert.equal(MockRemoteAPI.requests.length, 3);
+ assert.equal(MockRemoteAPI.requests[0].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[0].url, MockData.recentBuildsUrl('some tester', 2));
+ MockRemoteAPI.requests[0].resolve({});
+ assert.equal(MockRemoteAPI.requests[1].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[1].url, MockData.recentBuildsUrl('some-builder-1', 2));
+ MockRemoteAPI.requests[1].resolve({'builds': [MockData.runningBuildData({buildRequestId: 801}), MockData.finishedBuildData({buildRequestId: 800})]});
+ assert.equal(MockRemoteAPI.requests[2].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[2].url, MockData.recentBuildsUrl('some builder 2', 2));
+ MockRemoteAPI.requests[2].resolve({});
+ MockRemoteAPI.reset();
+
+ await syncPromise;
+ await BuildRequest.fetchForTriggerable(MockData.mockTestSyncConfigWithPatchAcceptingBuilder().triggerableName);
+ assert.equal(BuildRequest.all().length, 8);
+ buildRequest = BuildRequest.findById(800);
+ anotherBuildRequest = BuildRequest.findById(900);
+ assert.equal(buildRequest.status(), 'completed');
+ assert.equal(anotherBuildRequest.status(), 'completed');
+ assert.equal(buildRequest.statusUrl(), 'http://build.webkit.org/buids/1');
+ assert.equal(anotherBuildRequest.statusUrl(), 'http://build.webkit.org/buids/1');
+ });
+
+ it('should defer scheduling a build request if there is a "running" build request with same commit set, but should schedule the build request if "running" build request with same commit set fails later on', async () => {
+ await MockData.addMockBuildRequestsWithRoots(TestServer.database(), ['running', 'scheduled', 'pending', 'pending', 'pending', 'pending', 'pending', 'pending']);
+ await Manifest.fetch();
+ const config = MockData.mockTestSyncConfigWithPatchAcceptingBuilder();
+ const logger = new MockLogger;
+ const slaveInfo = {name: 'sync-slave', password: 'password'};
+ const triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
+ const syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
+ assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURL(), MockData.mockBuildbotBuilders());
+ MockRemoteAPI.reset();
+ await MockRemoteAPI.waitForRequest();
+
+ assert.equal(BuildRequest.all().length, 8);
+ let buildRequest = BuildRequest.findById(800);
+ let anotherBuildRequest = BuildRequest.findById(900);
+ assert.equal(buildRequest.status(), 'running');
+ assert.equal(anotherBuildRequest.status(), 'pending');
+ assert.equal(buildRequest.statusUrl(), 'http://build.webkit.org/buids/1');
+ assert.equal(anotherBuildRequest.statusUrl(), null);
+ let commitSet = buildRequest.commitSet();
+ let anotherCommitSet = anotherBuildRequest.commitSet();
+ assert.ok(commitSet.equalsIgnoringRoot(anotherCommitSet));
+ assert.ok(!commitSet.equals(anotherCommitSet));
+
+ assert.equal(MockRemoteAPI.requests.length, 3);
+ assert.equal(MockRemoteAPI.requests[0].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[0].url, MockData.pendingBuildsUrl('some tester'));
+ MockRemoteAPI.requests[0].resolve({});
+ assert.equal(MockRemoteAPI.requests[1].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[1].url, MockData.pendingBuildsUrl('some-builder-1'));
+ MockRemoteAPI.requests[1].resolve({'builds': [MockData.pendingBuild({buildRequestId: 801})]});
+ assert.equal(MockRemoteAPI.requests[2].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[2].url, MockData.pendingBuildsUrl('some builder 2'));
+ MockRemoteAPI.requests[2].resolve({});
+ MockRemoteAPI.reset();
+ await MockRemoteAPI.waitForRequest();
+
+ assert.equal(MockRemoteAPI.requests.length, 3);
+ assert.equal(MockRemoteAPI.requests[0].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[0].url, MockData.recentBuildsUrl('some tester', 2));
+ MockRemoteAPI.requests[0].resolve({});
+ assert.equal(MockRemoteAPI.requests[1].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[1].url, MockData.recentBuildsUrl('some-builder-1', 2));
+ MockRemoteAPI.requests[1].resolve({'builds': [MockData.runningBuildData({buildRequestId: 800})]});
+ assert.equal(MockRemoteAPI.requests[2].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[2].url, MockData.recentBuildsUrl('some builder 2', 2));
+ MockRemoteAPI.requests[2].resolve({});
+ MockRemoteAPI.reset();
+ await MockRemoteAPI.waitForRequest();
+
+ assert.equal(MockRemoteAPI.requests.length, 3);
+ assert.equal(MockRemoteAPI.requests[0].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[0].url, MockData.pendingBuildsUrl('some tester'));
+ MockRemoteAPI.requests[0].resolve({});
+ assert.equal(MockRemoteAPI.requests[1].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[1].url, MockData.pendingBuildsUrl('some-builder-1'));
+ MockRemoteAPI.requests[1].resolve({});
+ assert.equal(MockRemoteAPI.requests[2].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[2].url, MockData.pendingBuildsUrl('some builder 2'));
+ MockRemoteAPI.requests[2].resolve({});
+ MockRemoteAPI.reset();
+ await MockRemoteAPI.waitForRequest();
+
+ assert.equal(MockRemoteAPI.requests.length, 3);
+ assert.equal(MockRemoteAPI.requests[0].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[0].url, MockData.recentBuildsUrl('some tester', 2));
+ MockRemoteAPI.requests[0].resolve({});
+ assert.equal(MockRemoteAPI.requests[1].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[1].url, MockData.recentBuildsUrl('some-builder-1', 2));
+ MockRemoteAPI.requests[1].resolve({'builds': [MockData.runningBuildData({buildRequestId: 801}), MockData.finishedBuildData({buildRequestId: 800})]});
+ assert.equal(MockRemoteAPI.requests[2].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[2].url, MockData.recentBuildsUrl('some builder 2', 2));
+ MockRemoteAPI.requests[2].resolve({});
+ MockRemoteAPI.reset();
+
+ await syncPromise;
+ await BuildRequest.fetchForTriggerable(MockData.mockTestSyncConfigWithPatchAcceptingBuilder().triggerableName);
+ assert.equal(BuildRequest.all().length, 8);
+ buildRequest = BuildRequest.findById(800);
+ anotherBuildRequest = BuildRequest.findById(900);
+ assert.equal(buildRequest.status(), 'failed');
+ assert.equal(anotherBuildRequest.status(), 'pending');
+ assert.equal(buildRequest.statusUrl(), MockData.statusUrl('some-builder-1', 123));
+ assert.equal(anotherBuildRequest.statusUrl(), null);
+
+ const secondSyncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
+ assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURL(), MockData.mockBuildbotBuilders());
+ MockRemoteAPI.reset();
+ await MockRemoteAPI.waitForRequest();
+
+ assert.equal(MockRemoteAPI.requests.length, 3);
+ assert.equal(MockRemoteAPI.requests[0].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[0].url, MockData.pendingBuildsUrl('some tester'));
+ MockRemoteAPI.requests[0].resolve({});
+ assert.equal(MockRemoteAPI.requests[1].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[1].url, MockData.pendingBuildsUrl('some-builder-1'));
+ MockRemoteAPI.requests[1].resolve({});
+ assert.equal(MockRemoteAPI.requests[2].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[2].url, MockData.pendingBuildsUrl('some builder 2'));
+ MockRemoteAPI.requests[2].resolve({});
+ MockRemoteAPI.reset();
+ await MockRemoteAPI.waitForRequest();
+
+ assert.equal(MockRemoteAPI.requests.length, 3);
+ assert.equal(MockRemoteAPI.requests[0].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[0].url, MockData.recentBuildsUrl('some tester', 2));
+ MockRemoteAPI.requests[0].resolve({});
+ assert.equal(MockRemoteAPI.requests[1].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[1].url, MockData.recentBuildsUrl('some-builder-1', 2));
+ MockRemoteAPI.requests[1].resolve({'builds': [MockData.runningBuildData({buildRequestId: 801})]});
+ assert.equal(MockRemoteAPI.requests[2].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[2].url, MockData.recentBuildsUrl('some builder 2', 2));
+ MockRemoteAPI.requests[2].resolve({});
+ MockRemoteAPI.reset();
+ await MockRemoteAPI.waitForRequest();
+
+ assert.equal(MockRemoteAPI.requests.length, 1);
+ assert.equal(MockRemoteAPI.requests[0].method, 'POST');
+ assert.equal(MockRemoteAPI.requests[0].url, '/api/v2/forceschedulers/force-some-builder-2');
+ assert.deepEqual(MockRemoteAPI.requests[0].data, {'id': 900, 'jsonrpc': '2.0', 'method': 'force', 'params':
+ {'wk': '191622', 'os': '10.11 15A284', 'wk-patch': 'http://localhost:8180/api/uploaded-file/100.txt',
+ 'build-request-id': '900', 'forcescheduler': 'force-some-builder-2'}});
+ });
+
+ it('should not reuse the root when a build request with same commit set is avaialble but the build request has been scheduled', async () => {
+ await MockData.addMockBuildRequestsWithRoots(TestServer.database());
+ await Manifest.fetch();
+ const config = MockData.mockTestSyncConfigWithPatchAcceptingBuilder();
+ const logger = new MockLogger;
+ const slaveInfo = {name: 'sync-slave', password: 'password'};
+ const triggerable = new BuildbotTriggerable(config, TestServer.remoteAPI(), MockRemoteAPI, slaveInfo, logger);
+ const syncPromise = triggerable.initSyncers().then(() => triggerable.syncOnce());
+ assertRequestAndResolve(MockRemoteAPI.requests[0], 'GET', MockData.buildbotBuildersURL(), MockData.mockBuildbotBuilders());
+ MockRemoteAPI.reset();
+ await MockRemoteAPI.waitForRequest();
+
+ assert.equal(BuildRequest.all().length, 8);
+ let buildRequest = BuildRequest.findById(800);
+ let anotherBuildRequest = BuildRequest.findById(900);
+ assert.equal(buildRequest.status(), 'completed');
+ assert.equal(anotherBuildRequest.status(), 'pending');
+ assert.equal(buildRequest.statusUrl(), 'http://build.webkit.org/buids/1');
+ assert.equal(anotherBuildRequest.statusUrl(), null);
+ let commitSet = buildRequest.commitSet();
+ let anotherCommitSet = anotherBuildRequest.commitSet();
+ assert.ok(commitSet.equalsIgnoringRoot(anotherCommitSet));
+ assert.ok(!commitSet.equals(anotherCommitSet));
+
+ assert.equal(MockRemoteAPI.requests.length, 3);
+ assert.equal(MockRemoteAPI.requests[0].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[0].url, MockData.pendingBuildsUrl('some tester'));
+ MockRemoteAPI.requests[0].resolve({});
+ assert.equal(MockRemoteAPI.requests[1].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[1].url, MockData.pendingBuildsUrl('some-builder-1'));
+ MockRemoteAPI.requests[1].resolve({});
+ assert.equal(MockRemoteAPI.requests[2].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[2].url, MockData.pendingBuildsUrl('some builder 2'));
+ MockRemoteAPI.requests[2].resolve({});
+ MockRemoteAPI.reset();
+ await MockRemoteAPI.waitForRequest();
+
+ assert.equal(MockRemoteAPI.requests.length, 3);
+ assert.equal(MockRemoteAPI.requests[0].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[0].url, MockData.recentBuildsUrl('some tester', 2));
+ MockRemoteAPI.requests[0].resolve({});
+ assert.equal(MockRemoteAPI.requests[1].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[1].url, MockData.recentBuildsUrl('some-builder-1', 2));
+ MockRemoteAPI.requests[1].resolve({'builds': [MockData.runningBuildData({buildRequestId: 801}), MockData.finishedBuildData({buildRequestId: 800})]});
+ assert.equal(MockRemoteAPI.requests[2].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[2].url, MockData.recentBuildsUrl('some builder 2', 2));
+ MockRemoteAPI.requests[2].resolve({});
+ MockRemoteAPI.reset();
+ await MockRemoteAPI.waitForRequest();
+
+ assert.equal(MockRemoteAPI.requests.length, 3);
+ assert.equal(MockRemoteAPI.requests[0].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[0].url, MockData.pendingBuildsUrl('some tester'));
+ MockRemoteAPI.requests[0].resolve({});
+ assert.equal(MockRemoteAPI.requests[1].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[1].url, MockData.pendingBuildsUrl('some-builder-1'));
+ MockRemoteAPI.requests[1].resolve({});
+ assert.equal(MockRemoteAPI.requests[2].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[2].url, MockData.pendingBuildsUrl('some builder 2'));
+ MockRemoteAPI.requests[2].resolve({});
+ MockRemoteAPI.reset();
+ await MockRemoteAPI.waitForRequest();
+
+ assert.equal(MockRemoteAPI.requests.length, 3);
+ assert.equal(MockRemoteAPI.requests[0].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[0].url, MockData.recentBuildsUrl('some tester', 2));
+ MockRemoteAPI.requests[0].resolve({});
+ assert.equal(MockRemoteAPI.requests[1].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[1].url, MockData.recentBuildsUrl('some-builder-1', 2));
+ MockRemoteAPI.requests[1].resolve({'builds': [MockData.runningBuildData({buildRequestId: 801}), MockData.finishedBuildData({buildRequestId: 800})]});
+ assert.equal(MockRemoteAPI.requests[2].method, 'GET');
+ assert.equal(MockRemoteAPI.requests[2].url, MockData.recentBuildsUrl('some builder 2', 2));
+ MockRemoteAPI.requests[2].resolve({'builds': [MockData.runningBuildData({buildRequestId: 900, builderId: 3})]});
+ MockRemoteAPI.reset();
+
+ await syncPromise;
+ await BuildRequest.fetchForTriggerable(MockData.mockTestSyncConfigWithPatchAcceptingBuilder().triggerableName);
+ assert.equal(BuildRequest.all().length, 8);
+ buildRequest = BuildRequest.findById(800);
+ anotherBuildRequest = BuildRequest.findById(900);
+ assert.equal(buildRequest.status(), 'completed');
+ assert.equal(anotherBuildRequest.status(), 'running');
+ assert.equal(buildRequest.statusUrl(), 'http://build.webkit.org/buids/1');
+ assert.equal(anotherBuildRequest.statusUrl(), 'http://build.webkit.org/#/builders/3/builds/124');
+ });
});
describe('updateTriggerables', () => {
Modified: trunk/Websites/perf.webkit.org/tools/js/buildbot-triggerable.js (269870 => 269871)
--- trunk/Websites/perf.webkit.org/tools/js/buildbot-triggerable.js 2020-11-16 20:25:21 UTC (rev 269870)
+++ trunk/Websites/perf.webkit.org/tools/js/buildbot-triggerable.js 2020-11-16 20:32:08 UTC (rev 269871)
@@ -77,42 +77,56 @@
})});
}
- syncOnce()
+ async syncOnce()
{
- let syncerList = this._syncers;
- let buildReqeustsByGroup = new Map;
+ this._logger.log(`Fetching build requests for ${this._name}...`);
- this._logger.log(`Fetching build requests for ${this._name}...`);
- let validRequests;
- return BuildRequest.fetchForTriggerable(this._name).then((buildRequests) => {
- validRequests = this._validateRequests(buildRequests);
- buildReqeustsByGroup = BuildbotTriggerable._testGroupMapForBuildRequests(buildRequests);
- return this._pullBuildbotOnAllSyncers(buildReqeustsByGroup);
- }).then((updates) => {
- this._logger.log('Scheduling builds');
- const promistList = [];
- const testGroupList = Array.from(buildReqeustsByGroup.values()).sort(function (a, b) { return a.groupOrder - b.groupOrder; });
- for (const group of testGroupList) {
- const nextRequest = this._nextRequestInGroup(group, updates);
- if (!validRequests.has(nextRequest))
- continue;
- const promise = this._scheduleRequestIfSlaveIsAvailable(nextRequest, group.requests,
- nextRequest.isBuild() ? group.buildSyncer : group.testSyncer,
- nextRequest.isBuild() ? group.buildSlaveName : group.testSlaveName);
- if (promise)
- promistList.push(promise);
+ const buildRequests = await BuildRequest.fetchForTriggerable(this._name);
+ const validRequests = this._validateRequests(buildRequests);
+ const buildReqeustsByGroup = BuildbotTriggerable._testGroupMapForBuildRequests(buildRequests);
+ let updates = await this._pullBuildbotOnAllSyncers(buildReqeustsByGroup);
+ let rootReuseUpdates = {}
+ this._logger.log('Scheduling builds');
+ const promiseList = [];
+ const testGroupList = Array.from(buildReqeustsByGroup.values()).sort(function (a, b) { return a.groupOrder - b.groupOrder; });
+
+ await Promise.all(testGroupList.map((group) => [group, this._nextRequestInGroup(group, updates)])
+ .filter(([group, request]) => validRequests.has(request))
+ .map(([group, request]) => this._scheduleRequest(group, request, rootReuseUpdates)));
+
+ // Pull all buildbots for the second time since the previous step may have scheduled more builds
+ updates = await this._pullBuildbotOnAllSyncers(buildReqeustsByGroup);
+
+ // rootReuseUpdates will be overridden by status fetched from buildbot.
+ updates = {
+ ...rootReuseUpdates,
+ ...updates
+ };
+ return await this._remote.postJSONWithStatus(`/api/build-requests/${this._name}`, {
+ 'slaveName': this._slaveInfo.name,
+ 'slavePassword': this._slaveInfo.password,
+ 'buildRequestUpdates': updates});
+ }
+
+ async _scheduleRequest(testGroup, buildRequest, updates)
+ {
+ const buildRequestForRootReuse = await buildRequest.findBuildRequestWithSameRoots();
+ if (buildRequestForRootReuse) {
+ if (!buildRequestForRootReuse.hasCompleted()) {
+ this._logger.log(`Found build request ${buildRequestForRootReuse.id()} is building the same root, will wait until it finishes.`);
+ return;
}
- return Promise.all(promistList);
- }).then(() => {
- // Pull all buildbots for the second time since the previous step may have scheduled more builds.
- return this._pullBuildbotOnAllSyncers(buildReqeustsByGroup);
- }).then((updates) => {
- // FIXME: Add a new API that just updates the requests.
- return this._remote.postJSONWithStatus(`/api/build-requests/${this._name}`, {
- 'slaveName': this._slaveInfo.name,
- 'slavePassword': this._slaveInfo.password,
- 'buildRequestUpdates': updates});
- });
+
+ this._logger.log(`Will reuse existing root built from ${buildRequestForRootReuse.id()} for ${buildRequest.id()}`);
+ updates[buildRequest.id()] = {status: 'completed', url: buildRequestForRootReuse.statusUrl(),
+ statusDescription: buildRequestForRootReuse.statusDescription(),
+ buildRequestForRootReuse: buildRequestForRootReuse.id()};
+ return;
+ }
+
+ return await this._scheduleRequestIfSlaveIsAvailable(buildRequest, testGroup.requests,
+ buildRequest.isBuild() ? testGroup.buildSyncer : testGroup.testSyncer,
+ buildRequest.isBuild() ? testGroup.buildSlaveName : testGroup.testSlaveName);
}
_validateRequests(buildRequests)
Modified: trunk/Websites/perf.webkit.org/unit-tests/build-request-tests.js (269870 => 269871)
--- trunk/Websites/perf.webkit.org/unit-tests/build-request-tests.js 2020-11-16 20:25:21 UTC (rev 269870)
+++ trunk/Websites/perf.webkit.org/unit-tests/build-request-tests.js 2020-11-16 20:32:08 UTC (rev 269871)
@@ -3,7 +3,9 @@
const assert = require('assert');
require('../tools/js/v3-models.js');
-let MockModels = require('./resources/mock-v3-models.js').MockModels;
+const MockModels = require('./resources/mock-v3-models.js').MockModels;
+const NodePrivilegedAPI = require('../tools/js/privileged-api.js').PrivilegedAPI;
+const MockRemoteAPI = require('./resources/mock-remote-api.js').MockRemoteAPI;
function sampleBuildRequestData()
{
@@ -56,9 +58,531 @@
};
}
+function oneTestGroup()
+{
+ return {
+ "testGroups": [{
+ "id": "2128",
+ "task": "1376",
+ "platform": "31",
+ "name": "Confirm",
+ "author": "rniwa",
+ "createdAt": 1458688514000,
+ "hidden": false,
+ "needsNotification": true,
+ "buildRequests": ["16985", "16986", "16987", "16988"],
+ "commitSets": ["4255", "4256"],
+ "notificationSentAt": null,
+ 'initialRepetitionCount': 1
+ }],
+ "buildRequests": [{
+ "id": "16985",
+ "triggerable": "3",
+ "task": "1376",
+ "test": "844",
+ "platform": "31",
+ "testGroup": "2128",
+ "order": "-2",
+ "commitSet": "4255",
+ "status": "pending",
+ "url": null,
+ "build": null,
+ "createdAt": 1458688514000
+ }, {
+ "id": "16986",
+ "triggerable": "3",
+ "task": "1376",
+ "test": "844",
+ "platform": "31",
+ "testGroup": "2128",
+ "order": "-1",
+ "commitSet": "4256",
+ "status": "pending",
+ "url": null,
+ "build": null,
+ "createdAt": 1458688514000
+ }, {
+ "id": "16987",
+ "triggerable": "3",
+ "task": "1376",
+ "test": "844",
+ "platform": "31",
+ "testGroup": "2128",
+ "order": "0",
+ "commitSet": "4255",
+ "status": "pending",
+ "url": null,
+ "build": null,
+ "createdAt": 1458688514000
+ }, {
+ "id": "16988",
+ "triggerable": "3",
+ "task": "1376",
+ "test": "844",
+ "platform": "31",
+ "testGroup": "2128",
+ "order": "3",
+ "commitSet": "4256",
+ "status": "pending",
+ "url": null,
+ "build": null,
+ "createdAt": 1458688514000
+ }],
+ "commitSets": [{
+ "id": "4255",
+ "revisionItems": [{"commit": "87832"}, {"commit": "93116"}],
+ "customRoots": [],
+ }, {
+ "id": "4256",
+ "revisionItems": [{"commit": "87832"}, {"commit": "96336"}],
+ "customRoots": [],
+ }],
+ "commits": [{
+ "id": "87832",
+ "repository": "9",
+ "revision": "10.11 15A284",
+ "time": 0
+ }, {
+ "id": "93116",
+ "repository": "11",
+ "revision": "191622",
+ "time": 1445945816878
+ }, {
+ "id": "87832",
+ "repository": "9",
+ "revision": "10.11 15A284",
+ "time": 0
+ }, {
+ "id": "96336",
+ "repository": "11",
+ "revision": "192736",
+ "time": 1448225325650
+ }],
+ "uploadedFiles": [],
+ "status": "OK"
+ };
+}
+
+function threeTestGroups(secondTestGroupOverrides, thirdTestGroupOverrides)
+{
+ if (!secondTestGroupOverrides)
+ secondTestGroupOverrides = {};
+ if (!thirdTestGroupOverrides)
+ thirdTestGroupOverrides = {};
+ return {
+ "testGroups": [{
+ "id": "2128",
+ "task": "1376",
+ "platform": "32",
+ "name": "Confirm",
+ "author": "rniwa",
+ "createdAt": 1458688514000,
+ "hidden": false,
+ "needsNotification": true,
+ "buildRequests": ["16985", "16986", "16987", "16988"],
+ "commitSets": ["4255", "4256"],
+ "notificationSentAt": null,
+ 'initialRepetitionCount': 1
+ }, {
+ "id": "2129",
+ "task": secondTestGroupOverrides.task || '1376',
+ "platform": secondTestGroupOverrides.platform || '32',
+ "name": "Confirm",
+ "author": "rniwa",
+ "createdAt": 1458688514000,
+ "hidden": false,
+ "needsNotification": true,
+ "buildRequests": ["16989", "16990", "16991", "16992"],
+ "commitSets": ["4255", "4256"],
+ "notificationSentAt": null,
+ 'initialRepetitionCount': 1
+ }, {
+ "id": "2130",
+ "task": thirdTestGroupOverrides.task || '1376',
+ "platform": thirdTestGroupOverrides.platform || '32',
+ "name": "Confirm",
+ "author": "rniwa",
+ "createdAt": 1458688514000,
+ "hidden": false,
+ "needsNotification": true,
+ "buildRequests": ["16993", "16994", "16995", "16996"],
+ "commitSets": ["4255", "4256"],
+ "notificationSentAt": null,
+ 'initialRepetitionCount': 1
+ }],
+ "buildRequests": [{
+ "id": "16985",
+ "triggerable": "3",
+ "task": "1376",
+ "test": "844",
+ "platform": "32",
+ "testGroup": "2128",
+ "order": "-2",
+ "commitSet": "4255",
+ "status": "pending",
+ "url": null,
+ "build": null,
+ "createdAt": 1458688514000
+ }, {
+ "id": "16986",
+ "triggerable": "3",
+ "task": "1376",
+ "test": "844",
+ "platform": "32",
+ "testGroup": "2128",
+ "order": "-1",
+ "commitSet": "4256",
+ "status": "pending",
+ "url": null,
+ "build": null,
+ "createdAt": 1458688514000
+ }, {
+ "id": "16987",
+ "triggerable": "3",
+ "task": "1376",
+ "test": "844",
+ "platform": "32",
+ "testGroup": "2128",
+ "order": "0",
+ "commitSet": "4255",
+ "status": "pending",
+ "url": null,
+ "build": null,
+ "createdAt": 1458688514000
+ }, {
+ "id": "16988",
+ "triggerable": "3",
+ "task": "1376",
+ "test": "844",
+ "platform": "32",
+ "testGroup": "2128",
+ "order": "1",
+ "commitSet": "4256",
+ "status": "pending",
+ "url": null,
+ "build": null,
+ "createdAt": 1458688514000
+ }, {
+ "id": "16989",
+ "triggerable": "3",
+ "task": secondTestGroupOverrides.task || '1376',
+ "test": "844",
+ "platform": secondTestGroupOverrides.platform || '32',
+ "testGroup": "2129",
+ "order": "-2",
+ "commitSet": "4255",
+ "status": secondTestGroupOverrides.status && secondTestGroupOverrides.status[0] || 'pending',
+ "url": null,
+ "build": null,
+ "createdAt": 1458688514000
+ }, {
+ "id": "16990",
+ "triggerable": "3",
+ "task": secondTestGroupOverrides.task || '1376',
+ "test": "844",
+ "platform": secondTestGroupOverrides.platform || '32',
+ "testGroup": "2129",
+ "order": "-1",
+ "commitSet": "4256",
+ "status": secondTestGroupOverrides.status && secondTestGroupOverrides.status[1] || 'pending',
+ "url": null,
+ "build": null,
+ "createdAt": 1458688514000
+ }, {
+ "id": "16991",
+ "triggerable": "3",
+ "task": secondTestGroupOverrides.task || '1376',
+ "test": "844",
+ "platform": secondTestGroupOverrides.platform || '32',
+ "testGroup": "2129",
+ "order": "0",
+ "commitSet": "4255",
+ "status": secondTestGroupOverrides.status && secondTestGroupOverrides.status[2] || 'pending',
+ "url": null,
+ "build": null,
+ "createdAt": 1458688514000
+ }, {
+ "id": "16992",
+ "triggerable": "3",
+ "task": secondTestGroupOverrides.task || '1376',
+ "test": "844",
+ "platform": secondTestGroupOverrides.platform || '32',
+ "testGroup": "2129",
+ "order": "3",
+ "commitSet": "4256",
+ "status": secondTestGroupOverrides.status && secondTestGroupOverrides.status[3] || 'pending',
+ "url": null,
+ "build": null,
+ "createdAt": 1458688514000
+ }, {
+ "id": "16993",
+ "triggerable": "3",
+ "task": thirdTestGroupOverrides.task || '1376',
+ "test": "844",
+ "platform": thirdTestGroupOverrides.platform || '32',
+ "testGroup": "2130",
+ "order": "-2",
+ "commitSet": "4255",
+ "status": thirdTestGroupOverrides.status && thirdTestGroupOverrides.status[0] || 'pending',
+ "url": null,
+ "build": null,
+ "createdAt": 1458688513000
+ }, {
+ "id": "16994",
+ "triggerable": "3",
+ "task": thirdTestGroupOverrides.task || '1376',
+ "test": "844",
+ "platform": thirdTestGroupOverrides.platform || '32',
+ "testGroup": "2130",
+ "order": "-1",
+ "commitSet": "4256",
+ "status": thirdTestGroupOverrides.status && thirdTestGroupOverrides.status[1] || 'pending',
+ "url": null,
+ "build": null,
+ "createdAt": 1458688514000
+ }, {
+ "id": "16995",
+ "triggerable": "3",
+ "task": thirdTestGroupOverrides.task || '1376',
+ "test": "844",
+ "platform": thirdTestGroupOverrides.platform || '32',
+ "testGroup": "2130",
+ "order": "0",
+ "commitSet": "4255",
+ "status": thirdTestGroupOverrides.status && thirdTestGroupOverrides.status[2] || 'pending',
+ "url": null,
+ "build": null,
+ "createdAt": 1458688514000
+ }, {
+ "id": "16996",
+ "triggerable": "3",
+ "task": secondTestGroupOverrides.task || '1376',
+ "test": "844",
+ "platform": secondTestGroupOverrides.platform || '32',
+ "testGroup": "2130",
+ "order": "1",
+ "commitSet": "4256",
+ "status": thirdTestGroupOverrides.status && thirdTestGroupOverrides.status[3] || 'pending',
+ "url": null,
+ "build": null,
+ "createdAt": 1458688514000
+ }],
+ "commitSets": [{
+ "id": "4255",
+ "revisionItems": [{"commit": "87832"}, {"commit": "93116"}],
+ "customRoots": [],
+ }, {
+ "id": "4256",
+ "revisionItems": [{"commit": "87832"}, {"commit": "96336"}],
+ "customRoots": [],
+ }],
+ "commits": [{
+ "id": "87832",
+ "repository": "9",
+ "revision": "10.11 15A284",
+ "time": 0
+ }, {
+ "id": "93116",
+ "repository": "11",
+ "revision": "191622",
+ "time": 1445945816878
+ }, {
+ "id": "87832",
+ "repository": "9",
+ "revision": "10.11 15A284",
+ "time": 0
+ }, {
+ "id": "96336",
+ "repository": "11",
+ "revision": "192736",
+ "time": 1448225325650
+ }],
+ "uploadedFiles": [],
+ "status": "OK"
+ };
+}
+
describe('BuildRequest', function () {
MockModels.inject();
+ describe('findBuildRequestWithSameRoots', () => {
+ const requests = MockRemoteAPI.inject('https://perf.webkit.org');
+
+ it('should return null when the build request is not build type build request', async () => {
+ const data = ""
+ const request = BuildRequest.constructBuildRequestsFromData(data)[0];
+ const result = await request.findBuildRequestWithSameRoots();
+ assert.equal(result, null);
+ });
+
+ it('should return null if this there is no other test group under current analysis task', async () => {
+ const data = ""
+ const platformId = data.buildRequests[0].platform;
+ Platform.ensureSingleton(platformId, {id: platformId, metrics: [], name: 'some platform'});
+ const request = BuildRequest.constructBuildRequestsFromData(data)[0];
+ const promise = request.findBuildRequestWithSameRoots();
+ assert.equal(requests.length, 1);
+
+ assert.equal(requests[0].url, '/api/test-groups?task=1376');
+ assert.equal(requests[0].method, 'GET');
+ requests[0].resolve(oneTestGroup());
+
+ const result = await promise;
+ assert.equal(result, null);
+ });
+
+ it('should return completed build request if a completed reusable build request found', async () => {
+ const overrides = {
+ task: '1376',
+ platform: '32',
+ status: ['completed', 'pending', 'pending', 'pending']
+ }
+ const data = ""
+ const platformId = data.buildRequests[0].platform;
+ Platform.ensureSingleton(platformId, {id: platformId, metrics: [], name: 'some platform'});
+ const request = BuildRequest.constructBuildRequestsFromData(data)[0];
+ const promise = request.findBuildRequestWithSameRoots();
+ assert.equal(requests.length, 1);
+
+ assert.equal(requests[0].url, '/api/test-groups?task=1376');
+ assert.equal(requests[0].method, 'GET');
+ requests[0].resolve(threeTestGroups(overrides));
+
+ const result = await promise;
+ assert.equal(result, BuildRequest.findById(16989))
+ });
+
+ it('should not use cache while fetching test groups under analysis task', async () => {
+ const overrides = {
+ task: '1376',
+ platform: '32',
+ status: ['completed', 'pending', 'pending', 'pending']
+ };
+ const data = ""
+ const platformId = data.buildRequests[0].platform;
+ Platform.ensureSingleton(platformId, {id: platformId, metrics: [], name: 'some platform'});
+ const request = BuildRequest.constructBuildRequestsFromData(data)[0];
+ let promise = request.findBuildRequestWithSameRoots();
+ assert.equal(requests.length, 1);
+
+ assert.equal(requests[0].url, '/api/test-groups?task=1376');
+ assert.equal(requests[0].method, 'GET');
+ requests[0].resolve(threeTestGroups(overrides));
+
+ let result = await promise;
+ assert.equal(result, BuildRequest.findById(16989))
+
+ MockRemoteAPI.reset();
+ promise = request.findBuildRequestWithSameRoots();
+ assert.equal(requests.length, 1);
+
+ assert.equal(requests[0].url, '/api/test-groups?task=1376');
+ assert.equal(requests[0].method, 'GET');
+ requests[0].resolve(threeTestGroups(overrides));
+
+ result = await promise;
+ assert.equal(result, BuildRequest.findById(16989))
+ });
+
+ it('should only allow identical platform if the platform is not under a platform group', async () => {
+ const overrides = {
+ task: '1376',
+ platform: '33',
+ status: ['completed', 'pending', 'pending', 'pending']
+ };
+ const data = ""
+ const platformId = data.buildRequests[0].platform;
+ Platform.ensureSingleton(platformId, {id: platformId, metrics: [], name: 'some platform'});
+ Platform.ensureSingleton('33', {id: '33', metrics: [], name: 'another platform'});
+ const request = BuildRequest.constructBuildRequestsFromData(data)[0];
+ const promise = request.findBuildRequestWithSameRoots();
+ assert.equal(requests.length, 1);
+
+ assert.equal(requests[0].url, '/api/test-groups?task=1376');
+ assert.equal(requests[0].method, 'GET');
+ requests[0].resolve(threeTestGroups(overrides));
+
+ const result = await promise;
+ assert.equal(result, null);
+ });
+
+ it('should allow different platform if the platforms are under the same platform group', async () => {
+ const overrides = {
+ task: '1376',
+ platform: '33',
+ status: ['completed', 'pending', 'pending', 'pending']
+ };
+ const data = ""
+ const platformId = data.buildRequests[0].platform;
+ const platformGroup = PlatformGroup.ensureSingleton('1', {id: 1, name: 'some platform group'});
+ Platform.ensureSingleton(platformId, {id: platformId, metrics: [], name: 'some platform', group: platformGroup});
+ Platform.ensureSingleton('33', {id: '33', metrics: [], name: 'another platform', group: platformGroup});
+ const request = BuildRequest.constructBuildRequestsFromData(data)[0];
+ const promise = request.findBuildRequestWithSameRoots();
+ assert.equal(requests.length, 1);
+
+ assert.equal(requests[0].url, '/api/test-groups?task=1376');
+ assert.equal(requests[0].method, 'GET');
+ requests[0].resolve(threeTestGroups(overrides));
+
+ const result = await promise;
+ assert.equal(result, BuildRequest.findById(16989))
+ });
+
+ it('should in favor of running build request over scheduled build request', async () => {
+ const secondOverrides = {
+ task: '1376',
+ platform: '32',
+ status: ['scheduled', 'pending', 'pending', 'pending']
+ };
+ const thirdOverrides = {
+ task: '1376',
+ platform: '32',
+ status: ['running', 'pending', 'pending', 'pending']
+ }
+ const data = "" thirdOverrides);
+ const platformId = data.buildRequests[0].platform;
+ Platform.ensureSingleton(platformId, {id: platformId, metrics: [], name: 'some platform'});
+ const request = BuildRequest.constructBuildRequestsFromData(data)[0];
+ const promise = request.findBuildRequestWithSameRoots();
+ assert.equal(requests.length, 1);
+
+ assert.equal(requests[0].url, '/api/test-groups?task=1376');
+ assert.equal(requests[0].method, 'GET');
+ requests[0].resolve(threeTestGroups(secondOverrides, thirdOverrides));
+
+ const result = await promise;
+ assert.equal(result, BuildRequest.findById(16993))
+ });
+
+ it('should in favor of running build request which has earlier creation time', async () => {
+ const secondOverrides = {
+ task: '1376',
+ platform: '32',
+ status: ['running', 'pending', 'pending', 'pending']
+ };
+ const thirdOverrides = {
+ task: '1376',
+ platform: '32',
+ status: ['running', 'pending', 'pending', 'pending']
+ }
+ const data = "" thirdOverrides);
+ const platformId = data.buildRequests[0].platform;
+ Platform.ensureSingleton(platformId, {id: platformId, metrics: [], name: 'some platform'});
+ const request = BuildRequest.constructBuildRequestsFromData(data)[0];
+ const promise = request.findBuildRequestWithSameRoots();
+ assert.equal(requests.length, 1);
+
+ assert.equal(requests[0].url, '/api/test-groups?task=1376');
+ assert.equal(requests[0].method, 'GET');
+ requests[0].resolve(threeTestGroups(secondOverrides, thirdOverrides));
+
+ const result = await promise;
+ assert.equal(result, BuildRequest.findById(16993));
+ assert.ok(BuildRequest.findById(16993).createdAt() < BuildRequest.findById(16989).createdAt());
+ });
+ });
+
describe('waitingTime', function () {
it('should return "0 minutes" when the reference time is identically equal to createdAt', function () {
const data = ""