Unverified Commit 0d046b34 authored by Dan Field's avatar Dan Field Committed by GitHub

Drop metrics_center (#74535)

parent 120966f9
Copyright 2014 The Flutter Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
Metrics center is a minimal set of code and services to support multiple perf
metrics generators (e.g., Cocoon device lab, Cirrus bots, LUCI bots, Firebase
Test Lab) and destinations (e.g., old Cocoon perf dashboard, Skia perf
dashboard). The work and maintenance it requires is very close to that of just
supporting a single generator and destination (e.g., engine bots to Skia perf),
and the small amount of extra work is designed to make it easy to support more
generators and destinations in the future.
This is currently under migration. More documentations will be added once the
migration is done.
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
export 'src/common.dart';
export 'src/flutter.dart';
export 'src/google_benchmark.dart';
export 'src/skiaperf.dart';
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:collection';
import 'dart:convert';
import 'package:crypto/crypto.dart';
import 'package:equatable/equatable.dart';
import 'package:googleapis_auth/auth.dart';
import 'package:googleapis_auth/auth_io.dart';
import 'package:http/http.dart';
/// Common format of a metric data point.
class MetricPoint extends Equatable {
MetricPoint(
this.value,
Map<String, String> tags,
) : _tags = SplayTreeMap<String, String>.from(tags);
/// Can store integer values.
final double value;
/// Test name, unit, timestamp, configs, git revision, ..., in sorted order.
UnmodifiableMapView<String, String> get tags =>
UnmodifiableMapView<String, String>(_tags);
/// Unique identifier for updating existing data point.
///
/// We shouldn't have to worry about hash collisions until we have about
/// 2^128 points.
///
/// This id should stay constant even if the [tags.keys] are reordered.
/// (Because we are using an ordered SplayTreeMap to generate the id.)
String get id => sha256.convert(utf8.encode('$_tags')).toString();
@override
String toString() {
return 'MetricPoint(value=$value, tags=$_tags)';
}
final SplayTreeMap<String, String> _tags;
@override
List<Object> get props => <Object>[value, tags];
}
/// Interface to write [MetricPoint].
abstract class MetricDestination {
/// Insert new data points or modify old ones with matching id.
Future<void> update(List<MetricPoint> points);
}
/// Create `AuthClient` in case we only have an access token without the full
/// credentials json. It's currently the case for Chrmoium LUCI bots.
AuthClient authClientFromAccessToken(String token, List<String> scopes) {
final DateTime anHourLater = DateTime.now().add(const Duration(hours: 1));
final AccessToken accessToken =
AccessToken('Bearer', token, anHourLater.toUtc());
final AccessCredentials accessCredentials =
AccessCredentials(accessToken, null, scopes);
return authenticatedClient(Client(), accessCredentials);
}
/// Some common tag keys
const String kGithubRepoKey = 'gitRepo';
const String kGitRevisionKey = 'gitRevision';
const String kUnitKey = 'unit';
const String kNameKey = 'name';
const String kSubResultKey = 'subResult';
/// Known github repo
const String kFlutterFrameworkRepo = 'flutter/flutter';
const String kFlutterEngineRepo = 'flutter/engine';
/// The key for the GCP project id in the credentials json.
const String kProjectId = 'project_id';
// Copyright 2014 The Flutter Authors. 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:metrics_center/src/common.dart';
import 'package:metrics_center/src/legacy_datastore.dart';
import 'package:metrics_center/src/legacy_flutter.dart';
/// Convenient class to capture the benchmarks in the Flutter engine repo.
class FlutterEngineMetricPoint extends MetricPoint {
FlutterEngineMetricPoint(
String name,
double value,
String gitRevision, {
Map<String, String> moreTags = const <String, String>{},
}) : super(
value,
<String, String>{
kNameKey: name,
kGithubRepoKey: kFlutterEngineRepo,
kGitRevisionKey: gitRevision,
}..addAll(moreTags),
);
}
/// All Flutter performance metrics (framework, engine, ...) should be written
/// to this destination.
class FlutterDestination extends MetricDestination {
// TODO(liyuqian): change the implementation of this class (without changing
// its public APIs) to remove `LegacyFlutterDestination` and directly use
// `SkiaPerfDestination` once the migration is fully done.
FlutterDestination._(this._legacyDestination);
static Future<FlutterDestination> makeFromCredentialsJson(
Map<String, dynamic> json) async {
final LegacyFlutterDestination legacyDestination =
LegacyFlutterDestination(await datastoreFromCredentialsJson(json));
return FlutterDestination._(legacyDestination);
}
static FlutterDestination makeFromAccessToken(
String accessToken, String projectId) {
final LegacyFlutterDestination legacyDestination = LegacyFlutterDestination(
datastoreFromAccessToken(accessToken, projectId));
return FlutterDestination._(legacyDestination);
}
@override
Future<void> update(List<MetricPoint> points) async {
await _legacyDestination.update(points);
}
final LegacyFlutterDestination _legacyDestination;
}
// Copyright 2014 The Flutter Authors. 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/storage/v1.dart';
import 'package:googleapis_auth/auth.dart';
/// Global (in terms of earth) mutex using Google Cloud Storage.
class GcsLock {
/// Create a lock with an authenticated client and a GCS bucket name.
///
/// The client is used to communicate with Google Cloud Storage APIs.
GcsLock(this._client, this._bucketName)
: assert(_client != null),
assert(_bucketName != null) {
_api = StorageApi(_client);
}
/// Create a temporary lock file in GCS, and use it as a mutex mechanism to
/// run a piece of code exclusively.
///
/// There must be no existing lock file with the same name in order to
/// proceed. If multiple [GcsLock]s with the same `bucketName` and
/// `lockFileName` try [protectedRun] simultaneously, only one will proceed
/// and create the lock file. All others will be blocked.
///
/// When [protectedRun] finishes, the lock file is deleted, and other blocked
/// [protectedRun] may proceed.
///
/// If the lock file is stuck (e.g., `_unlock` is interrupted unexpectedly),
/// one may need to manually delete the lock file from GCS to unblock any
/// [protectedRun] that may depend on it.
Future<void> protectedRun(String lockFileName, Future<void> f()) async {
await _lock(lockFileName);
try {
await f();
} catch (e, stacktrace) {
print(stacktrace);
rethrow;
} finally {
await _unlock(lockFileName);
}
}
Future<void> _lock(String lockFileName) async {
final Object object = Object();
object.bucket = _bucketName;
object.name = lockFileName;
final Media content = Media(const Stream<List<int>>.empty(), 0);
Duration waitPeriod = const Duration(milliseconds: 10);
bool locked = false;
while (!locked) {
try {
await _api.objects.insert(object, _bucketName,
ifGenerationMatch: '0', uploadMedia: content);
locked = true;
} on DetailedApiRequestError catch (e) {
if (e.status == 412) {
// Status 412 means that the lock file already exists. Wait until
// that lock file is deleted.
await Future<void>.delayed(waitPeriod);
waitPeriod *= 2;
if (waitPeriod >= _kWarningThreshold) {
print(
'The lock is waiting for a long time: $waitPeriod. '
'If the lock file $lockFileName in bucket $_bucketName '
'seems to be stuck (i.e., it was created a long time ago and '
'no one seems to be owning it currently), delete it manually '
'to unblock this.',
);
}
} else {
rethrow;
}
}
}
}
Future<void> _unlock(String lockFileName) async {
await _api.objects.delete(_bucketName, lockFileName);
}
StorageApi _api;
final String _bucketName;
final AuthClient _client;
static const Duration _kWarningThreshold = Duration(seconds: 10);
}
// Copyright 2014 The Flutter Authors. 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:github/github.dart';
/// Singleton class to query some Github info with an in-memory cache.
class GithubHelper {
/// Return the singleton helper.
factory GithubHelper() {
return _singleton;
}
GithubHelper._internal();
/// The result is cached in memory so querying the same thing again in the
/// same process is fast.
///
/// Our unit test requires that calling this method 1000 times for the same
/// `githubRepo` and `sha` should be done in 1 second.
Future<DateTime> getCommitDateTime(String githubRepo, String sha) async {
final String key = '$githubRepo/commit/$sha';
if (_commitDateTimeCache[key] == null) {
final RepositoryCommit commit = await _github.repositories
.getCommit(RepositorySlug.full(githubRepo), sha);
_commitDateTimeCache[key] = commit.commit.committer.date;
}
return _commitDateTimeCache[key];
}
static final GithubHelper _singleton = GithubHelper._internal();
final GitHub _github = GitHub(auth: findAuthenticationFromEnvironment());
final Map<String, DateTime> _commitDateTimeCache = <String, DateTime>{};
}
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:convert';
import 'dart:io';
import 'package:metrics_center/src/common.dart';
const String _kTimeUnitKey = 'time_unit';
const List<String> _kNonNumericalValueSubResults = <String>[
kNameKey,
_kTimeUnitKey,
'iterations',
'big_o',
];
/// Parse the json result of https://github.com/google/benchmark.
class GoogleBenchmarkParser {
/// Given a Google benchmark json output, parse its content into a list of [MetricPoint].
static Future<List<MetricPoint>> parse(String jsonFileName) async {
final Map<String, dynamic> jsonResult =
jsonDecode(File(jsonFileName).readAsStringSync())
as Map<String, dynamic>;
final Map<String, dynamic> rawContext = jsonResult['context'] as Map<String, dynamic>;
final Map<String, String> context = rawContext.map<String, String>(
(String k, dynamic v) => MapEntry<String, String>(k, v.toString()),
);
final List<MetricPoint> points = <MetricPoint>[];
for (final dynamic item in jsonResult['benchmarks']) {
_parseAnItem(item as Map<String, dynamic>, points, context);
}
return points;
}
}
void _parseAnItem(
Map<String, dynamic> item,
List<MetricPoint> points,
Map<String, String> context,
) {
final String name = item[kNameKey] as String;
final Map<String, String> timeUnitMap = <String, String>{
kUnitKey: item[_kTimeUnitKey] as String
};
for (final String subResult in item.keys) {
if (!_kNonNumericalValueSubResults.contains(subResult)) {
num rawValue;
try {
rawValue = item[subResult] as num;
} catch (e) {
print('$subResult: ${item[subResult]} (${item[subResult].runtimeType}) is not a number');
rethrow;
}
final double value = rawValue is int ? rawValue.toDouble() : rawValue as double;
points.add(
MetricPoint(
value,
<String, String>{kNameKey: name, kSubResultKey: subResult}
..addAll(context)
..addAll(subResult.endsWith('time') ? timeUnitMap : <String, String>{}),
),
);
}
}
}
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// TODO(liyuqian): Remove this file once the migration is fully done and we no
// longer need to fall back to the datastore.
import 'package:gcloud/db.dart';
import 'package:googleapis_auth/auth.dart';
import 'package:googleapis_auth/auth_io.dart';
// The official pub.dev/packages/gcloud documentation uses datastore_impl
// so we have to ignore implementation_imports here.
// ignore: implementation_imports
import 'package:gcloud/src/datastore_impl.dart';
import 'common.dart';
Future<DatastoreDB> datastoreFromCredentialsJson(
Map<String, dynamic> json) async {
final AutoRefreshingAuthClient client = await clientViaServiceAccount(
ServiceAccountCredentials.fromJson(json), DatastoreImpl.SCOPES);
return DatastoreDB(DatastoreImpl(client, json[kProjectId] as String));
}
DatastoreDB datastoreFromAccessToken(String token, String projectId) {
final AuthClient client =
authClientFromAccessToken(token, DatastoreImpl.SCOPES);
return DatastoreDB(DatastoreImpl(client, projectId));
}
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// TODO(liyuqian): Remove this legacy file once the migration is fully done.
// See go/flutter-metrics-center-migration for detailed plans.
import 'dart:convert';
import 'dart:math';
import 'package:gcloud/db.dart';
import 'common.dart';
import 'legacy_datastore.dart';
const String kSourceTimeMicrosName = 'sourceTimeMicros';
// The size of 500 is currently limited by Google datastore. It cannot write
// more than 500 entities in a single call.
const int kMaxBatchSize = 500;
/// This model corresponds to the existing data model 'MetricPoint' used in the
/// flutter-cirrus GCP project.
///
/// The originId and sourceTimeMicros fields are no longer used but we are still
/// providing valid values to them so it's compatible with old code and services
/// during the migration.
@Kind(name: 'MetricPoint', idType: IdType.String)
class LegacyMetricPointModel extends Model<String> {
LegacyMetricPointModel({MetricPoint fromMetricPoint}) {
if (fromMetricPoint != null) {
id = fromMetricPoint.id;
value = fromMetricPoint.value;
originId = 'legacy-flutter';
sourceTimeMicros = null;
tags = fromMetricPoint.tags.keys
.map((String key) =>
jsonEncode(<String, dynamic>{key: fromMetricPoint.tags[key]}))
.toList();
}
}
@DoubleProperty(required: true, indexed: false)
double value;
@StringListProperty()
List<String> tags;
@StringProperty(required: true)
String originId;
@IntProperty(propertyName: kSourceTimeMicrosName)
int sourceTimeMicros;
}
class LegacyFlutterDestination extends MetricDestination {
LegacyFlutterDestination(this._db);
static Future<LegacyFlutterDestination> makeFromCredentialsJson(
Map<String, dynamic> json) async {
return LegacyFlutterDestination(await datastoreFromCredentialsJson(json));
}
static LegacyFlutterDestination makeFromAccessToken(
String accessToken, String projectId) {
return LegacyFlutterDestination(
datastoreFromAccessToken(accessToken, projectId));
}
@override
Future<void> update(List<MetricPoint> points) async {
final List<LegacyMetricPointModel> flutterCenterPoints =
points.map((MetricPoint p) => LegacyMetricPointModel(fromMetricPoint: p)).toList();
for (int start = 0; start < points.length; start += kMaxBatchSize) {
final int end = min(start + kMaxBatchSize, points.length);
await _db.withTransaction((Transaction tx) async {
tx.queueMutations(inserts: flutterCenterPoints.sublist(start, end));
await tx.commit();
});
}
}
final DatastoreDB _db;
}
This diff is collapsed.
name: metrics_center
version: 0.0.4
description:
Support multiple performance metrics sources/formats and destinations.
homepage:
https://github.com/flutter/flutter/tree/master/dev/benchmarks/metrics_center
environment:
sdk: '>=2.10.0 <3.0.0'
dependencies:
args: 1.6.0
crypto: 2.1.5
gcloud: 0.7.3
googleapis_auth: 0.2.12
github: 7.0.4
equatable: 1.2.5
_discoveryapis_commons: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
charcode: 1.2.0-nullsafety.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
collection: 1.15.0-nullsafety.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
convert: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
googleapis: 0.56.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
http: 0.12.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
http_parser: 3.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
json_annotation: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
meta: 1.3.0-nullsafety.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
path: 1.8.0-nullsafety.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
source_span: 1.8.0-nullsafety.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
string_scanner: 1.1.0-nullsafety.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
term_glyph: 1.2.0-nullsafety.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
typed_data: 1.3.0-nullsafety.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
dev_dependencies:
test: 1.16.0-nullsafety.9
pedantic: 1.10.0-nullsafety.3
mockito: 4.1.1
fake_async: 1.2.0-nullsafety.3
_fe_analyzer_shared: 12.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
analyzer: 0.40.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
async: 2.5.0-nullsafety.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
boolean_selector: 2.1.0-nullsafety.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
cli_util: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
clock: 1.1.0-nullsafety.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
coverage: 0.14.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
glob: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
http_multi_server: 2.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
io: 0.3.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
js: 0.6.3-nullsafety.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
logging: 0.11.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
matcher: 0.12.10-nullsafety.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
mime: 0.9.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
node_interop: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
node_io: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
node_preamble: 1.4.12 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
package_config: 1.9.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
pool: 1.5.0-nullsafety.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
pub_semver: 1.4.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
shelf: 0.7.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
shelf_packages_handler: 2.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
shelf_static: 0.2.9+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
shelf_web_socket: 0.2.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
source_map_stack_trace: 2.1.0-nullsafety.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
source_maps: 0.10.10-nullsafety.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
stack_trace: 1.10.0-nullsafety.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
stream_channel: 2.1.0-nullsafety.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
test_api: 0.2.19-nullsafety.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
test_core: 0.3.12-nullsafety.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
vm_service: 5.5.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
watcher: 0.9.7+15 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
web_socket_channel: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
webkit_inspection_protocol: 0.7.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
yaml: 2.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
# PUBSPEC CHECKSUM: 0219
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:io';
import 'package:test/test.dart' hide TypeMatcher, isInstanceOf;
import 'package:test/test.dart' as test_package show TypeMatcher;
export 'package:test/test.dart' hide TypeMatcher, isInstanceOf;
// Defines a 'package:test' shim.
// TODO(ianh): Remove this file once https://github.com/dart-lang/matcher/issues/98 is fixed
/// A matcher that compares the type of the actual value to the type argument T.
test_package.TypeMatcher<T> isInstanceOf<T>() => isA<T>();
void tryToDelete(Directory directory) {
// This should not be necessary, but it turns out that
// on Windows it's common for deletions to fail due to
// bogus (we think) "access denied" errors.
try {
directory.deleteSync(recursive: true);
} on FileSystemException catch (error) {
print('Failed to delete ${directory.path}: $error');
}
}
{
"context": {
"date": "2019-12-17 15:14:14",
"num_cpus": 56,
"mhz_per_cpu": 2594,
"cpu_scaling_enabled": true,
"library_build_type": "release"
},
"benchmarks": [
{
"name": "BM_PaintRecordInit",
"iterations": 6749079,
"real_time": 101,
"cpu_time": 101,
"time_unit": "ns"
},
{
"name": "BM_ParagraphShortLayout",
"iterations": 151761,
"real_time": 4460,
"cpu_time": 4460,
"time_unit": "ns"
},
{
"name": "BM_ParagraphStylesBigO_BigO",
"cpu_coefficient": 6548,
"real_coefficient": 6548,
"big_o": "N",
"time_unit": "ns"
}
]
}
// Copyright 2014 The Flutter Authors. 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:metrics_center/src/common.dart';
import 'package:metrics_center/src/flutter.dart';
import 'common.dart';
void main() {
test('FlutterEngineMetricPoint works.', () {
const String gitRevision = 'ca799fa8b2254d09664b78ee80c43b434788d112';
final FlutterEngineMetricPoint simplePoint = FlutterEngineMetricPoint(
'BM_ParagraphLongLayout',
287235,
gitRevision,
);
expect(simplePoint.value, equals(287235));
expect(simplePoint.tags[kGithubRepoKey], kFlutterEngineRepo);
expect(simplePoint.tags[kGitRevisionKey], gitRevision);
expect(simplePoint.tags[kNameKey], 'BM_ParagraphLongLayout');
final FlutterEngineMetricPoint detailedPoint = FlutterEngineMetricPoint(
'BM_ParagraphLongLayout',
287224,
'ca799fa8b2254d09664b78ee80c43b434788d112',
moreTags: const <String, String>{
'executable': 'txt_benchmarks',
'sub_result': 'CPU',
kUnitKey: 'ns',
},
);
expect(detailedPoint.value, equals(287224));
expect(detailedPoint.tags['executable'], equals('txt_benchmarks'));
expect(detailedPoint.tags['sub_result'], equals('CPU'));
expect(detailedPoint.tags[kUnitKey], equals('ns'));
});
}
\ No newline at end of file
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:googleapis/storage/v1.dart';
import 'package:fake_async/fake_async.dart';
import 'package:gcloud/storage.dart';
import 'package:googleapis_auth/auth_io.dart';
import 'package:metrics_center/src/gcs_lock.dart';
import 'package:metrics_center/src/skiaperf.dart';
import 'package:mockito/mockito.dart';
import 'common.dart';
import 'utility.dart';
enum TestPhase {
run1,
run2,
}
class MockClient extends Mock implements AuthClient {}
void main() {
const Duration kDelayStep = Duration(milliseconds: 10);
final Map<String, dynamic> credentialsJson = getTestGcpCredentialsJson();
test('GcsLock prints warnings for long waits', () {
// Capture print to verify error messages.
final List<String> prints = <String>[];
final ZoneSpecification spec =
ZoneSpecification(print: (_, __, ___, String msg) => prints.add(msg));
Zone.current.fork(specification: spec).run<void>(() {
fakeAsync((FakeAsync fakeAsync) {
final MockClient mockClient = MockClient();
final GcsLock lock = GcsLock(mockClient, 'mockBucket');
when(mockClient.send(any)).thenThrow(DetailedApiRequestError(412, ''));
final Future<void> runFinished =
lock.protectedRun('mock.lock', () async {});
fakeAsync.elapse(const Duration(seconds: 10));
when(mockClient.send(any)).thenThrow(AssertionError('Stop!'));
runFinished.catchError((dynamic e) {
final AssertionError error = e as AssertionError;
expect(error.message, 'Stop!');
print('${error.message}');
});
fakeAsync.elapse(const Duration(seconds: 20));
});
});
const String kExpectedErrorMessage = 'The lock is waiting for a long time: '
'0:00:10.240000. If the lock file mock.lock in bucket mockBucket '
'seems to be stuck (i.e., it was created a long time ago and no one '
'seems to be owning it currently), delete it manually to unblock this.';
expect(prints, equals(<String>[kExpectedErrorMessage, 'Stop!']));
});
test('GcsLock integration test: single protectedRun is successful', () async {
final AutoRefreshingAuthClient client = await clientViaServiceAccount(
ServiceAccountCredentials.fromJson(credentialsJson), Storage.SCOPES);
final GcsLock lock = GcsLock(client, SkiaPerfDestination.kTestBucketName);
int testValue = 0;
await lock.protectedRun('test.lock', () async {
testValue = 1;
});
expect(testValue, 1);
}, skip: credentialsJson == null);
test('GcsLock integration test: protectedRun is exclusive', () async {
final AutoRefreshingAuthClient client = await clientViaServiceAccount(
ServiceAccountCredentials.fromJson(credentialsJson), Storage.SCOPES);
final GcsLock lock1 = GcsLock(client, SkiaPerfDestination.kTestBucketName);
final GcsLock lock2 = GcsLock(client, SkiaPerfDestination.kTestBucketName);
TestPhase phase = TestPhase.run1;
final Completer<void> started1 = Completer<void>();
final Future<void> finished1 = lock1.protectedRun('test.lock', () async {
started1.complete();
while (phase == TestPhase.run1) {
await Future<void>.delayed(kDelayStep);
}
});
await started1.future;
final Completer<void> started2 = Completer<void>();
final Future<void> finished2 = lock2.protectedRun('test.lock', () async {
started2.complete();
});
// started2 should not be set even after a long wait because lock1 is
// holding the GCS lock file.
await Future<void>.delayed(kDelayStep * 10);
expect(started2.isCompleted, false);
// When phase is switched to run2, lock1 should be released soon and
// lock2 should soon be able to proceed its protectedRun.
phase = TestPhase.run2;
await started2.future;
await finished1;
await finished2;
}, skip: credentialsJson == null);
}
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@Timeout(Duration(seconds: 3600))
import 'package:metrics_center/src/github_helper.dart';
import 'common.dart';
void main() {
test('GithubHelper gets correct commit date time', () async {
final GithubHelper helper = GithubHelper();
expect(
await helper.getCommitDateTime(
'flutter/flutter',
'ad20d368ffa09559754e4b2b5c12951341ca3b2d',
),
equals(DateTime.parse('2019-12-06 03:33:01.000Z')),
);
});
test('GithubHelper is a singleton', () {
final GithubHelper helper1 = GithubHelper();
final GithubHelper helper2 = GithubHelper();
expect(helper1, equals(helper2));
});
test('GithubHelper can query the same commit 1000 times within 1 second',
() async {
final DateTime start = DateTime.now();
for (int i = 0; i < 1000; i += 1) {
await GithubHelper().getCommitDateTime(
'flutter/flutter',
'ad20d368ffa09559754e4b2b5c12951341ca3b2d',
);
}
final Duration duration = DateTime.now().difference(start);
expect(duration, lessThan(const Duration(seconds: 1)));
});
}
// Copyright 2014 The Flutter Authors. 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:metrics_center/src/common.dart';
import 'package:metrics_center/src/google_benchmark.dart';
import 'common.dart';
import 'utility.dart';
void main() {
test('GoogleBenchmarkParser parses example json.', () async {
final List<MetricPoint> points =
await GoogleBenchmarkParser.parse('test/example_google_benchmark.json');
expect(points.length, 6);
expectSetMatch(
points.map((MetricPoint p) => p.value),
<int>[101, 101, 4460, 4460, 6548, 6548],
);
expectSetMatch(
points.map((MetricPoint p) => p.tags[kSubResultKey]),
<String>[
'cpu_time',
'real_time',
'cpu_coefficient',
'real_coefficient',
],
);
expectSetMatch(
points.map((MetricPoint p) => p.tags[kNameKey]),
<String>[
'BM_PaintRecordInit',
'BM_ParagraphShortLayout',
'BM_ParagraphStylesBigO_BigO',
],
);
});
}
// Copyright 2014 The Flutter Authors. 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:gcloud/src/datastore_impl.dart';
import 'package:googleapis_auth/auth_io.dart';
import 'package:metrics_center/src/common.dart';
import 'package:metrics_center/src/legacy_flutter.dart';
import 'common.dart';
import 'utility.dart';
const String kTestSourceId = 'test';
void main() {
final Map<String, dynamic> credentialsJson = getTestGcpCredentialsJson();
test(
'LegacyFlutterDestination integration test: '
'update does not crash.', () async {
final LegacyFlutterDestination dst =
await LegacyFlutterDestination.makeFromCredentialsJson(credentialsJson);
await dst.update(<MetricPoint>[MetricPoint(1.0, const <String, String>{})]);
}, skip: credentialsJson == null);
test(
'LegacyFlutterDestination integration test: '
'can update with an access token.', () async {
final AutoRefreshingAuthClient client = await clientViaServiceAccount(
ServiceAccountCredentials.fromJson(credentialsJson),
DatastoreImpl.SCOPES,
);
final String token = client.credentials.accessToken.data;
final LegacyFlutterDestination dst =
LegacyFlutterDestination.makeFromAccessToken(
token,
credentialsJson[kProjectId] as String,
);
await dst.update(<MetricPoint>[MetricPoint(1.0, const <String, String>{})]);
}, skip: credentialsJson == null);
}
This diff is collapsed.
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:convert';
import 'dart:io';
import 'common.dart';
// This will be used in many of our unit tests.
void expectSetMatch<T>(Iterable<T> actual, Iterable<T> expected) {
expect(Set<T>.from(actual), equals(Set<T>.from(expected)));
}
// May return null if the credentials file doesn't exist.
Map<String, dynamic> getTestGcpCredentialsJson() {
final File f = File('secret/test_gcp_credentials.json');
if (!f.existsSync()) {
return null;
}
return jsonDecode(File('secret/test_gcp_credentials.json').readAsStringSync())
as Map<String, dynamic>;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment