[metric_center] Migrate code to null safety (#439)

This commit is contained in:
Casey Hillers
2021-08-17 10:47:08 -07:00
committed by GitHub
parent e438ad5c32
commit 7b0b8e3381
12 changed files with 389 additions and 61 deletions

View File

@ -1,3 +1,7 @@
# 1.0.0
- Null safety support
# 0.1.1 # 0.1.1
- Update packages to null safe - Update packages to null safe

View File

@ -16,11 +16,11 @@ class MetricPoint extends Equatable {
/// Creates a new data point. /// Creates a new data point.
MetricPoint( MetricPoint(
this.value, this.value,
Map<String, String> tags, Map<String, String?> tags,
) : _tags = SplayTreeMap<String, String>.from(tags); ) : _tags = SplayTreeMap<String, String>.from(tags);
/// Can store integer values. /// Can store integer values.
final double value; final double? value;
/// Test name, unit, timestamp, configs, git revision, ..., in sorted order. /// Test name, unit, timestamp, configs, git revision, ..., in sorted order.
UnmodifiableMapView<String, String> get tags => UnmodifiableMapView<String, String> get tags =>
@ -43,7 +43,7 @@ class MetricPoint extends Equatable {
final SplayTreeMap<String, String> _tags; final SplayTreeMap<String, String> _tags;
@override @override
List<Object> get props => <Object>[value, tags]; List<Object?> get props => <Object?>[value, tags];
} }
/// Interface to write [MetricPoint]. /// Interface to write [MetricPoint].

View File

@ -82,7 +82,7 @@ class GcsLock {
await _api.objects.delete(_bucketName, lockFileName); await _api.objects.delete(_bucketName, lockFileName);
} }
StorageApi _api; late StorageApi _api;
final String _bucketName; final String _bucketName;
final AuthClient _client; final AuthClient _client;

View File

@ -50,21 +50,21 @@ void _parseAnItem(
}; };
for (final String subResult in item.keys) { for (final String subResult in item.keys) {
if (!_kNonNumericalValueSubResults.contains(subResult)) { if (!_kNonNumericalValueSubResults.contains(subResult)) {
num rawValue; num? rawValue;
try { try {
rawValue = item[subResult] as num; rawValue = item[subResult] as num?;
} catch (e) { } catch (e) {
print( print(
'$subResult: ${item[subResult]} (${item[subResult].runtimeType}) is not a number'); '$subResult: ${item[subResult]} (${item[subResult].runtimeType}) is not a number');
rethrow; rethrow;
} }
final double value = final double? value =
rawValue is int ? rawValue.toDouble() : rawValue as double; rawValue is int ? rawValue.toDouble() : rawValue as double?;
points.add( points.add(
MetricPoint( MetricPoint(
value, value,
<String, String>{kNameKey: name, kSubResultKey: subResult} <String, String?>{kNameKey: name, kSubResultKey: subResult}
..addAll(context) ..addAll(context)
..addAll( ..addAll(
subResult.endsWith('time') ? timeUnitMap : <String, String>{}), subResult.endsWith('time') ? timeUnitMap : <String, String>{}),

View File

@ -48,15 +48,15 @@ import 'gcs_lock.dart';
/// ``` /// ```
class SkiaPerfPoint extends MetricPoint { class SkiaPerfPoint extends MetricPoint {
SkiaPerfPoint._(this.githubRepo, this.gitHash, this.testName, this.subResult, SkiaPerfPoint._(this.githubRepo, this.gitHash, this.testName, this.subResult,
double value, this._options, this.jsonUrl) double? value, this._options, this.jsonUrl)
: assert(_options[kGithubRepoKey] == null), : assert(_options[kGithubRepoKey] == null),
assert(_options[kGitRevisionKey] == null), assert(_options[kGitRevisionKey] == null),
assert(_options[kNameKey] == null), assert(_options[kNameKey] == null),
super( super(
value, value,
<String, String>{} <String, String?>{}
..addAll(_options) ..addAll(_options)
..addAll(<String, String>{ ..addAll(<String, String?>{
kGithubRepoKey: githubRepo, kGithubRepoKey: githubRepo,
kGitRevisionKey: gitHash, kGitRevisionKey: gitHash,
kNameKey: testName, kNameKey: testName,
@ -78,9 +78,9 @@ class SkiaPerfPoint extends MetricPoint {
/// Skia perf will use the git revision's date instead of this date tag in /// Skia perf will use the git revision's date instead of this date tag in
/// the time axis. /// the time axis.
factory SkiaPerfPoint.fromPoint(MetricPoint p) { factory SkiaPerfPoint.fromPoint(MetricPoint p) {
final String githubRepo = p.tags[kGithubRepoKey]; final String? githubRepo = p.tags[kGithubRepoKey];
final String gitHash = p.tags[kGitRevisionKey]; final String? gitHash = p.tags[kGitRevisionKey];
final String name = p.tags[kNameKey]; final String? name = p.tags[kNameKey];
if (githubRepo == null || gitHash == null || name == null) { if (githubRepo == null || gitHash == null || name == null) {
throw '$kGithubRepoKey, $kGitRevisionKey, $kNameKey must be set in' throw '$kGithubRepoKey, $kGitRevisionKey, $kNameKey must be set in'
@ -113,7 +113,7 @@ class SkiaPerfPoint extends MetricPoint {
final String githubRepo; final String githubRepo;
/// SHA such as 'ad20d368ffa09559754e4b2b5c12951341ca3b2d' /// SHA such as 'ad20d368ffa09559754e4b2b5c12951341ca3b2d'
final String gitHash; final String? gitHash;
/// For Flutter devicelab, this is the task name (e.g., /// For Flutter devicelab, this is the task name (e.g.,
/// 'flutter_gallery__transition_perf'); for Google benchmark, this is the /// 'flutter_gallery__transition_perf'); for Google benchmark, this is the
@ -138,7 +138,7 @@ class SkiaPerfPoint extends MetricPoint {
/// The url to the Skia perf json file in the Google Cloud Storage bucket. /// The url to the Skia perf json file in the Google Cloud Storage bucket.
/// ///
/// This can be null if the point has been stored in the bucket yet. /// This can be null if the point has been stored in the bucket yet.
final String jsonUrl; final String? jsonUrl;
Map<String, dynamic> _toSubResultJson() { Map<String, dynamic> _toSubResultJson() {
return <String, dynamic>{ return <String, dynamic>{
@ -260,7 +260,7 @@ class SkiaPerfGcsAdaptor {
} }
Future<List<SkiaPerfPoint>> _readPointsWithoutRetry(String objectName) async { Future<List<SkiaPerfPoint>> _readPointsWithoutRetry(String objectName) async {
ObjectInfo info; ObjectInfo? info;
try { try {
info = await _gcsBucket.info(objectName); info = await _gcsBucket.info(objectName);
@ -282,10 +282,10 @@ class SkiaPerfGcsAdaptor {
final String firstGcsNameComponent = objectName.split('/')[0]; final String firstGcsNameComponent = objectName.split('/')[0];
_populateGcsNameToGithubRepoMapIfNeeded(); _populateGcsNameToGithubRepoMapIfNeeded();
final String githubRepo = _gcsNameToGithubRepo[firstGcsNameComponent]; final String githubRepo = _gcsNameToGithubRepo[firstGcsNameComponent]!;
assert(githubRepo != null); assert(githubRepo != null);
final String gitHash = decodedJson[kSkiaPerfGitHashKey] as String; final String? gitHash = decodedJson[kSkiaPerfGitHashKey] as String?;
final Map<String, dynamic> results = final Map<String, dynamic> results =
decodedJson[kSkiaPerfResultsKey] as Map<String, dynamic>; decodedJson[kSkiaPerfResultsKey] as Map<String, dynamic>;
for (final String name in results.keys) { for (final String name in results.keys) {
@ -298,7 +298,7 @@ class SkiaPerfGcsAdaptor {
gitHash, gitHash,
name, name,
subResult, subResult,
subResultMap[subResult] as double, subResultMap[subResult] as double?,
(subResultMap[kSkiaPerfOptionsKey] as Map<String, dynamic>) (subResultMap[kSkiaPerfOptionsKey] as Map<String, dynamic>)
.cast<String, String>(), .cast<String, String>(),
info.downloadLink.toString(), info.downloadLink.toString(),
@ -317,9 +317,9 @@ class SkiaPerfGcsAdaptor {
/// json files in the future to scale up the system if too many writes are /// json files in the future to scale up the system if too many writes are
/// competing for the same json file. /// competing for the same json file.
static Future<String> computeObjectName( static Future<String> computeObjectName(
String githubRepo, String revision, DateTime commitTime) async { String githubRepo, String? revision, DateTime commitTime) async {
assert(_githubRepoToGcsName[githubRepo] != null); assert(_githubRepoToGcsName[githubRepo] != null);
final String topComponent = _githubRepoToGcsName[githubRepo]; final String? topComponent = _githubRepoToGcsName[githubRepo];
// [commitTime] is not guranteed to be UTC. Ensure it is so all results // [commitTime] is not guranteed to be UTC. Ensure it is so all results
// pushed to GCS are the same timezone. // pushed to GCS are the same timezone.
final DateTime commitUtcTime = commitTime.toUtc(); final DateTime commitUtcTime = commitTime.toUtc();
@ -334,12 +334,12 @@ class SkiaPerfGcsAdaptor {
kFlutterFrameworkRepo: 'flutter-flutter', kFlutterFrameworkRepo: 'flutter-flutter',
kFlutterEngineRepo: 'flutter-engine', kFlutterEngineRepo: 'flutter-engine',
}; };
static final Map<String, String> _gcsNameToGithubRepo = <String, String>{}; static final Map<String?, String> _gcsNameToGithubRepo = <String?, String>{};
static void _populateGcsNameToGithubRepoMapIfNeeded() { static void _populateGcsNameToGithubRepoMapIfNeeded() {
if (_gcsNameToGithubRepo.isEmpty) { if (_gcsNameToGithubRepo.isEmpty) {
for (final String repo in _githubRepoToGcsName.keys) { for (final String repo in _githubRepoToGcsName.keys) {
final String gcsName = _githubRepoToGcsName[repo]; final String? gcsName = _githubRepoToGcsName[repo];
assert(_gcsNameToGithubRepo[gcsName] == null); assert(_gcsNameToGithubRepo[gcsName] == null);
_gcsNameToGithubRepo[gcsName] = repo; _gcsNameToGithubRepo[gcsName] = repo;
} }
@ -396,43 +396,43 @@ class SkiaPerfDestination extends MetricDestination {
// 1st, create a map based on git repo, git revision, and point id. Git repo // 1st, create a map based on git repo, git revision, and point id. Git repo
// and git revision are the top level components of the Skia perf GCS object // and git revision are the top level components of the Skia perf GCS object
// name. // name.
final Map<String, Map<String, Map<String, SkiaPerfPoint>>> pointMap = final Map<String, Map<String?, Map<String, SkiaPerfPoint>>> pointMap =
<String, Map<String, Map<String, SkiaPerfPoint>>>{}; <String, Map<String, Map<String, SkiaPerfPoint>>>{};
for (final SkiaPerfPoint p for (final SkiaPerfPoint p
in points.map((MetricPoint x) => SkiaPerfPoint.fromPoint(x))) { in points.map((MetricPoint x) => SkiaPerfPoint.fromPoint(x))) {
if (p != null) { if (p != null) {
pointMap[p.githubRepo] ??= <String, Map<String, SkiaPerfPoint>>{}; pointMap[p.githubRepo] ??= <String, Map<String, SkiaPerfPoint>>{};
pointMap[p.githubRepo][p.gitHash] ??= <String, SkiaPerfPoint>{}; pointMap[p.githubRepo]![p.gitHash] ??= <String, SkiaPerfPoint>{};
pointMap[p.githubRepo][p.gitHash][p.id] = p; pointMap[p.githubRepo]![p.gitHash]![p.id] = p;
} }
} }
// 2nd, read existing points from the gcs object and update with new ones. // 2nd, read existing points from the gcs object and update with new ones.
for (final String repo in pointMap.keys) { for (final String repo in pointMap.keys) {
for (final String revision in pointMap[repo].keys) { for (final String? revision in pointMap[repo]!.keys) {
final String objectName = await SkiaPerfGcsAdaptor.computeObjectName( final String objectName = await SkiaPerfGcsAdaptor.computeObjectName(
repo, revision, commitTime); repo, revision, commitTime);
final Map<String, SkiaPerfPoint> newPoints = pointMap[repo][revision]; final Map<String, SkiaPerfPoint>? newPoints = pointMap[repo]![revision];
// If too many bots are writing the metrics of a git revision into this // If too many bots are writing the metrics of a git revision into this
// single json file (with name `objectName`), the contention on the lock // single json file (with name `objectName`), the contention on the lock
// might be too high. In that case, break the json file into multiple // might be too high. In that case, break the json file into multiple
// json files according to bot names or task names. Skia perf read all // json files according to bot names or task names. Skia perf read all
// json files in the directory so one can use arbitrary names for those // json files in the directory so one can use arbitrary names for those
// sharded json file names. // sharded json file names.
_lock.protectedRun('$objectName.lock', () async { _lock!.protectedRun('$objectName.lock', () async {
final List<SkiaPerfPoint> oldPoints = final List<SkiaPerfPoint> oldPoints =
await _gcs.readPoints(objectName); await _gcs.readPoints(objectName);
for (final SkiaPerfPoint p in oldPoints) { for (final SkiaPerfPoint p in oldPoints) {
if (newPoints[p.id] == null) { if (newPoints![p.id] == null) {
newPoints[p.id] = p; newPoints[p.id] = p;
} }
} }
await _gcs.writePoints(objectName, newPoints.values.toList()); await _gcs.writePoints(objectName, newPoints!.values.toList());
}); });
} }
} }
} }
final SkiaPerfGcsAdaptor _gcs; final SkiaPerfGcsAdaptor _gcs;
final GcsLock _lock; late final GcsLock? _lock;
} }

View File

@ -1,23 +1,24 @@
name: metrics_center name: metrics_center
version: 0.1.1 version: 1.0.0
description: description:
Support multiple performance metrics sources/formats and destinations. Support multiple performance metrics sources/formats and destinations.
repository: https://github.com/flutter/packages/tree/master/packages/metrics_center repository: https://github.com/flutter/packages/tree/master/packages/metrics_center
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+metrics_center%22 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+metrics_center%22
environment: environment:
sdk: '>=2.10.0 <3.0.0' sdk: '>=2.12.0 <3.0.0'
dependencies: dependencies:
crypto: ^3.0.0 crypto: ^3.0.1
equatable: ^1.2.5 equatable: ^2.0.3
gcloud: ^0.8.0 gcloud: ^0.8.2
googleapis: ^3.0.0 googleapis: ^3.0.0
googleapis_auth: ^1.0.0 googleapis_auth: ^1.1.0
http: ^0.13.3 http: ^0.13.3
dev_dependencies: dev_dependencies:
build_runner: ^2.1.1
fake_async: ^1.2.0 fake_async: ^1.2.0
mockito: ^5.0.0 mockito: ^5.0.14
pedantic: ^1.10.0 pedantic: ^1.11.1
test: ^1.17.0 test: ^1.17.11

View File

@ -38,11 +38,11 @@ void main() {
expect(detailedPoint.tags[kUnitKey], equals('ns')); expect(detailedPoint.tags[kUnitKey], equals('ns'));
}); });
final Map<String, dynamic> credentialsJson = getTestGcpCredentialsJson(); final Map<String, dynamic>? credentialsJson = getTestGcpCredentialsJson();
test('FlutterDestination integration test with update.', () async { test('FlutterDestination integration test with update.', () async {
final FlutterDestination dst = final FlutterDestination dst =
await FlutterDestination.makeFromCredentialsJson(credentialsJson, await FlutterDestination.makeFromCredentialsJson(credentialsJson!,
isTesting: true); isTesting: true);
dst.update(<FlutterEngineMetricPoint>[simplePoint], dst.update(<FlutterEngineMetricPoint>[simplePoint],
DateTime.fromMillisecondsSinceEpoch(123)); DateTime.fromMillisecondsSinceEpoch(123));

View File

@ -10,9 +10,11 @@ import 'package:googleapis/storage/v1.dart';
import 'package:googleapis_auth/auth_io.dart'; import 'package:googleapis_auth/auth_io.dart';
import 'package:metrics_center/src/constants.dart'; import 'package:metrics_center/src/constants.dart';
import 'package:metrics_center/src/gcs_lock.dart'; import 'package:metrics_center/src/gcs_lock.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'common.dart'; import 'common.dart';
import 'gcs_lock_test.mocks.dart';
import 'utility.dart'; import 'utility.dart';
enum TestPhase { enum TestPhase {
@ -20,11 +22,10 @@ enum TestPhase {
run2, run2,
} }
class MockClient extends Mock implements AuthClient {} @GenerateMocks(<Type>[AuthClient])
void main() { void main() {
const Duration kDelayStep = Duration(milliseconds: 10); const Duration kDelayStep = Duration(milliseconds: 10);
final Map<String, dynamic> credentialsJson = getTestGcpCredentialsJson(); final Map<String, dynamic>? credentialsJson = getTestGcpCredentialsJson();
test('GcsLock prints warnings for long waits', () { test('GcsLock prints warnings for long waits', () {
// Capture print to verify error messages. // Capture print to verify error messages.
@ -34,7 +35,7 @@ void main() {
Zone.current.fork(specification: spec).run<void>(() { Zone.current.fork(specification: spec).run<void>(() {
fakeAsync((FakeAsync fakeAsync) { fakeAsync((FakeAsync fakeAsync) {
final MockClient mockClient = MockClient(); final MockAuthClient mockClient = MockAuthClient();
final GcsLock lock = GcsLock(mockClient, 'mockBucket'); final GcsLock lock = GcsLock(mockClient, 'mockBucket');
when(mockClient.send(any)).thenThrow(DetailedApiRequestError(412, '')); when(mockClient.send(any)).thenThrow(DetailedApiRequestError(412, ''));
final Future<void> runFinished = final Future<void> runFinished =

View File

@ -0,0 +1,117 @@
// Mocks generated by Mockito 5.0.14 from annotations
// in metrics_center/test/gcs_lock_test.dart.
// Do not manually edit this file.
import 'dart:async' as _i5;
import 'dart:convert' as _i6;
import 'dart:typed_data' as _i7;
import 'package:googleapis_auth/src/access_credentials.dart' as _i2;
import 'package:googleapis_auth/src/auth_client.dart' as _i4;
import 'package:http/http.dart' as _i3;
import 'package:mockito/mockito.dart' as _i1;
// ignore_for_file: always_specify_types
// ignore_for_file: avoid_redundant_argument_values
// ignore_for_file: avoid_setters_without_getters
// ignore_for_file: camel_case_types
// ignore_for_file: comment_references
// ignore_for_file: implementation_imports
// ignore_for_file: invalid_use_of_visible_for_testing_member
// ignore_for_file: prefer_const_constructors
// ignore_for_file: unnecessary_overrides
// ignore_for_file: unnecessary_parenthesis
class _FakeAccessCredentials_0 extends _i1.Fake
implements _i2.AccessCredentials {}
class _FakeResponse_1 extends _i1.Fake implements _i3.Response {}
class _FakeStreamedResponse_2 extends _i1.Fake implements _i3.StreamedResponse {
}
/// A class which mocks [AuthClient].
///
/// See the documentation for Mockito's code generation for more information.
class MockAuthClient extends _i1.Mock implements _i4.AuthClient {
MockAuthClient() {
_i1.throwOnMissingStub(this);
}
@override
_i2.AccessCredentials get credentials =>
(super.noSuchMethod(Invocation.getter(#credentials),
returnValue: _FakeAccessCredentials_0()) as _i2.AccessCredentials);
@override
String toString() => super.toString();
@override
_i5.Future<_i3.Response> head(Uri? url, {Map<String, String>? headers}) =>
(super.noSuchMethod(Invocation.method(#head, [url], {#headers: headers}),
returnValue: Future<_i3.Response>.value(_FakeResponse_1()))
as _i5.Future<_i3.Response>);
@override
_i5.Future<_i3.Response> get(Uri? url, {Map<String, String>? headers}) =>
(super.noSuchMethod(Invocation.method(#get, [url], {#headers: headers}),
returnValue: Future<_i3.Response>.value(_FakeResponse_1()))
as _i5.Future<_i3.Response>);
@override
_i5.Future<_i3.Response> post(Uri? url,
{Map<String, String>? headers,
Object? body,
_i6.Encoding? encoding}) =>
(super.noSuchMethod(
Invocation.method(#post, [url],
{#headers: headers, #body: body, #encoding: encoding}),
returnValue: Future<_i3.Response>.value(_FakeResponse_1()))
as _i5.Future<_i3.Response>);
@override
_i5.Future<_i3.Response> put(Uri? url,
{Map<String, String>? headers,
Object? body,
_i6.Encoding? encoding}) =>
(super.noSuchMethod(
Invocation.method(#put, [url],
{#headers: headers, #body: body, #encoding: encoding}),
returnValue: Future<_i3.Response>.value(_FakeResponse_1()))
as _i5.Future<_i3.Response>);
@override
_i5.Future<_i3.Response> patch(Uri? url,
{Map<String, String>? headers,
Object? body,
_i6.Encoding? encoding}) =>
(super.noSuchMethod(
Invocation.method(#patch, [url],
{#headers: headers, #body: body, #encoding: encoding}),
returnValue: Future<_i3.Response>.value(_FakeResponse_1()))
as _i5.Future<_i3.Response>);
@override
_i5.Future<_i3.Response> delete(Uri? url,
{Map<String, String>? headers,
Object? body,
_i6.Encoding? encoding}) =>
(super.noSuchMethod(
Invocation.method(#delete, [url],
{#headers: headers, #body: body, #encoding: encoding}),
returnValue: Future<_i3.Response>.value(_FakeResponse_1()))
as _i5.Future<_i3.Response>);
@override
_i5.Future<String> read(Uri? url, {Map<String, String>? headers}) =>
(super.noSuchMethod(Invocation.method(#read, [url], {#headers: headers}),
returnValue: Future<String>.value('')) as _i5.Future<String>);
@override
_i5.Future<_i7.Uint8List> readBytes(Uri? url,
{Map<String, String>? headers}) =>
(super.noSuchMethod(
Invocation.method(#readBytes, [url], {#headers: headers}),
returnValue: Future<_i7.Uint8List>.value(_i7.Uint8List(0)))
as _i5.Future<_i7.Uint8List>);
@override
_i5.Future<_i3.StreamedResponse> send(_i3.BaseRequest? request) =>
(super.noSuchMethod(Invocation.method(#send, [request]),
returnValue:
Future<_i3.StreamedResponse>.value(_FakeStreamedResponse_2()))
as _i5.Future<_i3.StreamedResponse>);
@override
void close() => super.noSuchMethod(Invocation.method(#close, []),
returnValueForMissingStub: null);
}

View File

@ -12,15 +12,13 @@ import 'package:googleapis_auth/auth_io.dart';
import 'package:metrics_center/metrics_center.dart'; import 'package:metrics_center/metrics_center.dart';
import 'package:metrics_center/src/constants.dart'; import 'package:metrics_center/src/constants.dart';
import 'package:metrics_center/src/gcs_lock.dart'; import 'package:metrics_center/src/gcs_lock.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'common.dart'; import 'common.dart';
import 'skiaperf_test.mocks.dart';
import 'utility.dart'; import 'utility.dart';
class MockBucket extends Mock implements Bucket {}
class MockObjectInfo extends Mock implements ObjectInfo {}
class MockGcsLock implements GcsLock { class MockGcsLock implements GcsLock {
@override @override
Future<void> protectedRun( Future<void> protectedRun(
@ -46,6 +44,7 @@ class MockSkiaPerfGcsAdaptor implements SkiaPerfGcsAdaptor {
<String, List<SkiaPerfPoint>>{}; <String, List<SkiaPerfPoint>>{};
} }
@GenerateMocks(<Type>[Bucket, ObjectInfo])
Future<void> main() async { Future<void> main() async {
const double kValue1 = 1.0; const double kValue1 = 1.0;
const double kValue2 = 2.0; const double kValue2 = 2.0;
@ -353,6 +352,8 @@ Future<void> main() async {
]; ];
final String skiaPerfJson = final String skiaPerfJson =
jsonEncode(SkiaPerfPoint.toSkiaPerfJson(writePoints)); jsonEncode(SkiaPerfPoint.toSkiaPerfJson(writePoints));
when(testBucket.writeBytes(testObjectName, utf8.encode(skiaPerfJson)))
.thenAnswer((_) async => FakeObjectInfo());
await skiaPerfGcs.writePoints(testObjectName, writePoints); await skiaPerfGcs.writePoints(testObjectName, writePoints);
verify(testBucket.writeBytes(testObjectName, utf8.encode(skiaPerfJson))); verify(testBucket.writeBytes(testObjectName, utf8.encode(skiaPerfJson)));
@ -392,9 +393,9 @@ Future<void> main() async {
}); });
// The following is for integration tests. // The following is for integration tests.
Bucket testBucket; Bucket? testBucket;
GcsLock testLock; GcsLock? testLock;
final Map<String, dynamic> credentialsJson = getTestGcpCredentialsJson(); final Map<String, dynamic>? credentialsJson = getTestGcpCredentialsJson();
if (credentialsJson != null) { if (credentialsJson != null) {
final ServiceAccountCredentials credentials = final ServiceAccountCredentials credentials =
ServiceAccountCredentials.fromJson(credentialsJson); ServiceAccountCredentials.fromJson(credentialsJson);
@ -412,7 +413,7 @@ Future<void> main() async {
} }
Future<void> skiaPerfGcsAdapterIntegrationTest() async { Future<void> skiaPerfGcsAdapterIntegrationTest() async {
final SkiaPerfGcsAdaptor skiaPerfGcs = SkiaPerfGcsAdaptor(testBucket); final SkiaPerfGcsAdaptor skiaPerfGcs = SkiaPerfGcsAdaptor(testBucket!);
final String testObjectName = await SkiaPerfGcsAdaptor.computeObjectName( final String testObjectName = await SkiaPerfGcsAdaptor.computeObjectName(
kFlutterFrameworkRepo, kFlutterFrameworkRepo,
@ -443,7 +444,7 @@ Future<void> main() async {
} }
Future<void> skiaPerfGcsIntegrationTestWithEnginePoints() async { Future<void> skiaPerfGcsIntegrationTestWithEnginePoints() async {
final SkiaPerfGcsAdaptor skiaPerfGcs = SkiaPerfGcsAdaptor(testBucket); final SkiaPerfGcsAdaptor skiaPerfGcs = SkiaPerfGcsAdaptor(testBucket!);
final String testObjectName = await SkiaPerfGcsAdaptor.computeObjectName( final String testObjectName = await SkiaPerfGcsAdaptor.computeObjectName(
kFlutterEngineRepo, kFlutterEngineRepo,
@ -569,7 +570,7 @@ Future<void> main() async {
Future<void> skiaPerfDestinationIntegrationTest() async { Future<void> skiaPerfDestinationIntegrationTest() async {
final SkiaPerfDestination destination = final SkiaPerfDestination destination =
SkiaPerfDestination(SkiaPerfGcsAdaptor(testBucket), testLock); SkiaPerfDestination(SkiaPerfGcsAdaptor(testBucket!), testLock);
await destination.update(<MetricPoint>[cocoonPointRev1Metric1], await destination.update(<MetricPoint>[cocoonPointRev1Metric1],
DateTime.fromMillisecondsSinceEpoch(123)); DateTime.fromMillisecondsSinceEpoch(123));
} }
@ -580,3 +581,32 @@ Future<void> main() async {
skip: testBucket == null, skip: testBucket == null,
); );
} }
class FakeObjectInfo extends ObjectInfo {
@override
int get crc32CChecksum => throw UnimplementedError();
@override
Uri get downloadLink => throw UnimplementedError();
@override
String get etag => throw UnimplementedError();
@override
ObjectGeneration get generation => throw UnimplementedError();
@override
int get length => throw UnimplementedError();
@override
List<int> get md5Hash => throw UnimplementedError();
@override
ObjectMetadata get metadata => throw UnimplementedError();
@override
String get name => throw UnimplementedError();
@override
DateTime get updated => throw UnimplementedError();
}

View File

@ -0,0 +1,175 @@
// Mocks generated by Mockito 5.0.14 from annotations
// in metrics_center/test/skiaperf_test.dart.
// Do not manually edit this file.
import 'dart:async' as _i2;
import 'package:gcloud/common.dart' as _i4;
import 'package:gcloud/storage.dart' as _i3;
import 'package:mockito/mockito.dart' as _i1;
// ignore_for_file: always_specify_types
// ignore_for_file: avoid_redundant_argument_values
// ignore_for_file: avoid_setters_without_getters
// ignore_for_file: camel_case_types
// ignore_for_file: comment_references
// ignore_for_file: implementation_imports
// ignore_for_file: invalid_use_of_visible_for_testing_member
// ignore_for_file: prefer_const_constructors
// ignore_for_file: unnecessary_overrides
// ignore_for_file: unnecessary_parenthesis
class _FakeStreamSink_0<S> extends _i1.Fake implements _i2.StreamSink<S> {}
class _FakeObjectInfo_1 extends _i1.Fake implements _i3.ObjectInfo {}
class _FakePage_2<T> extends _i1.Fake implements _i4.Page<T> {}
class _FakeDateTime_3 extends _i1.Fake implements DateTime {}
class _FakeUri_4 extends _i1.Fake implements Uri {}
class _FakeObjectGeneration_5 extends _i1.Fake implements _i3.ObjectGeneration {
}
class _FakeObjectMetadata_6 extends _i1.Fake implements _i3.ObjectMetadata {}
/// A class which mocks [Bucket].
///
/// See the documentation for Mockito's code generation for more information.
class MockBucket extends _i1.Mock implements _i3.Bucket {
MockBucket() {
_i1.throwOnMissingStub(this);
}
@override
String get bucketName =>
(super.noSuchMethod(Invocation.getter(#bucketName), returnValue: '')
as String);
@override
String absoluteObjectName(String? objectName) =>
(super.noSuchMethod(Invocation.method(#absoluteObjectName, [objectName]),
returnValue: '') as String);
@override
_i2.StreamSink<List<int>> write(String? objectName,
{int? length,
_i3.ObjectMetadata? metadata,
_i3.Acl? acl,
_i3.PredefinedAcl? predefinedAcl,
String? contentType}) =>
(super.noSuchMethod(
Invocation.method(#write, [
objectName
], {
#length: length,
#metadata: metadata,
#acl: acl,
#predefinedAcl: predefinedAcl,
#contentType: contentType
}),
returnValue: _FakeStreamSink_0<List<int>>())
as _i2.StreamSink<List<int>>);
@override
_i2.Future<_i3.ObjectInfo> writeBytes(String? name, List<int>? bytes,
{_i3.ObjectMetadata? metadata,
_i3.Acl? acl,
_i3.PredefinedAcl? predefinedAcl,
String? contentType}) =>
(super.noSuchMethod(
Invocation.method(#writeBytes, [
name,
bytes
], {
#metadata: metadata,
#acl: acl,
#predefinedAcl: predefinedAcl,
#contentType: contentType
}),
returnValue: Future<_i3.ObjectInfo>.value(_FakeObjectInfo_1()))
as _i2.Future<_i3.ObjectInfo>);
@override
_i2.Stream<List<int>> read(String? objectName, {int? offset, int? length}) =>
(super.noSuchMethod(
Invocation.method(
#read, [objectName], {#offset: offset, #length: length}),
returnValue: Stream<List<int>>.empty()) as _i2.Stream<List<int>>);
@override
_i2.Future<_i3.ObjectInfo> info(String? name) =>
(super.noSuchMethod(Invocation.method(#info, [name]),
returnValue: Future<_i3.ObjectInfo>.value(_FakeObjectInfo_1()))
as _i2.Future<_i3.ObjectInfo>);
@override
_i2.Future<dynamic> delete(String? name) =>
(super.noSuchMethod(Invocation.method(#delete, [name]),
returnValue: Future<dynamic>.value()) as _i2.Future<dynamic>);
@override
_i2.Future<dynamic> updateMetadata(
String? objectName, _i3.ObjectMetadata? metadata) =>
(super.noSuchMethod(
Invocation.method(#updateMetadata, [objectName, metadata]),
returnValue: Future<dynamic>.value()) as _i2.Future<dynamic>);
@override
_i2.Stream<_i3.BucketEntry> list({String? prefix, String? delimiter}) =>
(super.noSuchMethod(
Invocation.method(
#list, [], {#prefix: prefix, #delimiter: delimiter}),
returnValue: Stream<_i3.BucketEntry>.empty())
as _i2.Stream<_i3.BucketEntry>);
@override
_i2.Future<_i4.Page<_i3.BucketEntry>> page(
{String? prefix, String? delimiter, int? pageSize = 50}) =>
(super.noSuchMethod(
Invocation.method(#page, [], {
#prefix: prefix,
#delimiter: delimiter,
#pageSize: pageSize
}),
returnValue: Future<_i4.Page<_i3.BucketEntry>>.value(
_FakePage_2<_i3.BucketEntry>()))
as _i2.Future<_i4.Page<_i3.BucketEntry>>);
@override
String toString() => super.toString();
}
/// A class which mocks [ObjectInfo].
///
/// See the documentation for Mockito's code generation for more information.
class MockObjectInfo extends _i1.Mock implements _i3.ObjectInfo {
MockObjectInfo() {
_i1.throwOnMissingStub(this);
}
@override
String get name =>
(super.noSuchMethod(Invocation.getter(#name), returnValue: '') as String);
@override
int get length =>
(super.noSuchMethod(Invocation.getter(#length), returnValue: 0) as int);
@override
DateTime get updated => (super.noSuchMethod(Invocation.getter(#updated),
returnValue: _FakeDateTime_3()) as DateTime);
@override
String get etag =>
(super.noSuchMethod(Invocation.getter(#etag), returnValue: '') as String);
@override
List<int> get md5Hash =>
(super.noSuchMethod(Invocation.getter(#md5Hash), returnValue: <int>[])
as List<int>);
@override
int get crc32CChecksum =>
(super.noSuchMethod(Invocation.getter(#crc32CChecksum), returnValue: 0)
as int);
@override
Uri get downloadLink => (super.noSuchMethod(Invocation.getter(#downloadLink),
returnValue: _FakeUri_4()) as Uri);
@override
_i3.ObjectGeneration get generation =>
(super.noSuchMethod(Invocation.getter(#generation),
returnValue: _FakeObjectGeneration_5()) as _i3.ObjectGeneration);
@override
_i3.ObjectMetadata get metadata =>
(super.noSuchMethod(Invocation.getter(#metadata),
returnValue: _FakeObjectMetadata_6()) as _i3.ObjectMetadata);
@override
String toString() => super.toString();
}

View File

@ -13,11 +13,11 @@ void expectSetMatch<T>(Iterable<T> actual, Iterable<T> expected) {
} }
// May return null if the credentials file doesn't exist. // May return null if the credentials file doesn't exist.
Map<String, dynamic> getTestGcpCredentialsJson() { Map<String, dynamic>? getTestGcpCredentialsJson() {
final File f = File('secret/test_gcp_credentials.json'); final File f = File('secret/test_gcp_credentials.json');
if (!f.existsSync()) { if (!f.existsSync()) {
return null; return null;
} }
return jsonDecode(File('secret/test_gcp_credentials.json').readAsStringSync()) return jsonDecode(File('secret/test_gcp_credentials.json').readAsStringSync())
as Map<String, dynamic>; as Map<String, dynamic>?;
} }