From 69c7241c0d147e21c7219696af57b93b5460c1f7 Mon Sep 17 00:00:00 2001 From: Alexander Thomas Date: Thu, 20 Nov 2025 09:29:39 +0100 Subject: [PATCH 1/2] Upgrade builder scripts to Dart 3.10.1 --- builder/bin/find_base_commit.dart | 70 ++-- builder/bin/upload_results_to_database.dart | 57 ++- builder/lib/src/builder.dart | 112 ++++-- builder/lib/src/commits_cache.dart | 30 +- builder/lib/src/firestore.dart | 412 ++++++++++++-------- builder/lib/src/firestore_helpers.dart | 4 +- builder/lib/src/gerrit_change.dart | 33 +- builder/lib/src/result.dart | 38 +- builder/lib/src/reverted_changes.dart | 22 +- builder/lib/src/status.dart | 4 +- builder/lib/src/tryjob.dart | 84 ++-- builder/pubspec.lock | 222 ++++++----- builder/pubspec.yaml | 10 +- builder/test/approvals_test.dart | 231 +++++++---- builder/test/builder_test.dart | 119 ++++-- builder/test/commits_cache_test.dart | 11 +- builder/test/fakes.dart | 106 +++-- builder/test/firestore_test.dart | 69 ++-- builder/test/gerrit_test.dart | 6 +- builder/test/results_test.dart | 77 ++-- builder/test/revert_test.dart | 77 ++-- builder/test/test_data.dart | 116 +++--- builder/test/tryjob_test.dart | 177 +++++---- 23 files changed, 1288 insertions(+), 799 deletions(-) diff --git a/builder/bin/find_base_commit.dart b/builder/bin/find_base_commit.dart index 82f55a7..36fcd24 100755 --- a/builder/bin/find_base_commit.dart +++ b/builder/bin/find_base_commit.dart @@ -15,16 +15,24 @@ import 'package:glob/glob.dart'; void main(List args) async { final parser = ArgParser(); - parser.addMultiOption('builder', - abbr: 'b', - help: 'Select the builders matching the glob [option is repeatable]', - splitCommas: false); - parser.addOption('branch', - abbr: 'B', - help: 'Select the builders building this branch', - defaultsTo: 'main'); - parser.addOption('count', - abbr: 'c', help: 'List this many commits', defaultsTo: '1'); + parser.addMultiOption( + 'builder', + abbr: 'b', + help: 'Select the builders matching the glob [option is repeatable]', + splitCommas: false, + ); + parser.addOption( + 'branch', + abbr: 'B', + help: 'Select the builders building this branch', + defaultsTo: 'main', + ); + parser.addOption( + 'count', + abbr: 'c', + help: 'List this many commits', + defaultsTo: '1', + ); parser.addFlag('help', help: 'Show the program usage.', negatable: false); final options = parser.parse(args); @@ -41,21 +49,24 @@ ${parser.usage}'''); int count = int.parse(options['count']); final globs = List.from( - options['builder'].map((String pattern) => Glob(pattern))); + options['builder'].map((String pattern) => Glob(pattern)), + ); // Download the most recent builds from buildbucket. const maxBuilds = 1000; - final url = Uri.parse('https://cr-buildbucket.appspot.com' - '/prpc/buildbucket.v2.Builds/SearchBuilds'); + final url = Uri.parse( + 'https://cr-buildbucket.appspot.com' + '/prpc/buildbucket.v2.Builds/SearchBuilds', + ); const maxRetries = 3; const timeout = Duration(seconds: 30); final query = jsonEncode({ 'predicate': { 'builder': {'project': 'dart', 'bucket': 'ci.sandbox'}, - 'status': 'ENDED_MASK' + 'status': 'ENDED_MASK', }, 'pageSize': maxBuilds, - 'fields': 'builds.*.builder.builder,builds.*.input' + 'fields': 'builds.*.builder.builder,builds.*.input', }); late Map searchResult; for (int i = 1; i <= maxRetries; i++) { @@ -67,16 +78,21 @@ ${parser.usage}'''); ..write(query); final response = await request.close().timeout(timeout); if (response.statusCode != 200) { - print('Failed to search for builds: ' - '${response.statusCode}:${response.reasonPhrase}'); + print( + 'Failed to search for builds: ' + '${response.statusCode}:${response.reasonPhrase}', + ); exit(1); } const prefix = ")]}'"; searchResult = await (response .cast>() .transform(Utf8Decoder()) - .map((event) => - event.startsWith(prefix) ? event.substring(prefix.length) : event) + .map( + (event) => event.startsWith(prefix) + ? event.substring(prefix.length) + : event, + ) .transform(JsonDecoder()) .cast>() .first @@ -86,7 +102,8 @@ ${parser.usage}'''); } on TimeoutException catch (e) { final inSeconds = e.duration?.inSeconds; stderr.writeln( - 'Attempt $i of $maxRetries timed out after $inSeconds seconds'); + 'Attempt $i of $maxRetries timed out after $inSeconds seconds', + ); if (i == maxRetries) { stderr.writeln('error: Failed to download $url'); exit(1); @@ -131,8 +148,10 @@ ${parser.usage}'''); continue; } final commit = input['id'] as String; - final buildersForCommit = - buildersForCommits.putIfAbsent(commit, () => {}); + final buildersForCommit = buildersForCommits.putIfAbsent( + commit, + () => {}, + ); buildersForCommit.add(builder); } @@ -148,9 +167,10 @@ ${parser.usage}'''); } // List commits run on the most builders. - for (final commit in buildersForCommits.keys - .where((commit) => buildersForCommits[commit]!.length == maxBots) - .take(count)) { + for (final commit + in buildersForCommits.keys + .where((commit) => buildersForCommits[commit]!.length == maxBots) + .take(count)) { print(commit); } } diff --git a/builder/bin/upload_results_to_database.dart b/builder/bin/upload_results_to_database.dart index 62565c5..80f0c32 100644 --- a/builder/bin/upload_results_to_database.dart +++ b/builder/bin/upload_results_to_database.dart @@ -15,18 +15,20 @@ import 'package:http/http.dart' as http; late BuildInfo buildInfo; Future>> readChangedResults(File resultsFile) async { - final lines = (await resultsFile.readAsLines()) - .map((line) => jsonDecode(line)! as Map); + final lines = (await resultsFile.readAsLines()).map( + (line) => jsonDecode(line)! as Map, + ); if (lines.isEmpty) { print('Empty input results.json file'); exit(1); } - buildInfo = BuildInfo.fromResult( - lines.first, {for (final line in lines) line[fConfiguration]}); + buildInfo = BuildInfo.fromResult(lines.first, { + for (final line in lines) line[fConfiguration], + }); return lines.where(isChangedResult).toList(); } -File fileOption(options, String name) { +File fileOption(ArgResults options, String name) { final path = options[name]; if (path == null) { print("Required option '$name'!"); @@ -40,37 +42,50 @@ File fileOption(options, String name) { return file; } -Future processResults(options, client, firestore) async { +Future processResults( + ArgResults options, + http.Client client, + FirestoreService firestore, +) async { final inputFile = fileOption(options, 'results'); final results = await readChangedResults(inputFile); final String? buildbucketID = options['buildbucket_id']; final String? baseRevision = options['base_revision']; final commitCache = CommitsCache(firestore, client); if (buildInfo is TryBuildInfo) { - return Tryjob(buildInfo as TryBuildInfo, buildbucketID!, baseRevision!, - commitCache, firestore, client) - .process(results); + return Tryjob( + buildInfo as TryBuildInfo, + buildbucketID!, + baseRevision!, + commitCache, + firestore, + client, + ).process(results); } else { return Build(buildInfo, commitCache, firestore).process(results); } } void main(List arguments) async { - final options = (ArgParser() - ..addOption('project', - abbr: 'p', - defaultsTo: 'dart-ci-staging', - allowed: ['dart-ci-staging', 'dart-ci']) - ..addOption('results', abbr: 'r') - ..addOption('buildbucket_id', abbr: 'i') - ..addOption('base_revision', abbr: 'b') - ..addOption('status_file')) - .parse(arguments); + final options = + (ArgParser() + ..addOption( + 'project', + abbr: 'p', + defaultsTo: 'dart-ci-staging', + allowed: ['dart-ci-staging', 'dart-ci'], + ) + ..addOption('results', abbr: 'r') + ..addOption('buildbucket_id', abbr: 'i') + ..addOption('base_revision', abbr: 'b') + ..addOption('status_file')) + .parse(arguments); final baseClient = http.Client(); final client = await clientViaApplicationDefaultCredentials( - scopes: ['https://www.googleapis.com/auth/cloud-platform'], - baseClient: baseClient); + scopes: ['https://www.googleapis.com/auth/cloud-platform'], + baseClient: baseClient, + ); final api = FirestoreApi(client); final firestore = FirestoreService(api, client, project: options['project']); diff --git a/builder/lib/src/builder.dart b/builder/lib/src/builder.dart index 0d110f6..16b1145 100644 --- a/builder/lib/src/builder.dart +++ b/builder/lib/src/builder.dart @@ -46,7 +46,10 @@ class Build { await storeBuildCommitsInfo(); log('update build info'); if (!await firestore.updateBuildInfo( - info.builderName, info.buildNumber, endIndex)) { + info.builderName, + info.buildNumber, + endIndex, + )) { // This build's results have already been recorded. log('build already uploaded to database, exiting'); // TODO(karlklose): add a flag to overwrite builder results. @@ -61,7 +64,9 @@ class Build { try { status.unapprovedFailures = { for (final configuration in info.configurations) - configuration: await unapprovedFailuresForConfiguration(configuration) + configuration: await unapprovedFailuresForConfiguration( + configuration, + ), }..removeWhere((key, value) => value.isEmpty); } catch (e) { log('Failed to fetch unapproved failures: $e'); @@ -79,8 +84,8 @@ class Build { if (countApprovalsCopied > 0) ...[ '$countApprovalsCopied approvals copied', ...approvalMessages, - if (countApprovalsCopied > 10) '...' - ] + if (countApprovalsCopied > 10) '...', + ], ]; log(report.join('\n')); return status; @@ -102,13 +107,16 @@ class Build { if (info.previousCommitHash == null) { startIndex = endIndex; } else { - final startCommit = - await commitsCache.getCommit(info.previousCommitHash!); + final startCommit = await commitsCache.getCommit( + info.previousCommitHash!, + ); startIndex = startCommit.index + 1; if (startIndex > endIndex) { - throw ArgumentError('Results received with empty blamelist\n' - 'previous commit: ${info.previousCommitHash}\n' - 'built commit: ${info.commitRef}'); + throw ArgumentError( + 'Results received with empty blamelist\n' + 'previous commit: ${info.previousCommitHash}\n' + 'built commit: ${info.commitRef}', + ); } } } @@ -119,7 +127,7 @@ class Build { commits = [ for (var index = startIndex; index < endIndex; ++index) await commitsCache.getCommitByIndex(index), - endCommit + endCommit, ]; for (final commit in commits) { final index = commit.index; @@ -128,12 +136,13 @@ class Build { if (review != null) { tryApprovals.addAll({ for (final result in await firestore.tryApprovals(review)) - testResult(result.fields): index + testResult(result.fields): index, }); } if (reverted != null) { - allRevertedChanges - .add(await getRevertedChanges(reverted, index, firestore)); + allRevertedChanges.add( + await getRevertedChanges(reverted, index, firestore), + ); } } } @@ -155,30 +164,43 @@ class Build { bool approved; var result = await firestore.findResult(change, startIndex, endIndex); var activeResults = await firestore.findActiveResults( - change['name'], change['configuration']); + change['name'], + change['configuration'], + ); if (result == null) { - final approvingIndex = tryApprovals[testResult(change)] ?? + final approvingIndex = + tryApprovals[testResult(change)] ?? allRevertedChanges .firstWhereOrNull( - (revertedChange) => revertedChange.approveRevert(change)) + (revertedChange) => revertedChange.approveRevert(change), + ) ?.revertIndex; approved = approvingIndex != null; - final newResult = constructResult(change, startIndex, endIndex, - approved: approved, - landedReviewIndex: approvingIndex, - failure: failure); + final newResult = constructResult( + change, + startIndex, + endIndex, + approved: approved, + landedReviewIndex: approvingIndex, + failure: failure, + ); await firestore.storeResult(newResult); if (approved) { countApprovalsCopied++; if (countApprovalsCopied <= 10) { - approvalMessages - .add('Copied approval of result ${testResult(change)}'); + approvalMessages.add( + 'Copied approval of result ${testResult(change)}', + ); } } } else { approved = await firestore.updateResult( - result, change['configuration'], startIndex, endIndex, - failure: failure); + result, + change['configuration'], + startIndex, + endIndex, + failure: failure, + ); } if (failure && !approved) success = false; @@ -189,38 +211,52 @@ class Build { .getList(fActiveConfigurations) ?.contains(change['configuration']) ?? false)) { - log('Unexpected active result when processing new change:\n' - 'Active result: ${untagMap(activeResult.fields)}\n\n' - 'Change: $change\n\n' - 'approved: $approved'); + log( + 'Unexpected active result when processing new change:\n' + 'Active result: ${untagMap(activeResult.fields)}\n\n' + 'Change: $change\n\n' + 'approved: $approved', + ); } // Removes the configuration from the list of active configurations. await firestore.removeActiveConfiguration( - activeResult, change['configuration']); + activeResult, + change['configuration'], + ); } } Future> unapprovedFailuresForConfiguration( - String configuration) async { + String configuration, + ) async { final failures = await firestore.findUnapprovedFailures( - configuration, BuildStatus.unapprovedFailuresLimit + 1); + configuration, + BuildStatus.unapprovedFailuresLimit + 1, + ); await Future.forEach(failures, addBlamelistCommits); return failures; } Future addBlamelistCommits(SafeDocument result) async { - final startCommit = await commitsCache - .getCommitByIndex(result.getInt(fBlamelistStartIndex)!); + final startCommit = await commitsCache.getCommitByIndex( + result.getInt(fBlamelistStartIndex)!, + ); result.fields[fBlamelistStartCommit] = taggedValue(startCommit.hash); - final endCommit = - await commitsCache.getCommitByIndex(result.getInt(fBlamelistEndIndex)!); + final endCommit = await commitsCache.getCommitByIndex( + result.getInt(fBlamelistEndIndex)!, + ); result.fields[fBlamelistEndCommit] = taggedValue(endCommit.hash); } } Map constructResult( - Map change, int startIndex, int endIndex, - {required bool approved, int? landedReviewIndex, required bool failure}) { + Map change, + int startIndex, + int endIndex, { + required bool approved, + int? landedReviewIndex, + required bool failure, +}) { return { fName: change[fName], fResult: change[fResult], @@ -232,6 +268,6 @@ Map constructResult( fConfigurations: [change['configuration']], fApproved: approved, if (failure) fActive: true, - if (failure) fActiveConfigurations: [change['configuration']] + if (failure) fActiveConfigurations: [change['configuration']], }; } diff --git a/builder/lib/src/commits_cache.dart b/builder/lib/src/commits_cache.dart index 1a5b742..9726f89 100644 --- a/builder/lib/src/commits_cache.dart +++ b/builder/lib/src/commits_cache.dart @@ -77,11 +77,14 @@ class CommitsCache { final response = await httpClient.get(url); final protectedJson = response.body; if (!protectedJson.startsWith(prefix)) { - throw Exception('Gerrit response missing prefix $prefix: $protectedJson.' - 'Requested URL: $url'); + throw Exception( + 'Gerrit response missing prefix $prefix: $protectedJson.' + 'Requested URL: $url', + ); } final commits = List.castFrom>( - jsonDecode(protectedJson.substring(prefix.length))['log']); + jsonDecode(protectedJson.substring(prefix.length))['log'], + ); if (commits.isEmpty) { print('Found no new commits between $lastHash and $branch'); } @@ -135,7 +138,7 @@ class CommitsCache { } class TestingCommitsCache extends CommitsCache { - TestingCommitsCache(firestore, httpClient) : super(firestore, httpClient); + TestingCommitsCache(super.firestore, super.httpClient); @override Future _getNewCommits() { @@ -159,7 +162,7 @@ const months = { 'Sep': '09', 'Oct': '10', 'Nov': '11', - 'Dec': '12' + 'Dec': '12', }; DateTime parseGitilesDateTime(String gitiles) { @@ -171,8 +174,9 @@ DateTime parseGitilesDateTime(String gitiles) { } final reviewRegExp = RegExp( - '^Reviewed-on: https://dart-review.googlesource.com/c/sdk/\\+/(\\d+)\$', - multiLine: true); + '^Reviewed-on: https://dart-review.googlesource.com/c/sdk/\\+/(\\d+)\$', + multiLine: true, +); int? _review(Map commit) { final match = reviewRegExp.firstMatch(commit['message']); @@ -180,14 +184,18 @@ int? _review(Map commit) { return null; } -final revertRegExp = - RegExp('^This reverts commit ([\\da-f]+)\\.\$', multiLine: true); +final revertRegExp = RegExp( + '^This reverts commit ([\\da-f]+)\\.\$', + multiLine: true, +); String? _revert(Map commit) => revertRegExp.firstMatch(commit['message'])?.group(1); -final relandRegExp = - RegExp('^This is a reland of ([\\da-f]+)\\.?\$', multiLine: true); +final relandRegExp = RegExp( + '^This is a reland of ([\\da-f]+)\\.?\$', + multiLine: true, +); String? _reland(Map commit) => relandRegExp.firstMatch(commit['message'])?.group(1); diff --git a/builder/lib/src/firestore.dart b/builder/lib/src/firestore.dart index 9deca92..5139a18 100644 --- a/builder/lib/src/firestore.dart +++ b/builder/lib/src/firestore.dart @@ -20,7 +20,7 @@ class Commit { final String hash /*!*/; Commit(this.hash, this.document); Commit.fromJson(this.hash, Map data) - : document = SafeDocument(Document(fields: taggedMap(data), name: '')); + : document = SafeDocument(Document(fields: taggedMap(data), name: '')); int get index => document.getInt('index')!; String? get revertOf => document.getStringOrNull(fRevertOf); bool get isRevert => document.fields.containsKey(fRevertOf); @@ -38,9 +38,11 @@ class FirestoreService { final Stopwatch _stopwatch; - FirestoreService(this.firestore, this.client, - {this.project = 'dart-ci-staging'}) - : _stopwatch = Stopwatch()..start(); + FirestoreService( + this.firestore, + this.client, { + this.project = 'dart-ci-staging', + }) : _stopwatch = Stopwatch()..start(); void log(String string) { final time = _stopwatch.elapsed.toString(); @@ -50,12 +52,13 @@ class FirestoreService { } } - Future> query( - {required String from, - Filter? where, - Order? orderBy, - int? limit, - String? parent}) async { + Future> query({ + required String from, + Filter? where, + Order? orderBy, + int? limit, + String? parent, + }) async { final query = StructuredQuery() ..from = inCollection(from) ..where = where @@ -75,9 +78,7 @@ class FirestoreService { } } - Future getDocumentOrNull( - String path, - ) async { + Future getDocumentOrNull(String path) async { try { final document = await firestore.projects.databases.documents.get(path); documentsFetched++; @@ -99,17 +100,21 @@ class FirestoreService { String get database => 'projects/$project/databases/(default)'; String get documents => '$database/documents'; - Future> runQuery(StructuredQuery query, - {String? parent}) async { + Future> runQuery( + StructuredQuery query, { + String? parent, + }) async { final request = RunQueryRequest()..structuredQuery = query; final parentPath = parent == null ? documents : '$documents/$parent'; - final queryResponse = await firestore.projects.databases.documents - .runQuery(request, parentPath); + final queryResponse = await firestore.projects.databases.documents.runQuery( + request, + parentPath, + ); if (queryResponse.first.document == null) return []; documentsFetched += queryResponse.length; return [ for (final responseElement in queryResponse) - SafeDocument(responseElement.document!) + SafeDocument(responseElement.document!), ]; } @@ -136,22 +141,30 @@ class FirestoreService { } Future getCommitByIndex(int index) async { - final response = - await query(from: 'commits', where: fieldEquals('index', index)); + final response = await query( + from: 'commits', + where: fieldEquals('index', index), + ); return _commit(response.first); } Future getLastCommit() async { - final lastCommit = - await query(from: 'commits', orderBy: orderBy('index', false)); + final lastCommit = await query( + from: 'commits', + orderBy: orderBy('index', false), + ); return _commit(lastCommit.first); } Future addCommit(String id, Map data) async { try { final document = Document()..fields = taggedMap(data); - await firestore.projects.databases.documents - .createDocument(document, documents, 'commits', documentId: id); + await firestore.projects.databases.documents.createDocument( + document, + documents, + 'commits', + documentId: id, + ); documentsWritten++; log("Added commit $id -> ${data['index']}"); } on DetailedApiRequestError catch (e) { @@ -165,21 +178,27 @@ class FirestoreService { Future updateConfiguration(String configuration, String builder) async { documentsWritten++; - final record = - await getDocumentOrNull('$documents/configurations/$configuration'); + final record = await getDocumentOrNull( + '$documents/configurations/$configuration', + ); if (record == null) { final newRecord = Document()..fields = taggedMap({'builder': builder}); await firestore.projects.databases.documents.createDocument( - newRecord, documents, 'configurations', - documentId: configuration); + newRecord, + documents, + 'configurations', + documentId: configuration, + ); log('Configuration document $configuration -> $builder created'); } else { final originalBuilder = SafeDocument(record).getString('builder'); if (originalBuilder != builder) { record.fields!['builder']!.stringValue = builder; await updateFields(record, ['builder']); - log('Configuration document changed: $configuration -> $builder ' - '(was $originalBuilder)'); + log( + 'Configuration document changed: $configuration -> $builder ' + '(was $originalBuilder)', + ); } } } @@ -189,15 +208,24 @@ class FirestoreService { /// Returns `true` if and only if there is no completed record /// for this build. Future updateBuildInfo( - String builder, int buildNumber, int index) async { + String builder, + int buildNumber, + int index, + ) async { final record = await getDocumentOrNull('$documents/builds/$builder:$index'); if (record == null) { final newRecord = Document() - ..fields = taggedMap( - {'builder': builder, 'build_number': buildNumber, 'index': index}); + ..fields = taggedMap({ + 'builder': builder, + 'build_number': buildNumber, + 'index': index, + }); await firestore.projects.databases.documents.createDocument( - newRecord, documents, 'builds', - documentId: '$builder:$index'); + newRecord, + documents, + 'builds', + documentId: '$builder:$index', + ); documentsWritten++; return true; } else { @@ -215,8 +243,12 @@ class FirestoreService { /// /// Returns `true` if and only if there is no completed record /// for this build. - Future recordTryBuild(TryBuildInfo info, String buildbucketID, - bool success, bool truncated) async { + Future recordTryBuild( + TryBuildInfo info, + String buildbucketID, + bool success, + bool truncated, + ) async { final newRecord = Document() ..fields = taggedMap({ 'builder': info.builderName, @@ -228,29 +260,38 @@ class FirestoreService { 'completed': true, if (truncated) 'truncated': true, }); - log('creating try-build record for ' - '${info.builderName} ${info.buildNumber} ${info.review} ${info.patchset}'); - await firestore.projects.databases.documents - .createDocument(newRecord, documents, 'try_builds'); + log( + 'creating try-build record for ' + '${info.builderName} ${info.buildNumber} ${info.review} ${info.patchset}', + ); + await firestore.projects.databases.documents.createDocument( + newRecord, + documents, + 'try_builds', + ); documentsWritten++; } Future findResult( - Map change, int startIndex, int endIndex) async { + Map change, + int startIndex, + int endIndex, + ) async { final name = change['name'] as String; final result = change['result'] as String; final previousResult = change['previous_result'] as String; final expected = change['expected'] as String; final snapshot = await query( - from: 'results', - orderBy: orderBy('blamelist_end_index', false), - where: compositeFilter([ - fieldEquals('name', name), - fieldEquals('result', result), - fieldEquals('previous_result', previousResult), - fieldEquals('expected', expected) - ]), - limit: 5); + from: 'results', + orderBy: orderBy('blamelist_end_index', false), + where: compositeFilter([ + fieldEquals('name', name), + fieldEquals('result', result), + fieldEquals('previous_result', previousResult), + fieldEquals('expected', expected), + ]), + limit: 5, + ); bool blamelistIncludesChange(SafeDocument document) { final before = endIndex < document.getInt('blamelist_start_index')!; @@ -283,8 +324,12 @@ class FirestoreService { } Future updateResult( - String result, String? configuration, int startIndex, int endIndex, - {required bool failure}) async { + String result, + String? configuration, + int startIndex, + int endIndex, { + required bool failure, + }) async { late bool approved; await retryCommit(() async { final document = await getDocument(result); @@ -299,7 +344,7 @@ class FirestoreService { final updates = [ 'blamelist_start_index', 'blamelist_end_index', - if (failure) 'active' + if (failure) 'active', ]; document.fields!['blamelist_start_index'] = taggedValue(newStart); document.fields!['blamelist_end_index'] = taggedValue(newEnd); @@ -319,7 +364,7 @@ class FirestoreService { if (failure) FieldTransform() ..fieldPath = 'active_configurations' - ..appendMissingElements = addConfiguration + ..appendMissingElements = addConfiguration, ]; documentsWritten++; return write; @@ -350,11 +395,15 @@ class FirestoreService { /// Returns all results which are either pinned to or have a range that is /// this single index. // TODO: rename this function Future>> findRevertedChanges(int index) async { - final pinnedResults = - await query(from: 'results', where: fieldEquals('pinned_index', index)); + final pinnedResults = await query( + from: 'results', + where: fieldEquals('pinned_index', index), + ); final results = pinnedResults.map((response) => response.fields).toList(); final unpinnedResults = await query( - from: 'results', where: fieldEquals('blamelist_end_index', index)); + from: 'results', + where: fieldEquals('blamelist_end_index', index), + ); for (final data in unpinnedResults) { if (data.getInt('blamelist_start_index') == index && data.isNull('pinned_index')) { @@ -365,7 +414,10 @@ class FirestoreService { } Future storeTryChange( - Map change, int review, int patchset) async { + Map change, + int review, + int patchset, + ) async { final name = change['name'] as String; final result = change['result'] as String; final expected = change['expected'] as String; @@ -374,16 +426,17 @@ class FirestoreService { // Find an existing result record for this test on this patchset. final responses = await query( - from: 'try_results', - where: compositeFilter([ - fieldEquals('review', review), - fieldEquals('patchset', patchset), - fieldEquals('name', name), - fieldEquals('result', result), - fieldEquals('previous_result', previousResult), - fieldEquals('expected', expected) - ]), - limit: 1); + from: 'try_results', + where: compositeFilter([ + fieldEquals('review', review), + fieldEquals('patchset', patchset), + fieldEquals('name', name), + fieldEquals('result', result), + fieldEquals('previous_result', previousResult), + fieldEquals('expected', expected), + ]), + limit: 1, + ); // TODO(karlklose): We could run only this query, and then see if the // patchset is equal or not. We don't need the separate // query with equal patchset. @@ -394,16 +447,17 @@ class FirestoreService { if (responses.isEmpty) { // Is the previous result for this test on this review approved? final previous = await query( - from: 'try_results', - where: compositeFilter([ - fieldEquals('review', review), - fieldEquals('name', name), - fieldEquals('result', result), - fieldEquals('previous_result', previousResult), - fieldEquals('expected', expected), - ]), - orderBy: orderBy('patchset', false), - limit: 1); + from: 'try_results', + where: compositeFilter([ + fieldEquals('review', review), + fieldEquals('name', name), + fieldEquals('result', result), + fieldEquals('previous_result', previousResult), + fieldEquals('expected', expected), + ]), + orderBy: orderBy('patchset', false), + limit: 1, + ); final approved = previous.isNotEmpty && previous.first.getBool('approved') == true; @@ -416,19 +470,23 @@ class FirestoreService { 'review': review, 'patchset': patchset, 'configurations': [configuration], - 'approved': approved + 'approved': approved, }); - await firestore.projects.databases.documents - .createDocument(document, documents, 'try_results', mask_fieldPaths: [ - 'name', - 'result', - 'previous_result', - 'expected', - 'review', - 'patchset', - 'configurations', - 'approved' - ]); + await firestore.projects.databases.documents.createDocument( + document, + documents, + 'try_results', + mask_fieldPaths: [ + 'name', + 'result', + 'previous_result', + 'expected', + 'review', + 'patchset', + 'configurations', + 'approved', + ], + ); documentsWritten++; return approved; } else { @@ -441,7 +499,7 @@ class FirestoreService { await _executeWrite([ Write() ..update = document.toDocument() - ..updateTransforms = [addConfiguration] + ..updateTransforms = [addConfiguration], ]); return document.getBool('approved') == true; } @@ -452,18 +510,23 @@ class FirestoreService { await _executeWrite([ Write() ..update = document - ..updateMask = (DocumentMask()..fieldPaths = ['approved']) + ..updateMask = (DocumentMask()..fieldPaths = ['approved']), ]); } /// Removes [configuration] from the active configurations and marks the /// active result inactive when we remove the last active config. Future removeActiveConfiguration( - SafeDocument activeResult, String? configuration) async { + SafeDocument activeResult, + String? configuration, + ) async { final configurations = activeResult.getList('active_configurations')!; assert(configurations.contains(configuration)); await removeArrayEntry( - activeResult, 'active_configurations', taggedValue(configuration)); + activeResult, + 'active_configurations', + taggedValue(configuration), + ); final document = await getDocument(activeResult.name); activeResult = SafeDocument(document); if (activeResult.getList('active_configurations')?.isEmpty == true) { @@ -471,14 +534,17 @@ class FirestoreService { activeResult.fields.remove('active'); final write = Write() ..update = activeResult.toDocument() - ..updateMask = - (DocumentMask()..fieldPaths = ['active', 'active_configurations']); + ..updateMask = (DocumentMask() + ..fieldPaths = ['active', 'active_configurations']); await _executeWrite([write]); } } Future removeArrayEntry( - SafeDocument document, String fieldName, Value entry) async { + SafeDocument document, + String fieldName, + Value entry, + ) async { await _executeWrite([ Write() ..transform = (DocumentTransform() @@ -486,38 +552,46 @@ class FirestoreService { ..fieldTransforms = [ FieldTransform() ..fieldPath = fieldName - ..removeAllFromArray = (ArrayValue()..values = [entry]) - ]) + ..removeAllFromArray = (ArrayValue()..values = [entry]), + ]), ]); } Future> findActiveResults( - String name, String configuration) async { + String name, + String configuration, + ) async { final results = await query( - from: 'results', - where: compositeFilter([ - arrayContains('active_configurations', configuration), - fieldEquals('active', true), - fieldEquals('name', name) - ])); + from: 'results', + where: compositeFilter([ + arrayContains('active_configurations', configuration), + fieldEquals('active', true), + fieldEquals('name', name), + ]), + ); if (results.length > 1) { - log([ - 'Multiple active results for the same configuration and test', - ...results - ].join('\n')); + log( + [ + 'Multiple active results for the same configuration and test', + ...results, + ].join('\n'), + ); } return results; } Future> findUnapprovedFailures( - String configuration, int limit) async { + String configuration, + int limit, + ) async { final results = await query( - from: 'results', - where: compositeFilter([ - arrayContains('active_configurations', configuration), - fieldEquals('approved', false) - ]), - limit: limit); + from: 'results', + where: compositeFilter([ + arrayContains('active_configurations', configuration), + fieldEquals('approved', false), + ]), + limit: limit, + ); return results; } @@ -528,8 +602,12 @@ class FirestoreService { Future storeReview(String review, Map data) async { final document = Document()..fields = data; documentsWritten++; - await firestore.projects.databases.documents - .createDocument(document, documents, 'reviews', documentId: review); + await firestore.projects.databases.documents.createDocument( + document, + documents, + 'reviews', + documentId: review, + ); } Future deleteDocument(String name) async { @@ -550,23 +628,31 @@ class FirestoreService { await _executeWrite([ Write() ..update = document - ..updateMask = (DocumentMask()..fieldPaths = fields) + ..updateMask = (DocumentMask()..fieldPaths = fields), ]); } - Future storePatchset(String review, int patchset, String kind, - String? description, int patchsetGroup, int number) async { + Future storePatchset( + String review, + int patchset, + String kind, + String? description, + int patchsetGroup, + int number, + ) async { final document = Document() ..name = '$documents/reviews/$review/patchsets/$patchset' ..fields = taggedMap({ 'kind': kind, 'description': description, 'patchset_group': patchsetGroup, - 'number': number + 'number': number, }); await _executeWrite([Write()..update = document]); - log('Stored patchset: $documents/reviews/$review/patchsets/$patchset\n' - '${untagMap(document.fields!)}'); + log( + 'Stored patchset: $documents/reviews/$review/patchsets/$patchset\n' + '${untagMap(document.fields!)}', + ); documentsWritten++; } @@ -588,65 +674,78 @@ class FirestoreService { } Future linkCommentsToCommit(int review, int index) async { - final comments = - await query(from: 'comments', where: fieldEquals('review', review)); + final comments = await query( + from: 'comments', + where: fieldEquals('review', review), + ); if (comments.isEmpty) return; final writes = []; for (final document in comments) { document.fields['blamelist_start_index'] = taggedValue(index); document.fields['blamelist_end_index'] = taggedValue(index); - writes.add(Write() - ..update = document.toDocument() - ..updateMask = (DocumentMask() - ..fieldPaths = ['blamelist_start_index', 'blamelist_end_index'])); + writes.add( + Write() + ..update = document.toDocument() + ..updateMask = (DocumentMask() + ..fieldPaths = ['blamelist_start_index', 'blamelist_end_index']), + ); } await _executeWrite(writes); } Future> tryApprovals(int review) async { final patchsets = await query( - from: 'patchsets', - parent: 'reviews/$review', - orderBy: orderBy('number', false), - limit: 1); + from: 'patchsets', + parent: 'reviews/$review', + orderBy: orderBy('number', false), + limit: 1, + ); if (patchsets.isEmpty) { return []; } final lastPatchsetGroup = patchsets.first.getInt('patchset_group'); return query( - from: 'try_results', - where: compositeFilter([ - fieldEquals('approved', true), - fieldEquals('review', review), - fieldGreaterThanOrEqual('patchset', lastPatchsetGroup) - ])); + from: 'try_results', + where: compositeFilter([ + fieldEquals('approved', true), + fieldEquals('review', review), + fieldGreaterThanOrEqual('patchset', lastPatchsetGroup), + ]), + ); } Future> tryResults( - int review, String configuration) async { + int review, + String configuration, + ) async { final patchsets = await query( - from: 'patchsets', - parent: 'reviews/$review', - orderBy: orderBy('number', false), - limit: 1); + from: 'patchsets', + parent: 'reviews/$review', + orderBy: orderBy('number', false), + limit: 1, + ); if (patchsets.isEmpty) { return []; } final lastPatchsetGroup = patchsets.first.getInt('patchset_group'); return query( - from: 'try_results', - where: compositeFilter([ - fieldEquals('review', review), - arrayContains('configurations', configuration), - fieldGreaterThanOrEqual('patchset', lastPatchsetGroup) - ])); + from: 'try_results', + where: compositeFilter([ + fieldEquals('review', review), + arrayContains('configurations', configuration), + fieldGreaterThanOrEqual('patchset', lastPatchsetGroup), + ]), + ); } // TODO(b/210111991): Remove Builder and TryBuilder records completely. // They were only used to communicate success back to the builder // when the processing was not on the builder. Future completeBuilderRecord( - String builder, int index, bool success) async { + String builder, + int index, + bool success, + ) async { final path = '$documents/builds/$builder:$index'; final document = await getDocument(path); await _completeBuilderRecord(document, success); @@ -657,10 +756,7 @@ class FirestoreService { document.fields!['success'] = taggedValue(success); final write = Write() ..update = document - ..updateMask = (DocumentMask() - ..fieldPaths = [ - 'success', - ]); + ..updateMask = (DocumentMask()..fieldPaths = ['success']); return write; }); } diff --git a/builder/lib/src/firestore_helpers.dart b/builder/lib/src/firestore_helpers.dart index 8c52d0a..1ea01fb 100644 --- a/builder/lib/src/firestore_helpers.dart +++ b/builder/lib/src/firestore_helpers.dart @@ -9,8 +9,8 @@ class SafeDocument { final Map fields; SafeDocument(Document document) - : name = document.name!, - fields = document.fields!; + : name = document.name!, + fields = document.fields!; Document toDocument() => Document(name: name, fields: fields); int? getInt(String name) { diff --git a/builder/lib/src/gerrit_change.dart b/builder/lib/src/gerrit_change.dart index 8025584..66b5172 100644 --- a/builder/lib/src/gerrit_change.dart +++ b/builder/lib/src/gerrit_change.dart @@ -12,11 +12,7 @@ class GerritInfo { static const gerritUrl = 'https://dart-review.googlesource.com/changes'; static const gerritQuery = 'o=ALL_REVISIONS&o=DETAILED_ACCOUNTS&o=CURRENT_COMMIT'; - static const trivialKinds = { - 'TRIVIAL_REBASE', - 'NO_CHANGE', - 'NO_CODE_CHANGE', - }; + static const trivialKinds = {'TRIVIAL_REBASE', 'NO_CHANGE', 'NO_CODE_CHANGE'}; static const prefix = ")]}'\n"; final http.Client httpClient; @@ -25,8 +21,8 @@ class GerritInfo { final String patchset; GerritInfo(int review, int patchset, this.firestore, this.httpClient) - : review = review.toString(), - patchset = patchset.toString(); + : review = review.toString(), + patchset = patchset.toString(); /// Fetches the owner, changeId, message, and date of a Gerrit change and /// stores them in the databases. @@ -39,13 +35,14 @@ class GerritInfo { if (!protectedJson.startsWith(prefix)) { throw Exception('Gerrit response missing prefix $prefix: $protectedJson'); } - final reviewInfo = jsonDecode(protectedJson.substring(prefix.length)) - as Map; + final reviewInfo = + jsonDecode(protectedJson.substring(prefix.length)) + as Map; final reverted = revert(reviewInfo); if (!(await firestore.hasReview(review))) { await firestore.storeReview(review, { 'subject': taggedValue(reviewInfo['subject']), - if (reverted != null) 'revert_of': taggedValue(reverted) + if (reverted != null) 'revert_of': taggedValue(reverted), }); } // Add the patchset information to the patchsets subcollection. @@ -57,16 +54,24 @@ class GerritInfo { if (!trivialKinds.contains(revision['kind'])) { patchsetGroupFirst = number; } - await firestore.storePatchset(review, number, revision['kind'], - revision['description'], patchsetGroupFirst, number); + await firestore.storePatchset( + review, + number, + revision['kind'], + revision['description'], + patchsetGroupFirst, + number, + ); } } static String? revert(Map reviewInfo) { final current = reviewInfo['current_revision']; final commit = reviewInfo['revisions'][current]['commit']; - final regExp = - RegExp('^This reverts commit ([\\da-f]+)\\.\$', multiLine: true); + final regExp = RegExp( + '^This reverts commit ([\\da-f]+)\\.\$', + multiLine: true, + ); return regExp.firstMatch(commit['message'])?.group(1); } } diff --git a/builder/lib/src/result.dart b/builder/lib/src/result.dart index 8b65946..526c61f 100644 --- a/builder/lib/src/result.dart +++ b/builder/lib/src/result.dart @@ -69,11 +69,11 @@ String? fromStringOrValue(dynamic value) { } String testResult(Map change) => [ - fromStringOrValue(change[fName]), - fromStringOrValue(change[fResult]), - fromStringOrValue(change[fPreviousResult]), - fromStringOrValue(change[fExpected]) - ].join(' '); + fromStringOrValue(change[fName]), + fromStringOrValue(change[fResult]), + fromStringOrValue(change[fPreviousResult]), + fromStringOrValue(change[fExpected]), +].join(' '); /// The information about a builder, taken from a Result object, /// that is needed to process the results @@ -87,20 +87,26 @@ class BuildInfo { final Set configurations; BuildInfo(Map result, this.configurations) - : builderName = result[fBuilderName], - buildNumber = int.parse(result[fBuildNumber]), - commitRef = result[fCommitHash], - previousCommitHash = result[fPreviousCommitHash]; + : builderName = result[fBuilderName], + buildNumber = int.parse(result[fBuildNumber]), + commitRef = result[fCommitHash], + previousCommitHash = result[fPreviousCommitHash]; factory BuildInfo.fromResult( - Map result, Set configurations) { + Map result, + Set configurations, + ) { final commitRef = result[fCommitHash]; final match = commitRefRegExp.matchAsPrefix(commitRef); if (match == null) { return BuildInfo(result, configurations); } else { return TryBuildInfo( - result, configurations, int.parse(match[1]!), int.parse(match[2]!)); + result, + configurations, + int.parse(match[1]!), + int.parse(match[2]!), + ); } } } @@ -109,16 +115,16 @@ class TryBuildInfo extends BuildInfo { final int review; final int patchset; - TryBuildInfo(Map result, Set configurations, - this.review, this.patchset) - : super(result, configurations); + TryBuildInfo(super.result, super.configurations, this.review, this.patchset); } class TestNameLock { final locks = >{}; - Future guardedCall(Future Function(Map change) f, - Map change) async { + Future guardedCall( + Future Function(Map change) f, + Map change, + ) async { final name = change[fName]!; while (locks.containsKey(name)) { await locks[name]; diff --git a/builder/lib/src/reverted_changes.dart b/builder/lib/src/reverted_changes.dart index eb80ec2..5085ebd 100644 --- a/builder/lib/src/reverted_changes.dart +++ b/builder/lib/src/reverted_changes.dart @@ -9,15 +9,22 @@ import 'firestore.dart'; import 'result.dart'; Future getRevertedChanges( - String reverted, int revertIndex, FirestoreService firestore) async { + String reverted, + int revertIndex, + FirestoreService firestore, +) async { final revertedCommit = await firestore.getCommit(reverted); if (revertedCommit == null) { throw 'Cannot find commit for reverted commit hash $reverted'; } final index = revertedCommit.index; final changes = await firestore.findRevertedChanges(index); - return RevertedChanges(index, revertIndex, changes, - groupBy(changes, (change) => getValue(change[fName]!))); + return RevertedChanges( + index, + revertIndex, + changes, + groupBy(changes, (change) => getValue(change[fName]!)), + ); } class RevertedChanges { @@ -27,13 +34,18 @@ class RevertedChanges { final Map>> changesForTest; RevertedChanges( - this.index, this.revertIndex, this.changes, this.changesForTest); + this.index, + this.revertIndex, + this.changes, + this.changesForTest, + ); bool approveRevert(Map revert) { final reverted = changesForTest[revert[fName]]; return isFailure(revert) && reverted != null && reverted.any( - (change) => revert[fResult] == getValue(change[fPreviousResult]!)); + (change) => revert[fResult] == getValue(change[fPreviousResult]!), + ); } } diff --git a/builder/lib/src/status.dart b/builder/lib/src/status.dart index dad3d53..5acc843 100644 --- a/builder/lib/src/status.dart +++ b/builder/lib/src/status.dart @@ -32,9 +32,9 @@ class BuildStatus { ' ${entry.key}:', for (final result in entry.value.take(unapprovedFailuresLimit)) resultLine(result), - if (entry.value.length > unapprovedFailuresLimit) ' ...' + if (entry.value.length > unapprovedFailuresLimit) ' ...', ], - '' + '', ].join('\n'); } } diff --git a/builder/lib/src/tryjob.dart b/builder/lib/src/tryjob.dart index 3a468b5..682f182 100644 --- a/builder/lib/src/tryjob.dart +++ b/builder/lib/src/tryjob.dart @@ -43,18 +43,18 @@ class ChangeCounter { } List report() => [ - if (changes > 0) 'Stored $changes changes', - if (hasTruncatedChanges) 'Did not store all results. Truncating.', - if (hasTooManyPassingChanges) - 'Only $maxReportedSuccesses new passes stored', - if (hasTooManyFailingChanges) - 'Only $maxReportedFailures new failures stored', - if (unapprovedFailures > 0) - '$unapprovedFailures unapproved failing tests found', - if (failures > 0) '$failures failing tests found', - if (passes > 0) '$passes passing tests found', - if (newFlakes > 0) '$newFlakes new flaky tests found', - ]; + if (changes > 0) 'Stored $changes changes', + if (hasTruncatedChanges) 'Did not store all results. Truncating.', + if (hasTooManyPassingChanges) + 'Only $maxReportedSuccesses new passes stored', + if (hasTooManyFailingChanges) + 'Only $maxReportedFailures new failures stored', + if (unapprovedFailures > 0) + '$unapprovedFailures unapproved failing tests found', + if (failures > 0) '$failures failing tests found', + if (passes > 0) '$passes passing tests found', + if (newFlakes > 0) '$newFlakes new flaky tests found', + ]; } class Tryjob { @@ -70,14 +70,24 @@ class Tryjob { Map lastLandedResultByName = {}; final String buildbucketID; - Tryjob(this.info, this.buildbucketID, this.baseRevision, this.commits, - this.firestore, this.httpClient); + Tryjob( + this.info, + this.buildbucketID, + this.baseRevision, + this.commits, + this.firestore, + this.httpClient, + ); void log(String string) => firestore.log(string); Future update() async { - await GerritInfo(info.review, info.patchset, firestore, httpClient) - .update(); + await GerritInfo( + info.review, + info.patchset, + firestore, + httpClient, + ).update(); } bool isNotLandedResult(Map change) { @@ -89,18 +99,21 @@ class Tryjob { await update(); log('storing ${results.length} change(s)'); final resultsByConfiguration = groupBy, String>( - results, (result) => result['configuration']); + results, + (result) => result['configuration'], + ); for (final configuration in resultsByConfiguration.keys) { if (info.previousCommitHash != null) { landedResults = await fetchLandedResults(configuration); // Map will contain the last result with each name. lastLandedResultByName = { - for (final result in landedResults) result.getString(fName): result + for (final result in landedResults) result.getString(fName): result, }; } - final changes = - resultsByConfiguration[configuration]!.where(isNotLandedResult); + final changes = resultsByConfiguration[configuration]!.where( + isNotLandedResult, + ); await Pool(30).forEach(changes, guardedStoreChange).drain(); } @@ -108,7 +121,11 @@ class Tryjob { success = false; } await firestore.recordTryBuild( - info, buildbucketID, success, counter.hasTruncatedChanges); + info, + buildbucketID, + success, + counter.hasTruncatedChanges, + ); final status = BuildStatus() ..success = success ..truncatedResults = counter.hasTruncatedChanges; @@ -131,8 +148,11 @@ class Tryjob { transformChange(change); counter.count(change); if (counter.isNotReported(change)) return; - final approved = - await firestore.storeTryChange(change, info.review, info.patchset); + final approved = await firestore.storeTryChange( + change, + info.review, + info.patchset, + ); if (!approved && isFailure(change)) { counter.unapprovedFailures++; success = false; @@ -143,20 +163,24 @@ class Tryjob { final resultsBase = await commits.getCommit(info.previousCommitHash!); final rebaseBase = await commits.getCommit(baseRevision); if (resultsBase.index > rebaseBase.index) { - print('Try build is rebased on $baseRevision, which is before ' - 'the commit ${info.previousCommitHash} with CI comparison results'); + print( + 'Try build is rebased on $baseRevision, which is before ' + 'the commit ${info.previousCommitHash} with CI comparison results', + ); return []; } final reviews = [ - for (var index = resultsBase.index + 1; - index <= rebaseBase.index; - ++index) - (await commits.getCommitByIndex(index)).review + for ( + var index = resultsBase.index + 1; + index <= rebaseBase.index; + ++index + ) + (await commits.getCommitByIndex(index)).review, ]; return [ for (final landedReview in reviews) if (landedReview != null) - ...await firestore.tryResults(landedReview, configuration) + ...await firestore.tryResults(landedReview, configuration), ]; } } diff --git a/builder/pubspec.lock b/builder/pubspec.lock index f12aa96..d20320f 100644 --- a/builder/pubspec.lock +++ b/builder/pubspec.lock @@ -5,202 +5,218 @@ packages: dependency: transitive description: name: _discoveryapis_commons - sha256: f8bb1fdbd77f3d5c1d62b5b0eca75fbf1e41bf4f6c62628f880582e2182ae45d + sha256: "113c4100b90a5b70a983541782431b82168b3cae166ab130649c36eb3559d498" url: "https://pub.dev" source: hosted - version: "1.0.6" + version: "1.0.7" _fe_analyzer_shared: dependency: transitive description: name: _fe_analyzer_shared - sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 + sha256: "5b7468c326d2f8a4f630056404ca0d291ade42918f4a3c6233618e724f39da8e" url: "https://pub.dev" source: hosted - version: "64.0.0" + version: "92.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" + sha256: "70e4b1ef8003c64793a9e268a551a82869a8a96f39deb73dea28084b0e8bf75e" url: "https://pub.dev" source: hosted - version: "6.2.0" + version: "9.0.0" args: dependency: "direct main" description: name: args - sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.7.0" async: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.13.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" + cli_config: + dependency: transitive + description: + name: cli_config + sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec + url: "https://pub.dev" + source: hosted + version: "0.2.0" collection: dependency: "direct main" description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.1" convert: dependency: transitive description: name: convert - sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.1.2" coverage: dependency: transitive description: name: coverage - sha256: "2fb815080e44a09b85e0f2ca8a820b15053982b2e714b59267719e8a9ff17097" + sha256: "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d" url: "https://pub.dev" source: hosted - version: "1.6.3" + version: "1.15.0" crypto: dependency: transitive description: name: crypto - sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.7" file: dependency: transitive description: name: file - sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 url: "https://pub.dev" source: hosted - version: "7.0.0" + version: "7.0.1" frontend_server_client: dependency: transitive description: name: frontend_server_client - sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "4.0.0" glob: dependency: "direct main" description: name: glob - sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.3" + google_identity_services_web: + dependency: transitive + description: + name: google_identity_services_web + sha256: "5d187c46dc59e02646e10fe82665fc3884a9b71bc1c90c2b8b749316d33ee454" + url: "https://pub.dev" + source: hosted + version: "0.3.3+1" googleapis: dependency: "direct main" description: name: googleapis - sha256: c2f311bcd1b3e4052234162edc6b626a9013a7e1385d6aad37e9e6a6c5f89908 + sha256: "692fb9e90c321b61a7a2123de0353ec8a20691cd979db2553d8d732f710f6535" url: "https://pub.dev" source: hosted - version: "11.4.0" + version: "15.0.0" googleapis_auth: dependency: "direct main" description: name: googleapis_auth - sha256: af7c3a3edf9d0de2e1e0a77e994fae0a581c525fa7012af4fa0d4a52ed9484da + sha256: b81fe352cc4a330b3710d2b7ad258d9bcef6f909bb759b306bf42973a7d046db url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "2.0.0" http: dependency: "direct main" description: name: http - sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.6.0" http_multi_server: dependency: transitive description: name: http_multi_server - sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 url: "https://pub.dev" source: hosted - version: "3.2.1" + version: "3.2.2" http_parser: dependency: transitive description: name: http_parser - sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" url: "https://pub.dev" source: hosted - version: "4.0.2" + version: "4.1.2" io: dependency: transitive description: name: io - sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.5" js: dependency: transitive description: name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" url: "https://pub.dev" source: hosted - version: "0.6.7" + version: "0.7.2" lints: dependency: "direct dev" description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + sha256: a5e2b223cb7c9c8efdc663ef484fdd95bb243bff242ef5b13e26883547fce9a0 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "6.0.0" logging: dependency: transitive description: name: logging - sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.0" matcher: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.17" meta: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.17.0" mime: dependency: transitive description: name: mime - sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "2.0.0" node_preamble: dependency: transitive description: @@ -213,34 +229,34 @@ packages: dependency: transitive description: name: package_config - sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc url: "https://pub.dev" source: hosted - version: "2.1.0" + version: "2.2.0" path: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.1" pool: dependency: "direct main" description: name: pool - sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d" url: "https://pub.dev" source: hosted - version: "1.5.1" + version: "1.5.2" pub_semver: dependency: transitive description: name: pub_semver - sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" retry: dependency: "direct main" description: @@ -253,10 +269,10 @@ packages: dependency: transitive description: name: shelf - sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.4.2" shelf_packages_handler: dependency: transitive description: @@ -269,130 +285,146 @@ packages: dependency: transitive description: name: shelf_static - sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e + sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.3" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" + sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "3.0.0" source_map_stack_trace: dependency: transitive description: name: source_map_stack_trace - sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" + sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" source_maps: dependency: transitive description: name: source_maps - sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" + sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812" url: "https://pub.dev" source: hosted - version: "0.10.12" + version: "0.10.13" source_span: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.10.1" stack_trace: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" string_scanner: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.4.1" term_glyph: dependency: transitive description: name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" test: dependency: "direct dev" description: name: test - sha256: "9b0dd8e36af4a5b1569029949d50a52cb2a2a2fdaa20cebb96e6603b9ae241f9" + sha256: "8f0eb7fa76b7d05a4f3707e0dbd581babef5b0915ca508b757cf15d0cabb56cb" url: "https://pub.dev" source: hosted - version: "1.24.6" + version: "1.27.0" test_api: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "19a78f63e83d3a61f00826d09bc2f60e191bf3504183c001262be6ac75589fb8" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.8" test_core: dependency: transitive description: name: test_core - sha256: "4bef837e56375537055fdbbbf6dd458b1859881f4c7e6da936158f77d61ab265" + sha256: bad9916601a4f2ef6e4dbc466fb712e4b42cf4917c3fd428b018f51984fce13b url: "https://pub.dev" source: hosted - version: "0.5.6" + version: "0.6.13" typed_data: dependency: transitive description: name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.4.0" vm_service: dependency: transitive description: name: vm_service - sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583 + sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" url: "https://pub.dev" source: hosted - version: "11.10.0" + version: "15.0.2" watcher: dependency: transitive description: name: watcher - sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + sha256: "592ab6e2892f67760543fb712ff0177f4ec76c031f02f5b4ff8d3fc5eb9fb61a" + url: "https://pub.dev" + source: hosted + version: "1.1.4" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.0.1" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "3.0.3" webkit_inspection_protocol: dependency: transitive description: @@ -405,9 +437,9 @@ packages: dependency: transitive description: name: yaml - sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "3.1.3" sdks: - dart: "3.1.0" + dart: ">=3.10.1 <4.0.0" diff --git a/builder/pubspec.yaml b/builder/pubspec.yaml index 0690a0a..98b6b8e 100644 --- a/builder/pubspec.yaml +++ b/builder/pubspec.yaml @@ -4,18 +4,18 @@ publish_to: none version: 0.1.0 environment: - sdk: 3.1.0 # Pinned, the recipe will use this exact version. + sdk: ^3.10.1 # The recipe will use this exact version. dependencies: args: ^2.3.0 - googleapis_auth: ^1.2.0 - googleapis: ^11.4.0 + googleapis_auth: ^2.0.0 + googleapis: ^15.0.0 http: ^1.1.0 pool: ^1.5.0 retry: ^3.1.0 - collection: ^1.15.0-nullsafety.4 + collection: ^1.15.0 glob: ^2.1.0 dev_dependencies: - lints: ^2.0.0 + lints: ^6.0.0 test: ^1.9.4 diff --git a/builder/test/approvals_test.dart b/builder/test/approvals_test.dart index 4ce17fe..e008a75 100644 --- a/builder/test/approvals_test.dart +++ b/builder/test/approvals_test.dart @@ -69,34 +69,52 @@ Future removeBuildersAndResults() async { } for (final test in testsToRemove) { - await deleteDocuments(await firestore.query( - from: 'try_results', where: fieldEquals('name', test))); + await deleteDocuments( + await firestore.query( + from: 'try_results', + where: fieldEquals('name', test), + ), + ); } for (final test in testsToRemove) { - await deleteDocuments(await firestore.query( - from: 'results', where: fieldEquals('name', test))); + await deleteDocuments( + await firestore.query(from: 'results', where: fieldEquals('name', test)), + ); } for (final builder in buildersToRemove) { - await deleteDocuments(await firestore.query( - from: 'try_builds', where: fieldEquals('builder', builder))); + await deleteDocuments( + await firestore.query( + from: 'try_builds', + where: fieldEquals('builder', builder), + ), + ); } for (final builder in buildersToRemove) { - await deleteDocuments(await firestore.query( - from: 'builds', where: fieldEquals('builder', builder))); + await deleteDocuments( + await firestore.query( + from: 'builds', + where: fieldEquals('builder', builder), + ), + ); } for (final builder in buildersToRemove) { - await deleteDocuments(await firestore.query( - from: 'configurations', where: fieldEquals('builder', builder))); + await deleteDocuments( + await firestore.query( + from: 'configurations', + where: fieldEquals('builder', builder), + ), + ); } } Future loadTestCommits(int startIndex) async { // Get review data for the last two landed CLs before or at startIndex. final reviews = await firestore.query( - from: 'reviews', - orderBy: orderBy('landed_index', false), - where: fieldLessThanOrEqual('landed_index', startIndex), - limit: 2); + from: 'reviews', + orderBy: orderBy('landed_index', false), + where: fieldLessThanOrEqual('landed_index', startIndex), + limit: 2, + ); final firstReview = reviews.first; index1 = firstReview.fields['landed_index']!.integerValue!; review = firstReview.name.split('/').last; @@ -130,13 +148,10 @@ Future loadTestCommits(int startIndex) async { var commits = { for (final index in [index1, index2, index3, index4]) index: (await firestore.query( - from: 'commits', - where: fieldEquals('index', int.parse(index)), - limit: 1)) - .first - .name - .split('/') - .last + from: 'commits', + where: fieldEquals('index', int.parse(index)), + limit: 1, + )).first.name.split('/').last, }; commit1 = commits[index1]!; commit2 = commits[index2]!; @@ -144,21 +159,27 @@ Future loadTestCommits(int startIndex) async { commit4 = commits[index4]!; } -Tryjob makeTryjob(String name, Map firstChange, - {String? baseCommit}) => - Tryjob( - BuildInfo.fromResult(firstChange, {firstChange[fConfiguration]}) - as TryBuildInfo, - 'bbID_$name', - baseCommit ?? commit4, - commitsCache, - firestore, - client); +Tryjob makeTryjob( + String name, + Map firstChange, { + String? baseCommit, +}) => Tryjob( + BuildInfo.fromResult(firstChange, {firstChange[fConfiguration]}) + as TryBuildInfo, + 'bbID_$name', + baseCommit ?? commit4, + commitsCache, + firestore, + client, +); const newFailure = 'Pass/RuntimeError/Pass'; Map makeTryChange( - String name, String result, String patchsetRef, - {String? testName}) { + String name, + String result, + String patchsetRef, { + String? testName, +}) { final results = result.split('/'); final previous = results[0]; final current = results[1]; @@ -190,8 +211,12 @@ Map makeTryChange( } Map makeChange( - String name, String result, String commit, String previousCommit, - {String? testName}) { + String name, + String result, + String commit, + String previousCommit, { + String? testName, +}) { final change = { ...makeTryChange(name, result, '', testName: testName), 'commit_hash': commit, @@ -201,15 +226,19 @@ Map makeChange( } Build makeBuild(String commit, Map change) { - return Build(BuildInfo.fromResult(change, {change[fConfiguration]}), - commitsCache, firestore); + return Build( + BuildInfo.fromResult(change, {change[fConfiguration]}), + commitsCache, + firestore, + ); } void main() async { final baseClient = http.Client(); client = await clientViaApplicationDefaultCredentials( - scopes: ['https://www.googleapis.com/auth/cloud-platform'], - baseClient: baseClient); + scopes: ['https://www.googleapis.com/auth/cloud-platform'], + baseClient: baseClient, + ); final api = FirestoreApi(client); firestore = FirestoreService(api, client); if (!firestore.isStaging) { @@ -231,8 +260,9 @@ void main() async { expect(int.parse(index2), lessThan(int.parse(index1))); // reviewWithComments should have some comments, to test linking final comments = await firestore.query( - from: 'comments', - where: fieldEquals('review', int.parse(reviewWithComments))); + from: 'comments', + where: fieldEquals('review', int.parse(reviewWithComments)), + ); expect(comments, isNotEmpty); }); @@ -242,32 +272,50 @@ void main() async { final change1 = makeTryChange('approvals', newFailure, lastPatchsetRef); await makeTryjob('approvals', change1).process([change1]); var documents = await firestore.query( - from: 'try_results', where: fieldEquals('name', 'approvals_test')); + from: 'try_results', + where: fieldEquals('name', 'approvals_test'), + ); await firestore.approveResult(documents.single.toDocument()); - final change2 = makeTryChange('approvals', newFailure, patchsetGroupRef, - testName: 'approvals_2'); + final change2 = makeTryChange( + 'approvals', + newFailure, + patchsetGroupRef, + testName: 'approvals_2', + ); await makeTryjob('approvals', change2).process([change2]); documents = await firestore.query( - from: 'try_results', where: fieldEquals('name', 'approvals_2_test')); + from: 'try_results', + where: fieldEquals('name', 'approvals_2_test'), + ); await firestore.approveResult(documents.single.toDocument()); final change3 = makeChange('approvals', newFailure, commit1, commit4); final change3a = makeChange('approvals', newFailure, commit1, commit4) ..['configuration'] = 'second_approvals_configuration'; - final change4 = makeChange('approvals', newFailure, commit1, commit4, - testName: 'approvals_2'); - final status = - await makeBuild(commit1, change3).process([change3, change3a, change4]); + final change4 = makeChange( + 'approvals', + newFailure, + commit1, + commit4, + testName: 'approvals_2', + ); + final status = await makeBuild( + commit1, + change3, + ).process([change3, change3a, change4]); await checkBuild(change3['builder_name'], index1, success: true); expect(status.success, isTrue); expect(status.truncatedResults, isFalse); - await checkResult(change3, index3, index1, { - 'approved': true, - }); + await checkResult(change3, index3, index1, {'approved': true}); // Add a second configuration to an existing test failure, narrowing // the blamelist - final change5 = makeChange('approvals_3', newFailure, commit1, commit3, - testName: 'approvals'); + final change5 = makeChange( + 'approvals_3', + newFailure, + commit1, + commit3, + testName: 'approvals', + ); final status2 = await makeBuild(commit1, change5).process([change5]); await checkBuild(change5['builder_name'], index1, success: true); expect(status2.success, isTrue); @@ -276,7 +324,7 @@ void main() async { 'configurations': [ change3['configuration'], change3a['configuration'], - change5['configuration'] + change5['configuration'], ], }); }); @@ -288,10 +336,13 @@ void main() async { expect(reviewWithComments, isNot(review)); expect(reviewWithComments, isNot(review2)); expect( - await firestore.reviewIsLanded(int.parse(reviewWithComments)), isTrue); + await firestore.reviewIsLanded(int.parse(reviewWithComments)), + isTrue, + ); var commentsQuery = await firestore.query( - from: 'comments', - where: fieldEquals('review', int.parse(reviewWithComments))); + from: 'comments', + where: fieldEquals('review', int.parse(reviewWithComments)), + ); final landedIndex = commentsQuery.first.fields[fBlamelistStartIndex]!.integerValue!; for (final item in commentsQuery) { @@ -301,39 +352,53 @@ void main() async { expect(fields[fReview]!.integerValue, reviewWithComments); fields.remove(fBlamelistStartIndex); fields.remove(fBlamelistEndIndex); - await firestore.updateFields( - item.toDocument(), [fBlamelistStartIndex, fBlamelistEndIndex]); + await firestore.updateFields(item.toDocument(), [ + fBlamelistStartIndex, + fBlamelistEndIndex, + ]); } - var reviewDocument = await firestore - .getDocument('${firestore.documents}/reviews/$reviewWithComments'); + var reviewDocument = await firestore.getDocument( + '${firestore.documents}/reviews/$reviewWithComments', + ); expect(reviewDocument.fields!['landed_index']!.integerValue, landedIndex); reviewDocument.fields!.remove('landed_index'); await firestore.updateFields(reviewDocument, ['landed_index']); await firestore.linkReviewToCommit( - int.parse(reviewWithComments), int.parse(landedIndex)); + int.parse(reviewWithComments), + int.parse(landedIndex), + ); await firestore.linkCommentsToCommit( - int.parse(reviewWithComments), int.parse(landedIndex)); + int.parse(reviewWithComments), + int.parse(landedIndex), + ); commentsQuery = await firestore.query( - from: 'comments', - where: fieldEquals('review', int.parse(reviewWithComments))); + from: 'comments', + where: fieldEquals('review', int.parse(reviewWithComments)), + ); for (final item in commentsQuery) { final fields = item.fields; expect(fields[fBlamelistStartIndex]!.integerValue, landedIndex); expect(fields[fBlamelistEndIndex]!.integerValue, landedIndex); expect(fields[fReview]!.integerValue, reviewWithComments); } - reviewDocument = await firestore - .getDocument('${firestore.documents}/reviews/$reviewWithComments'); + reviewDocument = await firestore.getDocument( + '${firestore.documents}/reviews/$reviewWithComments', + ); expect(reviewDocument.fields!['landed_index']!.integerValue, landedIndex); }); } -Future checkTryBuild(String name, - {bool? success, bool? truncated}) async { +Future checkTryBuild( + String name, { + bool? success, + bool? truncated, +}) async { final buildbucketId = 'bbID_$name'; final buildDocuments = await firestore.query( - from: 'try_builds', where: fieldEquals('buildbucket_id', buildbucketId)); + from: 'try_builds', + where: fieldEquals('buildbucket_id', buildbucketId), + ); expect(buildDocuments.length, 1); final document = buildDocuments.single; expect(document.getBool('success'), success); @@ -345,23 +410,33 @@ Future checkTryBuild(String name, } Future checkBuild(String? builder, String index, {bool? success}) async { - final document = await firestore - .getDocument('${firestore.documents}/builds/$builder:$index'); + final document = await firestore.getDocument( + '${firestore.documents}/builds/$builder:$index', + ); expect(document.fields!['success']!.booleanValue, success); } -Future checkResult(Map change, String startIndex, - String endIndex, Map expected) async { +Future checkResult( + Map change, + String startIndex, + String endIndex, + Map expected, +) async { expect([fConfigurations, fApproved], containsAll(expected.keys)); final resultName = await firestore.findResult( - change, int.parse(startIndex), int.parse(endIndex)); + change, + int.parse(startIndex), + int.parse(endIndex), + ); expect(resultName, isNotNull); final resultDocument = await firestore.getDocument(resultName!); final data = untagMap(resultDocument.fields!); expect(data[fName], change[fName]); expect(data[fBlamelistStartIndex], int.parse(startIndex)); expect(data[fBlamelistEndIndex], int.parse(endIndex)); - expect(data[fConfigurations], - unorderedEquals(expected[fConfigurations] ?? data[fConfigurations])); + expect( + data[fConfigurations], + unorderedEquals(expected[fConfigurations] ?? data[fConfigurations]), + ); expect(data[fApproved], expected[fApproved] ?? data[fApproved]); } diff --git a/builder/test/builder_test.dart b/builder/test/builder_test.dart index fcba73d..f9a9cbc 100644 --- a/builder/test/builder_test.dart +++ b/builder/test/builder_test.dart @@ -56,33 +56,45 @@ Future removeBuildersAndResults() async { } for (final test in testsToRemove) { - await deleteDocuments(await firestore.query( - from: 'results', where: fieldEquals(fName, test))); + await deleteDocuments( + await firestore.query(from: 'results', where: fieldEquals(fName, test)), + ); } for (final builder in buildersToRemove) { - await deleteDocuments(await firestore.query( - from: 'builds', where: fieldEquals('builder', builder))); + await deleteDocuments( + await firestore.query( + from: 'builds', + where: fieldEquals('builder', builder), + ), + ); } } Future loadCommits() async { commit = await commitsCache.getCommitByIndex(index); previousCommit = await commitsCache.getCommitByIndex(previousIndex); - previousBlamelistStartCommit = - await commitsCache.getCommitByIndex(previousBlamelistStart); - previousBlamelistEndCommit = - await commitsCache.getCommitByIndex(previousBlamelistEnd); - previousBuildPreviousCommit = - await commitsCache.getCommitByIndex(previousBuildPreviousIndex); + previousBlamelistStartCommit = await commitsCache.getCommitByIndex( + previousBlamelistStart, + ); + previousBlamelistEndCommit = await commitsCache.getCommitByIndex( + previousBlamelistEnd, + ); + previousBuildPreviousCommit = await commitsCache.getCommitByIndex( + previousBuildPreviousIndex, + ); } Build makeBuild(Map firstChange) => Build( - BuildInfo.fromResult(firstChange, {firstChange[fConfiguration]}), - commitsCache, - firestore); + BuildInfo.fromResult(firstChange, {firstChange[fConfiguration]}), + commitsCache, + firestore, +); -Map makeChange(String name, String result, - {bool flaky = false}) { +Map makeChange( + String name, + String result, { + bool flaky = false, +}) { final results = result.split('/'); final previous = results[0]; final current = results[1]; @@ -122,8 +134,9 @@ Map makePreviousChange(String name, String result) { void main() async { final baseClient = http.Client(); client = await clientViaApplicationDefaultCredentials( - scopes: ['https://www.googleapis.com/auth/cloud-platform'], - baseClient: baseClient); + scopes: ['https://www.googleapis.com/auth/cloud-platform'], + baseClient: baseClient, + ); final api = FirestoreApi(client); firestore = FirestoreService(api, client); if (!firestore.isStaging) { @@ -138,9 +151,10 @@ void main() async { }); test('existing failure', () async { - final failingPreviousChange = - makePreviousChange('failure', 'Pass/RuntimeError/Pass') - ..[fName] = 'previous_failure_test'; + final failingPreviousChange = makePreviousChange( + 'failure', + 'Pass/RuntimeError/Pass', + )..[fName] = 'previous_failure_test'; registerChangeForDeletion(failingPreviousChange); // Name changed. final previousBuild = makeBuild(failingPreviousChange); final previousStatus = await previousBuild.process([failingPreviousChange]); @@ -161,34 +175,46 @@ void main() async { final failure = failures .where((failure) => failure.getString(fName) == 'failure_test') .single; - expect(previousFailure.getStringOrNull(fBlamelistEndCommit), - previousBlamelistEndCommit.hash); - expect(previousFailure.getStringOrNull(fBlamelistStartCommit), - previousBlamelistStartCommit.hash); + expect( + previousFailure.getStringOrNull(fBlamelistEndCommit), + previousBlamelistEndCommit.hash, + ); + expect( + previousFailure.getStringOrNull(fBlamelistStartCommit), + previousBlamelistStartCommit.hash, + ); expect(failure.getStringOrNull(fBlamelistEndCommit), commit.hash); expect(failure.getStringOrNull(fBlamelistStartCommit), commit.hash); final message = status.toJson(); expect(message, matches(r'There are unapproved failures\\n')); expect( - message, - matches( - r'previous_failure_test \(Pass -> RuntimeError , expected Pass \) at 44baaf\.\.ebe06b')); + message, + matches( + r'previous_failure_test \(Pass -> RuntimeError , expected Pass \) at 44baaf\.\.ebe06b', + ), + ); expect( - message, - matches( - r'failure_test \(Pass -> RuntimeError , expected Pass \) at 2368c2')); + message, + matches( + r'failure_test \(Pass -> RuntimeError , expected Pass \) at 2368c2', + ), + ); // Check a build with no changed results. // Another build with the same blamelist and configuration is allowed, // although it should not happen in practice. This build has no changes. - final unchangedChange = - makeChange('failure', 'RuntimeError/RuntimeError/Pass'); + final unchangedChange = makeChange( + 'failure', + 'RuntimeError/RuntimeError/Pass', + ); final unchangedBuild = makeBuild(unchangedChange); final unchangedStatus = await unchangedBuild.process([]); expect(unchangedStatus.success, isTrue); expect(unchangedStatus.unapprovedFailures, isNotEmpty); - expect(unchangedStatus.unapprovedFailures['failure_configuration']!, - isNotEmpty); + expect( + unchangedStatus.unapprovedFailures['failure_configuration']!, + isNotEmpty, + ); }); test('existing approved failure', () async { @@ -198,28 +224,35 @@ void main() async { ..[fPreviousCommitHash] = previousBuildPreviousCommit.hash; registerChangeForDeletion(failingOtherConfigurationChange); final otherConfigurationBuild = makeBuild(failingOtherConfigurationChange); - final otherStatus = await otherConfigurationBuild - .process([failingOtherConfigurationChange]); + final otherStatus = await otherConfigurationBuild.process([ + failingOtherConfigurationChange, + ]); expect(otherStatus.success, isFalse); expect(otherStatus.unapprovedFailures, isNotEmpty); expect( - otherStatus.unapprovedFailures.keys, contains('other_configuration')); + otherStatus.unapprovedFailures.keys, + contains('other_configuration'), + ); final result = (await firestore.findActiveResults( - 'approved_failure_test', 'other_configuration')) - .single; + 'approved_failure_test', + 'other_configuration', + )).single; expect(result.getInt(fBlamelistEndIndex), index); expect(result.getInt(fBlamelistStartIndex), previousBlamelistStart); await firestore.approveResult(result.toDocument()); - final failingChange = - makeChange('approved_failure', 'Pass/RuntimeError/Pass'); + final failingChange = makeChange( + 'approved_failure', + 'Pass/RuntimeError/Pass', + ); final build = makeBuild(failingChange); final status = await build.process([failingChange]); expect(status.success, isTrue); expect(status.truncatedResults, isFalse); expect(status.unapprovedFailures, isEmpty); final changedResult = (await firestore.findActiveResults( - 'approved_failure_test', 'other_configuration')) - .single; + 'approved_failure_test', + 'other_configuration', + )).single; expect(result.name, changedResult.name); // Check blamelist narrowing. expect(changedResult.getInt(fBlamelistEndIndex), index); diff --git a/builder/test/commits_cache_test.dart b/builder/test/commits_cache_test.dart index 50f924a..d615e67 100644 --- a/builder/test/commits_cache_test.dart +++ b/builder/test/commits_cache_test.dart @@ -20,8 +20,9 @@ import 'package:builder/src/commits_cache.dart'; void main() async { final baseClient = http.Client(); final client = await clientViaApplicationDefaultCredentials( - scopes: ['https://www.googleapis.com/auth/cloud-platform'], - baseClient: baseClient); + scopes: ['https://www.googleapis.com/auth/cloud-platform'], + baseClient: baseClient, + ); final firestore = fs.FirestoreService(FirestoreApi(client), client); // create commits cache final commits = TestingCommitsCache(firestore, baseClient); @@ -58,7 +59,7 @@ final commit68889 = { 'title': '[Cleanup] Removes deprecated --gc_at_instance_allocation.', 'index': 68889, 'created': DateTime.parse('2020-02-26 15:00:26.000Z'), - 'hash': '9c05fde96b62556944befd18ec834c56d6854fda' + 'hash': '9c05fde96b62556944befd18ec834c56d6854fda', }; final commit68890 = { @@ -67,7 +68,7 @@ final commit68890 = { 'Add analyzer run support to steamroller and minor QOL improvements.', 'index': 68890, 'created': DateTime.parse('2020-02-26 16:57:46.000Z'), - 'hash': '31053a8c0180b663858aadce1ff6c0eefcf78623' + 'hash': '31053a8c0180b663858aadce1ff6c0eefcf78623', }; final commit68900 = { @@ -83,7 +84,7 @@ final commit68905 = { 'title': '[dart2js] switch bot to use hostaserts once again', 'index': 68905, 'created': DateTime.parse('2020-02-26 21:41:47.000Z'), - 'hash': '5055c98beeacb3996c256e37148b4dc3561735ee' + 'hash': '5055c98beeacb3996c256e37148b4dc3561735ee', }; final commit68910 = { diff --git a/builder/test/fakes.dart b/builder/test/fakes.dart index a1a37e1..c06d2fc 100644 --- a/builder/test/fakes.dart +++ b/builder/test/fakes.dart @@ -23,10 +23,10 @@ class BuilderTest { BuilderTest(this.firstChange) { commitsCache = CommitsCache(firestore, client); builder = Build( - BuildInfo.fromResult( - firstChange, {firstChange[fConfiguration]}), - commitsCache, - firestore); + BuildInfo.fromResult(firstChange, {firstChange[fConfiguration]}), + commitsCache, + firestore, + ); } Future update() async { @@ -46,8 +46,9 @@ class BuilderTest { class FirestoreServiceFake implements FirestoreService { Map> commits = Map.from(fakeFirestoreCommits); Map> results = Map.from(fakeFirestoreResults); - List> fakeTryResults = - List.from(fakeFirestoreTryResults); + List> fakeTryResults = List.from( + fakeFirestoreTryResults, + ); int addedResultIdCounter = 1; @override @@ -78,7 +79,8 @@ class FirestoreServiceFake implements FirestoreService { @override Future getLastCommit() => getCommitByIndex( - commits.values.map((commit) => commit[fIndex]).reduce(max)); + commits.values.map((commit) => commit[fIndex]).reduce(max), + ); @override Future addCommit(String id, Map data) async { @@ -87,7 +89,10 @@ class FirestoreServiceFake implements FirestoreService { @override Future findResult( - Map change, int startIndex, int endIndex) { + Map change, + int startIndex, + int endIndex, + ) { String? resultId; int? resultEndIndex; for (final entry in results.entries) { @@ -110,15 +115,19 @@ class FirestoreServiceFake implements FirestoreService { @override Future> findActiveResults( - String? name, String? configuration) async { + String? name, + String? configuration, + ) async { return [ for (final id in results.keys) if (results[id]![fName] == name && results[id]![fActiveConfigurations] != null && results[id]![fActiveConfigurations].contains(configuration)) - SafeDocument(Document() - ..fields = taggedMap(Map.from(results[id]!)) - ..name = id) + SafeDocument( + Document() + ..fields = taggedMap(Map.from(results[id]!)) + ..name = id, + ), ]; } @@ -134,12 +143,18 @@ class FirestoreServiceFake implements FirestoreService { @override Future updateResult( - String resultId, String? configuration, int startIndex, int endIndex, - {required bool failure}) { + String resultId, + String? configuration, + int startIndex, + int endIndex, { + required bool failure, + }) { final result = Map.from(results[resultId]!); - result[fBlamelistStartIndex] = - max(startIndex, result[fBlamelistStartIndex]); + result[fBlamelistStartIndex] = max( + startIndex, + result[fBlamelistStartIndex], + ); result[fBlamelistEndIndex] = min(endIndex, result[fBlamelistEndIndex]); if (!result[fConfigurations].contains(configuration)) { @@ -162,7 +177,9 @@ class FirestoreServiceFake implements FirestoreService { @override Future removeActiveConfiguration( - SafeDocument activeResult, String? configuration) async { + SafeDocument activeResult, + String? configuration, + ) async { final result = Map.from(results[activeResult.name]!); result[fActiveConfigurations] = List.from(result[fActiveConfigurations]) ..remove(configuration); @@ -176,10 +193,12 @@ class FirestoreServiceFake implements FirestoreService { @override Future>> findRevertedChanges(int index) async { return results.values - .where((change) => - change[fPinnedIndex] == index || - (change[fBlamelistStartIndex] == index && - change[fBlamelistEndIndex] == index)) + .where( + (change) => + change[fPinnedIndex] == index || + (change[fBlamelistStartIndex] == index && + change[fBlamelistEndIndex] == index), + ) .map(taggedMap) .toList(); } @@ -188,25 +207,38 @@ class FirestoreServiceFake implements FirestoreService { Future> tryApprovals(int review) async { return fakeTryResults .where( - (result) => result[fReview] == review && result[fApproved] == true) + (result) => result[fReview] == review && result[fApproved] == true, + ) .map(taggedMap) - .map((fields) => SafeDocument(Document() - ..fields = fields - ..name = '')) + .map( + (fields) => SafeDocument( + Document() + ..fields = fields + ..name = '', + ), + ) .toList(); } @override Future> tryResults( - int review, String configuration) async { + int review, + String configuration, + ) async { return fakeTryResults - .where((result) => - result[fReview] == review && - result[fConfigurations].contains(configuration)) + .where( + (result) => + result[fReview] == review && + result[fConfigurations].contains(configuration), + ) .map(taggedMap) - .map((fields) => SafeDocument(Document() - ..fields = fields - ..name = '')) + .map( + (fields) => SafeDocument( + Document() + ..fields = fields + ..name = '', + ), + ) .toList(); } @@ -224,8 +256,14 @@ class FirestoreServiceFake implements FirestoreService { Future storeReview(String review, Map data) async {} @override - Future storePatchset(String review, int patchset, String kind, - String? description, int patchsetGroup, int number) async {} + Future storePatchset( + String review, + int patchset, + String kind, + String? description, + int patchsetGroup, + int number, + ) async {} } class HttpClientMock extends BaseClient { diff --git a/builder/test/firestore_test.dart b/builder/test/firestore_test.dart index 95c8ee0..22d79b7 100644 --- a/builder/test/firestore_test.dart +++ b/builder/test/firestore_test.dart @@ -23,13 +23,15 @@ import 'test_data.dart'; void main() async { final baseClient = http.Client(); final client = await clientViaApplicationDefaultCredentials( - scopes: ['https://www.googleapis.com/auth/cloud-platform'], - baseClient: baseClient); + scopes: ['https://www.googleapis.com/auth/cloud-platform'], + baseClient: baseClient, + ); final api = FirestoreApi(client); final firestore = FirestoreService(api, client); if (!firestore.isStaging) { throw (TestFailure( - 'Error: firestore_test_nodejs.dart is being run on production')); + 'Error: firestore_test_nodejs.dart is being run on production', + )); } const testDatabaseName = @@ -39,7 +41,7 @@ void main() async { 'firestore_test/Remove_active_configuration'; final createdResult = { ...activeFailureResult, - 'name': removeActiveConfigurationTestName + 'name': removeActiveConfigurationTestName, }; tearDownAll(() => baseClient.close()); @@ -54,19 +56,24 @@ void main() async { tearDown(() async { // Delete database records created by the tests. var snapshot = await firestore.query( - from: 'try_builds', where: fieldEquals('review', testReview)); + from: 'try_builds', + where: fieldEquals('review', testReview), + ); for (final doc in snapshot) { await firestore.deleteDocument(doc.name); } snapshot = await firestore.query( - from: 'patchsets', parent: 'reviews/$testReview/'); + from: 'patchsets', + parent: 'reviews/$testReview/', + ); for (final doc in snapshot) { await firestore.deleteDocument(doc.name); } snapshot = await firestore.query( - from: 'results', - where: fieldEquals('name', removeActiveConfigurationTestName)); + from: 'results', + where: fieldEquals('name', removeActiveConfigurationTestName), + ); for (final doc in snapshot) { await firestore.deleteDocument(doc.name); } @@ -79,23 +86,33 @@ void main() async { final createdResultDocument = await firestore.storeResult(createdResult); final name = removeActiveConfigurationTestName; - var foundActiveResults = - await firestore.findActiveResults(name, testConfiguration); + var foundActiveResults = await firestore.findActiveResults( + name, + testConfiguration, + ); var activeResult = foundActiveResults.single; expect(createdResultDocument.name, activeResult.name); await firestore.removeActiveConfiguration( - activeResult, testConfiguration); - foundActiveResults = - await firestore.findActiveResults(name, testConfiguration); + activeResult, + testConfiguration, + ); + foundActiveResults = await firestore.findActiveResults( + name, + testConfiguration, + ); expect(foundActiveResults, isEmpty); - foundActiveResults = - await firestore.findActiveResults(name, 'configuration 2'); + foundActiveResults = await firestore.findActiveResults( + name, + 'configuration 2', + ); activeResult = foundActiveResults.single; expect(activeResult.fields, contains('active')); await firestore.removeActiveConfiguration( - activeResult, 'configuration 2'); + activeResult, + 'configuration 2', + ); final document = await firestore.getDocument(createdResultDocument.name!); expect(document.fields, isNot(contains('active'))); expect(document.fields, isNot(contains('active_configurations'))); @@ -104,10 +121,9 @@ void main() async { test('approved try result fetching', () async { await firestore.storeReview( - testReview.toString(), - taggedMap({ - 'subject': 'test review: approved try result fetching', - })); + testReview.toString(), + taggedMap({'subject': 'test review: approved try result fetching'}), + ); await firestore.storePatchset( testReview.toString(), 1, @@ -152,12 +168,13 @@ void main() async { await firestore.storeTryChange(tryResult, testReview, 3); // Set the results on patchsets 1 and 2 to approved. final snapshot = await firestore.query( - from: 'try_results', - where: compositeFilter([ - fieldEquals('approved', false), - fieldEquals('review', testReview), - fieldLessThanOrEqual('patchset', 2) - ])); + from: 'try_results', + where: compositeFilter([ + fieldEquals('approved', false), + fieldEquals('review', testReview), + fieldLessThanOrEqual('patchset', 2), + ]), + ); for (final response in snapshot) { await firestore.approveResult(response.toDocument()); //await firestore.updateDocument(response.document.name, {'approved': taggedValue(true)}); diff --git a/builder/test/gerrit_test.dart b/builder/test/gerrit_test.dart index cf2951c..bcd2412 100644 --- a/builder/test/gerrit_test.dart +++ b/builder/test/gerrit_test.dart @@ -12,8 +12,10 @@ import 'gerrit_review_json.dart'; void main() async { test('get revert information from Gerrit log api output', () { - expect(GerritInfo.revert(json.decode(revertReviewGerritLog)), - '7ed1690b4ed6b56bc818173dff41a7a2530991a2'); + expect( + GerritInfo.revert(json.decode(revertReviewGerritLog)), + '7ed1690b4ed6b56bc818173dff41a7a2530991a2', + ); }); test('update', () async { diff --git a/builder/test/results_test.dart b/builder/test/results_test.dart index 5fc7a05..6c385d3 100644 --- a/builder/test/results_test.dart +++ b/builder/test/results_test.dart @@ -24,19 +24,26 @@ void main() async { test('Link landed commit to review', () async { final builderTest = BuilderTest(landedCommitChange); - builderTest.firestore.commits - .removeWhere((key, value) => value[fIndex] > existingCommitIndex); + builderTest.firestore.commits.removeWhere( + (key, value) => value[fIndex] > existingCommitIndex, + ); builderTest.client.addDefaultResponse(gitilesLog); await builderTest.storeBuildCommitsInfo(); await builderTest.builder.reviewsFetched; expect(builderTest.builder.endIndex, landedCommitIndex); expect(builderTest.builder.startIndex, existingCommitIndex + 1); - expect(builderTest.builder.tryApprovals, - {testResult(review44445Result): 54, testResult(review77779Result): 53}); - expect((await builderTest.firestore.getCommit(commit53Hash))!.toJson(), - commit53); - expect((await builderTest.firestore.getCommit(landedCommitHash))!.toJson(), - landedCommit); + expect(builderTest.builder.tryApprovals, { + testResult(review44445Result): 54, + testResult(review77779Result): 53, + }); + expect( + (await builderTest.firestore.getCommit(commit53Hash))!.toJson(), + commit53, + ); + expect( + (await builderTest.firestore.getCommit(landedCommitHash))!.toJson(), + landedCommit, + ); }); test('update previous active result', () async { @@ -45,29 +52,36 @@ void main() async { await builderTest.storeChange(landedCommitChange); expect(builderTest.builder.success, true); expect( - builderTest.firestore.results['activeResultID'], - Map.from(activeResult) - ..[fActiveConfigurations] = ['another configuration']); + builderTest.firestore.results['activeResultID'], + Map.from(activeResult) + ..[fActiveConfigurations] = ['another configuration'], + ); - final changeAnotherConfiguration = - Map.from(landedCommitChange) - ..['configuration'] = 'another configuration'; + final changeAnotherConfiguration = Map.from( + landedCommitChange, + )..['configuration'] = 'another configuration'; await builderTest.storeChange(changeAnotherConfiguration); expect(builderTest.builder.success, true); expect( - builderTest.firestore.results['activeResultID'], - Map.from(activeResult) - ..remove(fActiveConfigurations) - ..remove(fActive)); + builderTest.firestore.results['activeResultID'], + Map.from(activeResult) + ..remove(fActiveConfigurations) + ..remove(fActive), + ); expect(builderTest.builder.countApprovalsCopied, 1); expect(builderTest.builder.countChanges, 2); expect( - builderTest.firestore.results[await builderTest.firestore.findResult( - landedCommitChange, landedCommitIndex, landedCommitIndex)], - landedResult); + builderTest.firestore.results[await builderTest.firestore.findResult( + landedCommitChange, + landedCommitIndex, + landedCommitIndex, + )], + landedResult, + ); final result = (await builderTest.firestore.findActiveResults( - landedCommitChange['name'], landedCommitChange['configuration'])) - .single; + landedCommitChange['name'], + landedCommitChange['configuration'], + )).single; expect(untagMap(result.fields), landedResult); }); @@ -82,14 +96,19 @@ void main() async { expect(flakyChange[fResult], 'flaky'); expect(builderTest.builder.success, true); expect( - builderTest.firestore.results['activeResultID'], - Map.from(activeResult) - ..[fActiveConfigurations] = ['another configuration']); + builderTest.firestore.results['activeResultID'], + Map.from(activeResult) + ..[fActiveConfigurations] = ['another configuration'], + ); expect(builderTest.builder.countChanges, 1); expect( - builderTest.firestore.results[await builderTest.firestore - .findResult(flakyChange, landedCommitIndex, landedCommitIndex)], - flakyResult); + builderTest.firestore.results[await builderTest.firestore.findResult( + flakyChange, + landedCommitIndex, + landedCommitIndex, + )], + flakyResult, + ); }); } diff --git a/builder/test/revert_test.dart b/builder/test/revert_test.dart index fc61c7a..d9480a7 100644 --- a/builder/test/revert_test.dart +++ b/builder/test/revert_test.dart @@ -20,9 +20,11 @@ void main() async { expect(builderTest.builder.endIndex, revertCommit['index']); expect(builderTest.builder.startIndex, landedCommit['index'] + 1); expect( - (await builderTest.builder.firestore.getCommit(revertCommitHash))! - .toJson(), - revertCommit); + (await builderTest.builder.firestore.getCommit( + revertCommitHash, + ))!.toJson(), + revertCommit, + ); }); test('fetch commit that is a reland (as a reland)', () async { @@ -33,16 +35,21 @@ void main() async { expect(builderTest.builder.endIndex, relandCommit['index']); expect(builderTest.builder.startIndex, revertCommit['index'] + 1); expect( - (await builderTest.builder.firestore.getCommit(revertCommitHash))! - .toJson(), - revertCommit); + (await builderTest.builder.firestore.getCommit( + revertCommitHash, + ))!.toJson(), + revertCommit, + ); expect( - (await builderTest.builder.firestore.getCommit(commit56Hash))!.toJson(), - commit56); + (await builderTest.builder.firestore.getCommit(commit56Hash))!.toJson(), + commit56, + ); expect( - (await builderTest.builder.firestore.getCommit(relandCommitHash))! - .toJson(), - relandCommit); + (await builderTest.builder.firestore.getCommit( + relandCommitHash, + ))!.toJson(), + relandCommit, + ); }); test('fetch commit that is a reland (as a revert)', () async { @@ -52,9 +59,11 @@ void main() async { expect(builderTest.builder.endIndex, relandCommit['index']); expect(builderTest.builder.startIndex, revertCommit['index'] + 1); expect( - (await builderTest.builder.firestore.getCommit(relandCommitHash))! - .toJson(), - relandCommit); + (await builderTest.builder.firestore.getCommit( + relandCommitHash, + ))!.toJson(), + relandCommit, + ); }); test('Automatically approve expected failure on revert', () async { @@ -62,10 +71,11 @@ void main() async { await builderTest.update(); await builderTest.storeChange(revertChange); expect( - builderTest.firestore.results.values - .where((result) => result[fBlamelistEndIndex] == 55) - .single, - revertResult); + builderTest.firestore.results.values + .where((result) => result[fBlamelistEndIndex] == 55) + .single, + revertResult, + ); }); test('Revert in blamelist, doesn\'t match new failure', () async { @@ -76,8 +86,10 @@ void main() async { await builderTest.storeChange(commit56Change); Future findApproval(Map change) async { - final result = await builderTest.firestore - .findActiveResults(change[fName], change[fConfiguration]); + final result = await builderTest.firestore.findActiveResults( + change[fName], + change[fConfiguration], + ); return result.single.getBool(fApproved)!; } @@ -88,7 +100,7 @@ void main() async { } class RevertBuilderTest extends BuilderTest { - RevertBuilderTest(Map firstChange) : super(firstChange) { + RevertBuilderTest(super.firstChange) { expect(revertedCommit[fIndex] + 1, fakeFirestoreCommitsFirstIndex); expect(revertCommit[fIndex] - 1, fakeFirestoreCommitsLastIndex); firestore.commits @@ -215,7 +227,7 @@ const Map revertedChange = { 'previous_commit_hash': 'a nonexistent hash', 'previous_commit_time': 1563576211, 'previous_build_number': '306', - 'changed': true + 'changed': true, }; Map commit56Change = Map.from(revertChange) @@ -255,13 +267,17 @@ const Map revertedResult = { }; // Git logs -String? escape(s) => s.replaceAll('"', '\\"'); +String? escape(String s) => s.replaceAll('"', '\\"'); String revertGitilesLog = gitilesLog([revertCommitJson]); String relandGitilesLog = gitilesLog([relandCommitJson(relandAsRevert)]); -String revertAndRelandGitilesLog = gitilesLog( - [relandCommitJson(relandAsReland), commit56Json, revertCommitJson]); +String revertAndRelandGitilesLog = gitilesLog([ + relandCommitJson(relandAsReland), + commit56Json, + revertCommitJson, +]); -String gitilesLog(List commitLogs) => ''' +String gitilesLog(List commitLogs) => + ''' )]}' { "log": [ @@ -270,7 +286,8 @@ String gitilesLog(List commitLogs) => ''' } '''; -String revertCommitJson = ''' +String revertCommitJson = + ''' { "commit": "$revertCommitHash", "parents": ["$landedCommitHash"], @@ -284,7 +301,8 @@ String revertCommitJson = ''' } '''; -String commit56Json = ''' +String commit56Json = + ''' { "commit": "$commit56Hash", "parents": ["$revertCommitHash"], @@ -298,7 +316,8 @@ String commit56Json = ''' } '''; -String relandCommitJson(String relandLine) => ''' +String relandCommitJson(String relandLine) => + ''' { "commit": "$relandCommitHash", "parents": ["$commit56Hash"], diff --git a/builder/test/test_data.dart b/builder/test/test_data.dart index 545861f..cd35bc0 100644 --- a/builder/test/test_data.dart +++ b/builder/test/test_data.dart @@ -65,7 +65,7 @@ Map landedCommit = Map.unmodifiable({ 'index': landedCommitIndex, 'title': 'A commit used for testing tryjob approvals, with index 54', 'hash': landedCommitHash, - 'review': 44445 + 'review': 44445, }); /// Changes @@ -95,7 +95,7 @@ const Map existingCommitChange = { 'previous_commit_hash': previousCommitHash, 'previous_commit_time': 1563576211, 'previous_build_number': '306', - 'changed': true + 'changed': true, }; const Map landedCommitChange = { @@ -118,7 +118,7 @@ const Map landedCommitChange = { 'previous_commit_hash': existingCommitHash, 'previous_commit_time': 1563576211, 'previous_build_number': '306', - 'changed': true + 'changed': true, }; /// Results @@ -133,7 +133,7 @@ const Map activeFailureResult = { 'expected': 'Pass', 'previous_result': 'Pass', 'blamelist_start_index': 67195, - 'blamelist_end_index': 67195 + 'blamelist_end_index': 67195, }; // A result on existingCommit that is overridden by the new result in @@ -144,11 +144,11 @@ Map activeResult = { 'blamelist_end_index': existingCommitIndex, 'configurations': [ landedCommitChange['configuration'], - 'another configuration' + 'another configuration', ]..sort(), 'active_configurations': [ landedCommitChange['configuration'], - 'another configuration' + 'another configuration', ]..sort(), 'active': true, 'approved': false, @@ -168,7 +168,7 @@ Map landedResult = { ]..sort(), 'active_configurations': [ landedCommitChange['configuration'], - 'another configuration' + 'another configuration', ]..sort(), 'active': true, 'approved': true, @@ -195,14 +195,14 @@ const Map review44445Result = { 'configurations': [ 'dart2js-new-rti-linux-x64-d8', 'dartk-reload-rollback-linux-debug-x64', - 'dartk-reload-linux-debug-x64' + 'dartk-reload-linux-debug-x64', ], 'name': sampleTest, 'patchset': 1, 'result': 'RuntimeError', 'expected': 'Pass', 'previous_result': 'Pass', - 'approved': true + 'approved': true, }; const Map review77779Result = { 'review': 77779, @@ -212,7 +212,7 @@ const Map review77779Result = { 'result': 'RuntimeError', 'expected': 'CompileTimeError', 'previous_result': 'CompileTimeError', - 'approved': true + 'approved': true, }; const testBuilder = 'test_builder'; @@ -246,71 +246,61 @@ const Map tryjobFailingChange = { 'previous_commit_hash': existingCommitHash, 'previous_commit_time': 1563576211, 'previous_build_number': '1234', - 'changed': true + 'changed': true, }; final Map tryjob2OtherFailingChange = - Map.from(tryjobFailingChange) - ..addAll({ - 'name': 'test_suite/other_failing_test', - 'test_name': 'other_failing_test', - 'result': 'RuntimeError', - 'expected': 'Pass', - 'matches': false, - 'previous_result': 'Pass', - 'changed': true, - 'build_number': tryjob2BuildNumber, - }); + Map.from(tryjobFailingChange)..addAll({ + 'name': 'test_suite/other_failing_test', + 'test_name': 'other_failing_test', + 'result': 'RuntimeError', + 'expected': 'Pass', + 'matches': false, + 'previous_result': 'Pass', + 'changed': true, + 'build_number': tryjob2BuildNumber, + }); final Map tryjobExistingFailure = - Map.from(tryjobFailingChange) - ..addAll({ - 'name': 'test_suite/existing_failure_test', - 'test_name': 'passing_test', - 'result': 'RuntimeError', - 'expected': 'Pass', - 'matches': false, - 'previous_result': 'RuntimeError', - 'changed': false - }); + Map.from(tryjobFailingChange)..addAll({ + 'name': 'test_suite/existing_failure_test', + 'test_name': 'passing_test', + 'result': 'RuntimeError', + 'expected': 'Pass', + 'matches': false, + 'previous_result': 'RuntimeError', + 'changed': false, + }); -final Map tryjob2ExistingFailure = - Map.from(tryjobExistingFailure) - ..addAll({ - 'build_number': tryjob2BuildNumber, - }); +final Map tryjob2ExistingFailure = Map.from( + tryjobExistingFailure, +)..addAll({'build_number': tryjob2BuildNumber}); -final Map tryjob2FailingChange = - Map.from(tryjobFailingChange) - ..addAll({ - 'build_number': tryjob2BuildNumber, - }); +final Map tryjob2FailingChange = Map.from( + tryjobFailingChange, +)..addAll({'build_number': tryjob2BuildNumber}); final Map tryjobPassingChange = - Map.from(tryjobFailingChange) - ..addAll({ - 'name': 'test_suite/passing_test', - 'test_name': 'passing_test', - 'result': 'Pass', - 'expected': 'Pass', - 'matches': true, - 'previous_result': 'RuntimeError', - 'changed': true - }); + Map.from(tryjobFailingChange)..addAll({ + 'name': 'test_suite/passing_test', + 'test_name': 'passing_test', + 'result': 'Pass', + 'expected': 'Pass', + 'matches': true, + 'previous_result': 'RuntimeError', + 'changed': true, + }); -final Map tryjob2PassingChange = - Map.from(tryjobPassingChange) - ..addAll({ - 'build_number': tryjob2BuildNumber, - }); +final Map tryjob2PassingChange = Map.from( + tryjobPassingChange, +)..addAll({'build_number': tryjob2BuildNumber}); -final Map tryjob3PassingChange = - Map.from(tryjobPassingChange) - ..addAll({ - 'build_number': tryjob3BuildNumber, - }); +final Map tryjob3PassingChange = Map.from( + tryjobPassingChange, +)..addAll({'build_number': tryjob3BuildNumber}); -String gitilesLog = ''' +String gitilesLog = + ''' )]}' { "log": [ diff --git a/builder/test/tryjob_test.dart b/builder/test/tryjob_test.dart index 90ced2d..aa78de8 100644 --- a/builder/test/tryjob_test.dart +++ b/builder/test/tryjob_test.dart @@ -47,22 +47,31 @@ Future removeTryBuildersAndResults() async { } for (final test in testsToRemove) { - await deleteDocuments(await firestore.query( - from: 'try_results', where: fieldEquals('name', test))); + await deleteDocuments( + await firestore.query( + from: 'try_results', + where: fieldEquals('name', test), + ), + ); } for (final builder in buildersToRemove) { - await deleteDocuments(await firestore.query( - from: 'try_builds', where: fieldEquals('builder', builder))); + await deleteDocuments( + await firestore.query( + from: 'try_builds', + where: fieldEquals('builder', builder), + ), + ); } } Future> loadTestCommits(int startIndex) async { // Get review data for the last two landed CLs before or at startIndex. final reviews = await firestore.query( - from: 'reviews', - orderBy: orderBy('landed_index', false), - where: fieldLessThanOrEqual('landed_index', startIndex), - limit: 2); + from: 'reviews', + orderBy: orderBy('landed_index', false), + where: fieldLessThanOrEqual('landed_index', startIndex), + limit: 2, + ); final firstReview = reviews.first; final String? index = firstReview.fields['landed_index']!.integerValue; final String review = firstReview.name.split('/').last; @@ -90,13 +99,10 @@ Future> loadTestCommits(int startIndex) async { var commits = { for (final index in [index, landedIndex, baseIndex]) index: (await firestore.query( - from: 'commits', - where: fieldEquals('index', int.parse(index!)), - limit: 1)) - .first - .name - .split('/') - .last + from: 'commits', + where: fieldEquals('index', int.parse(index!)), + limit: 1, + )).first.name.split('/').last, }; return { 'index': index, @@ -111,31 +117,36 @@ Future> loadTestCommits(int startIndex) async { 'landedPatchset': landedPatchset, 'landedPatchsetRef': 'refs/changes/$landedReview/$landedPatchset', 'baseIndex': baseIndex, - 'baseCommit': commits[baseIndex] + 'baseCommit': commits[baseIndex], }; } Tryjob makeTryjob(String name, Map firstChange) => Tryjob( - BuildInfo.fromResult(firstChange, {firstChange[fConfiguration]}) - as TryBuildInfo, - 'bbID_$name', - data['landedCommit']!, - commitsCache, - firestore, - client); + BuildInfo.fromResult(firstChange, {firstChange[fConfiguration]}) + as TryBuildInfo, + 'bbID_$name', + data['landedCommit']!, + commitsCache, + firestore, + client, +); Tryjob makeLandedTryjob(String name, Map firstChange) => Tryjob( - BuildInfo.fromResult(firstChange, {firstChange[fConfiguration]}) - as TryBuildInfo, - 'bbID_$name', - data['baseCommit']!, - commitsCache, - firestore, - client); + BuildInfo.fromResult(firstChange, {firstChange[fConfiguration]}) + as TryBuildInfo, + 'bbID_$name', + data['baseCommit']!, + commitsCache, + firestore, + client, + ); -Map makeChange(String name, String result, - {bool flaky = false}) { +Map makeChange( + String name, + String result, { + bool flaky = false, +}) { final results = result.split('/'); final previous = results[0]; final current = results[1]; @@ -170,11 +181,16 @@ Map makeLandedChange(String name, String result) { return makeChange(name, result)..['commit_hash'] = data['landedPatchsetRef']; } -Future checkTryBuild(String name, - {bool? success, bool? truncated}) async { +Future checkTryBuild( + String name, { + bool? success, + bool? truncated, +}) async { final buildbucketId = 'bbID_$name'; final buildDocuments = await firestore.query( - from: 'try_builds', where: fieldEquals('buildbucket_id', buildbucketId)); + from: 'try_builds', + where: fieldEquals('buildbucket_id', buildbucketId), + ); expect(buildDocuments.length, 1); expect(buildDocuments.single.fields['success']!.booleanValue, success); if (truncated != null) { @@ -187,8 +203,9 @@ Future checkTryBuild(String name, void main() async { final baseClient = http.Client(); client = await clientViaApplicationDefaultCredentials( - scopes: ['https://www.googleapis.com/auth/cloud-platform'], - baseClient: baseClient); + scopes: ['https://www.googleapis.com/auth/cloud-platform'], + baseClient: baseClient, + ); final api = FirestoreApi(client); firestore = FirestoreService(api, client); if (!firestore.isStaging) { @@ -216,22 +233,29 @@ void main() async { 'builder': 'other_builder', }; registerChangeForDeletion(otherConfigurationChange); - final otherTryjob = - makeTryjob('other_configuration', otherConfigurationChange); - final otherFailedStatus = - await otherTryjob.process([otherConfigurationChange]); + final otherTryjob = makeTryjob( + 'other_configuration', + otherConfigurationChange, + ); + final otherFailedStatus = await otherTryjob.process([ + otherConfigurationChange, + ]); await checkTryBuild('other_configuration', success: false); expect(otherFailedStatus.success, isFalse); expect(otherFailedStatus.truncatedResults, isFalse); final result = await firestore.query( - from: 'try_results', where: fieldEquals('name', 'failure_test')); + from: 'try_results', + where: fieldEquals('name', 'failure_test'), + ); expect(result.length, 1); expect(result.single.getList('configurations')!.length, 2); }); test('landedFailure', () async { - final landedChange = - makeLandedChange('landedFailure', 'Pass/RuntimeError/Pass'); + final landedChange = makeLandedChange( + 'landedFailure', + 'Pass/RuntimeError/Pass', + ); final landedTryjob = makeLandedTryjob('landedFailure', landedChange); await landedTryjob.process([landedChange]); // This change has a base revision containing the landed failure, but @@ -245,8 +269,11 @@ void main() async { }); test('flaky', () async { - final flakyChange = - makeChange('flaky', 'Pass/RuntimeError/Pass', flaky: true); + final flakyChange = makeChange( + 'flaky', + 'Pass/RuntimeError/Pass', + flaky: true, + ); final tryjob = makeTryjob('flaky', flakyChange); final status = await tryjob.process([flakyChange]); await checkTryBuild('flaky', success: true); @@ -273,8 +300,10 @@ void main() async { ..['name'] = 'truncated_pass_2_test'; registerChangeForDeletion(failingChange); tryjob.counter.passes = ChangeCounter.maxReportedSuccesses; - final truncatedStatus = - await tryjob.process([passingChange, failingChange]); + final truncatedStatus = await tryjob.process([ + passingChange, + failingChange, + ]); await checkTryBuild('truncatedPass', success: false, truncated: true); expect(truncatedStatus.success, isFalse); expect(truncatedStatus.truncatedResults, isTrue); @@ -285,11 +314,14 @@ void main() async { expect(tryjob.counter.hasTooManyFailingChanges, isFalse); expect(tryjob.counter.hasTruncatedChanges, isTrue); final existingResult = await firestore.query( - from: 'try_results', - where: fieldEquals('name', 'truncated_pass_2_test')); + from: 'try_results', + where: fieldEquals('name', 'truncated_pass_2_test'), + ); expect(existingResult.length, 1); final truncatedResult = await firestore.query( - from: 'try_results', where: fieldEquals('name', 'truncatedPass_test')); + from: 'try_results', + where: fieldEquals('name', 'truncatedPass_test'), + ); expect(truncatedResult, isEmpty); }); @@ -299,8 +331,10 @@ void main() async { final truncatedChange = {...failingChange, 'name': 'truncated_2_test'}; registerChangeForDeletion(truncatedChange); tryjob.counter.failures = ChangeCounter.maxReportedFailures - 1; - final truncatedStatus = - await tryjob.process([failingChange, truncatedChange]); + final truncatedStatus = await tryjob.process([ + failingChange, + truncatedChange, + ]); await checkTryBuild('truncated', success: false, truncated: true); expect(truncatedStatus.success, isFalse); expect(truncatedStatus.truncatedResults, isTrue); @@ -309,36 +343,43 @@ void main() async { expect(tryjob.counter.hasTooManyFailingChanges, isTrue); expect(tryjob.counter.hasTruncatedChanges, isTrue); final existingResult = await firestore.query( - from: 'try_results', where: fieldEquals('name', 'truncated_test')); + from: 'try_results', + where: fieldEquals('name', 'truncated_test'), + ); expect(existingResult.length, 1); final truncatedResult = await firestore.query( - from: 'try_results', where: fieldEquals('name', 'truncated_2_test')); + from: 'try_results', + where: fieldEquals('name', 'truncated_2_test'), + ); expect(truncatedResult, isEmpty); }); test('patchsets', () async { final document = await firestore.getDocument( - '${firestore.documents}/reviews/${data['review']}/patchsets/${data['patchset']}'); + '${firestore.documents}/reviews/${data['review']}/patchsets/${data['patchset']}', + ); final fields = untagMap(document.fields!); expect(fields['number'].toString(), data['patchset']); await firestore.storePatchset( - data['review']!, - fields['number'], - fields['kind'], - fields['description'], - fields['patchset_group'], - fields['number']); + data['review']!, + fields['number'], + fields['kind'], + fields['description'], + fields['patchset_group'], + fields['number'], + ); final document1 = await firestore.getDocument(document.name!); expect(untagMap(document1.fields!), equals(fields)); fields['number'] += 1; fields['description'] = 'test description'; await firestore.storePatchset( - data['review']!, - fields['number'], - fields['kind'], - fields['description'], - fields['patchset_group'], - fields['number']); + data['review']!, + fields['number'], + fields['kind'], + fields['description'], + fields['patchset_group'], + fields['number'], + ); final name = '${firestore.documents}/reviews/${data['review']}/patchsets/${fields['number']}'; final document2 = await firestore.getDocument(name); From 6a72fa94efcc2b7ed3187f43a7deedaa40149282 Mon Sep 17 00:00:00 2001 From: Alexander Thomas Date: Thu, 20 Nov 2025 11:37:19 +0100 Subject: [PATCH 2/2] Upgrade SDK version in GH Action for builder --- .github/workflows/builder.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/builder.yaml b/.github/workflows/builder.yaml index 78c2e23..ae9c1d0 100644 --- a/.github/workflows/builder.yaml +++ b/.github/workflows/builder.yaml @@ -26,7 +26,7 @@ jobs: fail-fast: false matrix: sdk: - - 3.1.0 + - 3.10.1 os: - ubuntu-latest steps: