[builder] Add library for firestore helpers, rename tests
Change-Id: I025b342ba9fd85ccd26a7f04d31502fb8b89d7f1
Reviewed-on: https://dart-review.googlesource.com/c/dart_ci/+/204161
Reviewed-by: William Hesse <whesse@google.com>
Commit-Queue: William Hesse <whesse@google.com>
diff --git a/builder/lib/src/firestore.dart b/builder/lib/src/firestore.dart
index 69ebe66..608f134 100644
--- a/builder/lib/src/firestore.dart
+++ b/builder/lib/src/firestore.dart
@@ -5,168 +5,14 @@
import 'dart:convert';
import 'dart:io';
import 'dart:math' show max, min;
+
import 'package:builder/src/result.dart';
import 'package:googleapis/firestore/v1.dart';
import 'package:http/http.dart' as http;
-class ResultRecord {
- final Map<String, Value> fields;
+import 'firestore_helpers.dart';
- ResultRecord(this.fields);
-
- bool get approved => fields['approved'].booleanValue;
-
- @override
- String toString() => jsonEncode(fields);
-
- int get blamelistEndIndex {
- return int.parse(fields['blamelist_end_index'].integerValue);
- }
-
- bool containsActiveConfiguration(String configuration) {
- for (final value in fields['active_configurations'].arrayValue.values) {
- if (value.stringValue != null && value.stringValue == configuration) {
- return true;
- }
- }
- return false;
- }
-}
-
-Map<String, Value> taggedMap(Map<String, dynamic> fields) {
- return fields.map((key, value) => MapEntry(key, taggedValue(value)));
-}
-
-Value taggedValue(dynamic value) {
- if (value is int) {
- return Value()..integerValue = '$value';
- } else if (value is String) {
- return Value()..stringValue = value;
- } else if (value is bool) {
- return Value()..booleanValue = value;
- } else if (value is DateTime) {
- return Value()..timestampValue = value.toUtc().toIso8601String();
- } else if (value is List) {
- return Value()
- ..arrayValue = (ArrayValue()
- ..values = value.map((element) => taggedValue(element)).toList());
- } else if (value == null) {
- return Value()..nullValue = 'NULL_VALUE';
- } else {
- throw Exception('unsupported value type ${value.runtimeType}');
- }
-}
-
-dynamic getValue(Value value) {
- if (value.integerValue != null) {
- return int.parse(value.integerValue);
- } else if (value.stringValue != null) {
- return value.stringValue;
- } else if (value.booleanValue != null) {
- return value.booleanValue;
- } else if (value.arrayValue != null) {
- return value.arrayValue.values.map(getValue).toList();
- } else if (value.timestampValue != null) {
- return DateTime.parse(value.timestampValue);
- } else if (value.nullValue != null) {
- return null;
- }
- throw Exception('unsupported value ${value.toJson()}');
-}
-
-/// Converts a map with normal Dart values to a map where the values are
-/// JSON representation of firestore API values. For example: `{'x': 3}` is
-/// translated to `{'x': {'integerValue': 3}}`.
-Map<String, Object> taggedJsonMap(Map<String, dynamic> fields) {
- return fields.map((key, value) => MapEntry(key, taggedValue(value).toJson()));
-}
-
-Map<String, dynamic> untagMap(Map<String, Value> map) {
- return map.map((key, value) => MapEntry(key, getValue(value)));
-}
-
-List<CollectionSelector> inCollection(String name) {
- return [CollectionSelector()..collectionId = name];
-}
-
-FieldReference field(String name) {
- return FieldReference()..fieldPath = name;
-}
-
-Order orderBy(String fieldName, bool ascending) {
- return Order()
- ..field = field(fieldName)
- ..direction = ascending ? 'ASCENDING' : 'DESCENDING';
-}
-
-Filter fieldEquals(String fieldName, dynamic value) {
- return Filter()
- ..fieldFilter = (FieldFilter()
- ..field = field(fieldName)
- ..op = 'EQUAL'
- ..value = taggedValue(value));
-}
-
-Filter fieldLessThanOrEqual(String fieldName, dynamic value) {
- return Filter()
- ..fieldFilter = (FieldFilter()
- ..field = field(fieldName)
- ..op = 'LESS_THAN_OR_EQUAL'
- ..value = taggedValue(value));
-}
-
-Filter fieldGreaterThanOrEqual(String fieldName, dynamic value) {
- return Filter()
- ..fieldFilter = (FieldFilter()
- ..field = field(fieldName)
- ..op = 'GREATER_THAN_OR_EQUAL'
- ..value = taggedValue(value));
-}
-
-Filter arrayContains(String fieldName, dynamic value) {
- return Filter()
- ..fieldFilter = (FieldFilter()
- ..field = field(fieldName)
- ..op = 'ARRAY_CONTAINS'
- ..value = taggedValue(value));
-}
-
-Filter compositeFilter(List<Filter> filters) {
- return Filter()
- ..compositeFilter = (CompositeFilter()
- ..filters = filters
- ..op = 'AND');
-}
-
-class DataWrapper {
- final Map<String, Value> fields;
- DataWrapper(Document document) : fields = document.fields;
- DataWrapper.fields(this.fields);
- int getInt(String name) {
- final value = fields[name]?.integerValue;
- if (value == null) {
- return null;
- }
- return int.parse(value);
- }
-
- String getString(String name) {
- return fields[name]?.stringValue;
- }
-
- bool getBool(String name) {
- return fields[name]?.booleanValue;
- }
-
- List<dynamic> getList(String name) {
- return fields[name]?.arrayValue?.values?.map(getValue)?.toList();
- }
-
- bool isNull(String name) {
- return !fields.containsKey(name) ||
- fields['name'].nullValue == 'NULL_VALUE';
- }
-}
+export 'firestore_helpers.dart';
class Commit {
final DataWrapper wrapper;
diff --git a/builder/lib/src/firestore_helpers.dart b/builder/lib/src/firestore_helpers.dart
new file mode 100644
index 0000000..9d45900
--- /dev/null
+++ b/builder/lib/src/firestore_helpers.dart
@@ -0,0 +1,140 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:googleapis/firestore/v1.dart';
+
+Map<String, Value> taggedMap(Map<String, dynamic> fields) {
+ return fields.map((key, value) => MapEntry(key, taggedValue(value)));
+}
+
+Value taggedValue(dynamic value) {
+ if (value is int) {
+ return Value()..integerValue = '$value';
+ } else if (value is String) {
+ return Value()..stringValue = value;
+ } else if (value is bool) {
+ return Value()..booleanValue = value;
+ } else if (value is DateTime) {
+ return Value()..timestampValue = value.toUtc().toIso8601String();
+ } else if (value is List) {
+ return Value()
+ ..arrayValue = (ArrayValue()
+ ..values = value.map((element) => taggedValue(element)).toList());
+ } else if (value == null) {
+ return Value()..nullValue = 'NULL_VALUE';
+ } else {
+ throw Exception('unsupported value type ${value.runtimeType}');
+ }
+}
+
+dynamic getValue(Value value) {
+ if (value.integerValue != null) {
+ return int.parse(value.integerValue);
+ } else if (value.stringValue != null) {
+ return value.stringValue;
+ } else if (value.booleanValue != null) {
+ return value.booleanValue;
+ } else if (value.arrayValue != null) {
+ return value.arrayValue.values.map(getValue).toList();
+ } else if (value.timestampValue != null) {
+ return DateTime.parse(value.timestampValue);
+ } else if (value.nullValue != null) {
+ return null;
+ }
+ throw Exception('unsupported value ${value.toJson()}');
+}
+
+/// Converts a map with normal Dart values to a map where the values are
+/// JSON representation of firestore API values. For example: `{'x': 3}` is
+/// translated to `{'x': {'integerValue': 3}}`.
+Map<String, Object> taggedJsonMap(Map<String, dynamic> fields) {
+ return fields.map((key, value) => MapEntry(key, taggedValue(value).toJson()));
+}
+
+Map<String, dynamic> untagMap(Map<String, Value> map) {
+ return map.map((key, value) => MapEntry(key, getValue(value)));
+}
+
+List<CollectionSelector> inCollection(String name) {
+ return [CollectionSelector()..collectionId = name];
+}
+
+FieldReference field(String name) {
+ return FieldReference()..fieldPath = name;
+}
+
+Order orderBy(String fieldName, bool ascending) {
+ return Order()
+ ..field = field(fieldName)
+ ..direction = ascending ? 'ASCENDING' : 'DESCENDING';
+}
+
+Filter fieldEquals(String fieldName, dynamic value) {
+ return Filter()
+ ..fieldFilter = (FieldFilter()
+ ..field = field(fieldName)
+ ..op = 'EQUAL'
+ ..value = taggedValue(value));
+}
+
+Filter fieldLessThanOrEqual(String fieldName, dynamic value) {
+ return Filter()
+ ..fieldFilter = (FieldFilter()
+ ..field = field(fieldName)
+ ..op = 'LESS_THAN_OR_EQUAL'
+ ..value = taggedValue(value));
+}
+
+Filter fieldGreaterThanOrEqual(String fieldName, dynamic value) {
+ return Filter()
+ ..fieldFilter = (FieldFilter()
+ ..field = field(fieldName)
+ ..op = 'GREATER_THAN_OR_EQUAL'
+ ..value = taggedValue(value));
+}
+
+Filter arrayContains(String fieldName, dynamic value) {
+ return Filter()
+ ..fieldFilter = (FieldFilter()
+ ..field = field(fieldName)
+ ..op = 'ARRAY_CONTAINS'
+ ..value = taggedValue(value));
+}
+
+Filter compositeFilter(List<Filter> filters) {
+ return Filter()
+ ..compositeFilter = (CompositeFilter()
+ ..filters = filters
+ ..op = 'AND');
+}
+
+class DataWrapper {
+ final Map<String, Value> fields;
+ DataWrapper(Document document) : fields = document.fields;
+ DataWrapper.fields(this.fields);
+ int getInt(String name) {
+ final value = fields[name]?.integerValue;
+ if (value == null) {
+ return null;
+ }
+ return int.parse(value);
+ }
+
+ String getString(String name) {
+ return fields[name]?.stringValue;
+ }
+
+ bool getBool(String name) {
+ return fields[name]?.booleanValue;
+ }
+
+ List<dynamic> getList(String name) {
+ return fields[name]?.arrayValue?.values?.map(getValue)?.toList();
+ }
+
+ bool isNull(String name) {
+ return !fields.containsKey(name) ||
+ fields['name'].nullValue == 'NULL_VALUE';
+ }
+}
diff --git a/builder/lib/src/result.dart b/builder/lib/src/result.dart
index b1271e9..3344f69 100644
--- a/builder/lib/src/result.dart
+++ b/builder/lib/src/result.dart
@@ -5,10 +5,35 @@
// Field names and helper functions for result documents and
// commit documents from Firestore.
-// Field names of Result document fields
+import 'dart:convert' show jsonEncode;
import 'package:googleapis/firestore/v1.dart' show Value;
+class ResultRecord {
+ final Map<String, Value> fields;
+
+ ResultRecord(this.fields);
+
+ bool get approved => fields['approved'].booleanValue;
+
+ @override
+ String toString() => jsonEncode(fields);
+
+ int get blamelistEndIndex {
+ return int.parse(fields['blamelist_end_index'].integerValue);
+ }
+
+ bool containsActiveConfiguration(String configuration) {
+ for (final value in fields['active_configurations'].arrayValue.values) {
+ if (value.stringValue != null && value.stringValue == configuration) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+// Field names of Result document fields
const fName = 'name';
const fResult = 'result';
const fPreviousResult = 'previous_result';
diff --git a/builder/test/commits_cache_test.dart b/builder/test/commits_cache_test.dart
new file mode 100644
index 0000000..1d4e5e3
--- /dev/null
+++ b/builder/test/commits_cache_test.dart
@@ -0,0 +1,107 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:googleapis/firestore/v1.dart';
+import 'package:googleapis_auth/auth_io.dart';
+import 'package:http/http.dart' as http;
+import 'package:test/test.dart';
+
+import '../lib/src/firestore.dart' as fs;
+import '../lib/src/commits_cache.dart';
+
+// These tests read and write data from the Firestore database.
+// If they are run against the production database, they will not
+// write data to the database.
+
+void main() async {
+ final baseClient = http.Client();
+ final client = await clientViaApplicationDefaultCredentials(
+ 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);
+ test('Test fetch first commit', () async {
+ Future<void> fetchAndTestCommit(Map<String, dynamic> commit) async {
+ final fetched = await commits.getCommit(commit['hash']);
+ final copied = Map.from(fetched.toJson())
+ ..remove('author')
+ ..['hash'] = commit['hash'];
+ expect(copied, commit);
+ }
+
+ Future<void> fetchAndTestCommitByIndex(Map<String, dynamic> commit) async {
+ final fetched = await commits.getCommitByIndex(commit['index']);
+ final copied = Map.from(fetched.toJson())
+ ..remove('author')
+ ..['hash'] = commit['hash'];
+ expect(copied, commit);
+ }
+
+ expect(commits.startIndex, isNull);
+ await fetchAndTestCommit(commit68900);
+ expect(commits.startIndex, 68900);
+ expect(commits.endIndex, 68900);
+ await fetchAndTestCommit(commit68900);
+ expect(commits.startIndex, 68900);
+ expect(commits.endIndex, 68900);
+ await fetchAndTestCommitByIndex(commit68910);
+ expect(commits.startIndex, 68900);
+ expect(commits.endIndex, 68910);
+ await fetchAndTestCommitByIndex(commit68905);
+ expect(commits.startIndex, 68900);
+ expect(commits.endIndex, 68910);
+ await fetchAndTestCommit(commit68905);
+ expect(commits.startIndex, 68900);
+ expect(commits.endIndex, 68910);
+ await fetchAndTestCommitByIndex(commit68890);
+ expect(commits.startIndex, 68890);
+ expect(commits.endIndex, 68910);
+ await fetchAndTestCommitByIndex(commit68889);
+ expect(commits.startIndex, 68889);
+ expect(commits.endIndex, 68910);
+ });
+ tearDownAll(() => baseClient.close());
+}
+
+final commit68889 = <String, dynamic>{
+ 'review': 136974,
+ 'title': '[Cleanup] Removes deprecated --gc_at_instance_allocation.',
+ 'index': 68889,
+ 'created': DateTime.parse('2020-02-26 15:00:26.000Z'),
+ 'hash': '9c05fde96b62556944befd18ec834c56d6854fda'
+};
+
+final commit68890 = <String, dynamic>{
+ 'review': 136854,
+ 'title':
+ 'Add analyzer run support to steamroller and minor QOL improvements.',
+ 'index': 68890,
+ 'created': DateTime.parse('2020-02-26 16:57:46.000Z'),
+ 'hash': '31053a8c0180b663858aadce1ff6c0eefcf78623'
+};
+
+final commit68900 = <String, dynamic>{
+ 'review': 137322,
+ 'title': 'Remove unused SdkPatcher.',
+ 'index': 68900,
+ 'created': DateTime.parse('2020-02-26 20:20:31.000Z'),
+ 'hash': '118d220bfa7dc0f065b441e4edd584c2b9c0edc8',
+};
+
+final commit68905 = <String, dynamic>{
+ 'review': 137286,
+ 'title': '[dart2js] switch bot to use hostaserts once again',
+ 'index': 68905,
+ 'created': DateTime.parse('2020-02-26 21:41:47.000Z'),
+ 'hash': '5055c98beeacb3996c256e37148b4dc3561735ee'
+};
+
+final commit68910 = <String, dynamic>{
+ 'review': 137424,
+ 'title': 'corpus index updates',
+ 'index': 68910,
+ 'created': DateTime.parse('2020-02-26 23:19:11.000Z'),
+ 'hash': '8fb0e62babb213c98f4051f544fc80527bcecc18',
+};
diff --git a/builder/test/test_gerrit.dart b/builder/test/gerrit_test.dart
similarity index 100%
rename from builder/test/test_gerrit.dart
rename to builder/test/gerrit_test.dart
diff --git a/builder/test/test.dart b/builder/test/results_test.dart
similarity index 100%
rename from builder/test/test.dart
rename to builder/test/results_test.dart
diff --git a/builder/test/test_revert.dart b/builder/test/revert_test.dart
similarity index 100%
rename from builder/test/test_revert.dart
rename to builder/test/revert_test.dart