Skip staging test update to cocoon in test runner (#88835)
This commit is contained in:
parent
61502a64bc
commit
884dfc260d
@ -41,21 +41,14 @@ class UploadResultsCommand extends Command<void> {
|
|||||||
final String? testStatus = argResults!['test-status'] as String?;
|
final String? testStatus = argResults!['test-status'] as String?;
|
||||||
final String? commitTime = argResults!['commit-time'] as String?;
|
final String? commitTime = argResults!['commit-time'] as String?;
|
||||||
|
|
||||||
// Upload metrics to metrics_center from test runner when `commitTime` is specified. This
|
// Upload metrics to skia perf from test runner when `resultsPath` is specified.
|
||||||
// is mainly for testing purpose.
|
if (resultsPath != null) {
|
||||||
// The upload step will be skipped from cocoon once this is validated.
|
await uploadToSkiaPerf(resultsPath, commitTime);
|
||||||
// TODO(keyong): remove try block to block test when this is validated to work https://github.com/flutter/flutter/issues/88484
|
print('Successfully uploaded metrics to skia perf');
|
||||||
try {
|
|
||||||
if (commitTime != null) {
|
|
||||||
await uploadToMetricsCenter(resultsPath, commitTime);
|
|
||||||
print('Successfully uploaded metrics to metrics center');
|
|
||||||
}
|
|
||||||
} on Exception catch (e, stacktrace) {
|
|
||||||
print('Uploading metrics failure: $e\n\n$stacktrace');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final Cocoon cocoon = Cocoon(serviceAccountTokenPath: serviceAccountTokenFile);
|
final Cocoon cocoon = Cocoon(serviceAccountTokenPath: serviceAccountTokenFile);
|
||||||
return cocoon.sendResultsPath(
|
return cocoon.sendTaskStatus(
|
||||||
resultsPath: resultsPath,
|
resultsPath: resultsPath,
|
||||||
isTestFlaky: testFlakyStatus == 'True',
|
isTestFlaky: testFlakyStatus == 'True',
|
||||||
gitBranch: gitBranch,
|
gitBranch: gitBranch,
|
||||||
|
@ -75,16 +75,16 @@ class Cocoon {
|
|||||||
return _commitSha = result.stdout as String;
|
return _commitSha = result.stdout as String;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Upload the JSON results in [resultsPath] to Cocoon.
|
/// Update test status to Cocoon.
|
||||||
///
|
///
|
||||||
/// Flutter infrastructure's workflow is:
|
/// Flutter infrastructure's workflow is:
|
||||||
/// 1. Run DeviceLab test, writing results to a known path
|
/// 1. Run DeviceLab test
|
||||||
/// 2. Request service account token from luci auth (valid for at least 3 minutes)
|
/// 2. Request service account token from luci auth (valid for at least 3 minutes)
|
||||||
/// 3. Upload results from (1) to Cocoon
|
/// 3. Update test status from (1) to Cocoon
|
||||||
///
|
///
|
||||||
/// The `resultsPath` is not available for all tests. When it doesn't show up, we
|
/// The `resultsPath` is not available for all tests. When it doesn't show up, we
|
||||||
/// need to append `CommitBranch`, `CommitSha`, and `BuilderName`.
|
/// need to append `CommitBranch`, `CommitSha`, and `BuilderName`.
|
||||||
Future<void> sendResultsPath({
|
Future<void> sendTaskStatus({
|
||||||
String? resultsPath,
|
String? resultsPath,
|
||||||
bool? isTestFlaky,
|
bool? isTestFlaky,
|
||||||
String? gitBranch,
|
String? gitBranch,
|
||||||
@ -102,8 +102,7 @@ class Cocoon {
|
|||||||
resultsJson['NewStatus'] = testStatus;
|
resultsJson['NewStatus'] = testStatus;
|
||||||
}
|
}
|
||||||
resultsJson['TestFlaky'] = isTestFlaky ?? false;
|
resultsJson['TestFlaky'] = isTestFlaky ?? false;
|
||||||
const List<String> supportedBranches = <String>['master'];
|
if (_shouldUpdateCocoon(resultsJson)) {
|
||||||
if (supportedBranches.contains(resultsJson['CommitBranch'])) {
|
|
||||||
await retry(
|
await retry(
|
||||||
() async => _sendUpdateTaskRequest(resultsJson).timeout(Duration(seconds: requestTimeoutLimit)),
|
() async => _sendUpdateTaskRequest(resultsJson).timeout(Duration(seconds: requestTimeoutLimit)),
|
||||||
retryIf: (Exception e) => e is SocketException || e is TimeoutException || e is ClientException,
|
retryIf: (Exception e) => e is SocketException || e is TimeoutException || e is ClientException,
|
||||||
@ -112,6 +111,13 @@ class Cocoon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Only post-submit tests on `master` are allowed to update in cocoon.
|
||||||
|
bool _shouldUpdateCocoon(Map<String, dynamic> resultJson) {
|
||||||
|
const List<String> supportedBranches = <String>['master'];
|
||||||
|
return supportedBranches.contains(resultJson['CommitBranch']) &&
|
||||||
|
!resultJson['BuilderName'].toString().toLowerCase().contains('staging');
|
||||||
|
}
|
||||||
|
|
||||||
/// Write the given parameters into an update task request and store the JSON in [resultsPath].
|
/// Write the given parameters into an update task request and store the JSON in [resultsPath].
|
||||||
Future<void> writeTaskResultToFile({
|
Future<void> writeTaskResultToFile({
|
||||||
String? builderName,
|
String? builderName,
|
||||||
|
@ -45,6 +45,7 @@ Future<FlutterDestination> connectFlutterDestination() async {
|
|||||||
/// ]
|
/// ]
|
||||||
/// }
|
/// }
|
||||||
List<MetricPoint> parse(Map<String, dynamic> resultsJson) {
|
List<MetricPoint> parse(Map<String, dynamic> resultsJson) {
|
||||||
|
print('Results to upload to skia perf: $resultsJson');
|
||||||
final List<String> scoreKeys =
|
final List<String> scoreKeys =
|
||||||
(resultsJson['BenchmarkScoreKeys'] as List<dynamic>?)?.cast<String>() ?? const <String>[];
|
(resultsJson['BenchmarkScoreKeys'] as List<dynamic>?)?.cast<String>() ?? const <String>[];
|
||||||
final Map<String, dynamic> resultData =
|
final Map<String, dynamic> resultData =
|
||||||
@ -70,8 +71,13 @@ List<MetricPoint> parse(Map<String, dynamic> resultsJson) {
|
|||||||
return metricPoints;
|
return metricPoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Upload test metrics to metrics center.
|
/// Upload JSON results to skia perf.
|
||||||
Future<void> uploadToMetricsCenter(String? resultsPath, String? commitTime) async {
|
///
|
||||||
|
/// Flutter infrastructure's workflow is:
|
||||||
|
/// 1. Run DeviceLab test, writing results to a known path
|
||||||
|
/// 2. Request service account token from luci auth (valid for at least 3 minutes)
|
||||||
|
/// 3. Upload results from (1) to skia perf.
|
||||||
|
Future<void> uploadToSkiaPerf(String? resultsPath, String? commitTime) async {
|
||||||
int commitTimeSinceEpoch;
|
int commitTimeSinceEpoch;
|
||||||
if (resultsPath == null) {
|
if (resultsPath == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -132,7 +132,7 @@ void main() {
|
|||||||
'"ResultData":{},'
|
'"ResultData":{},'
|
||||||
'"BenchmarkScoreKeys":[]}';
|
'"BenchmarkScoreKeys":[]}';
|
||||||
fs.file(resultsPath).writeAsStringSync(updateTaskJson);
|
fs.file(resultsPath).writeAsStringSync(updateTaskJson);
|
||||||
await cocoon.sendResultsPath(resultsPath: resultsPath);
|
await cocoon.sendTaskStatus(resultsPath: resultsPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('uploads expected update task payload from results file', () async {
|
test('uploads expected update task payload from results file', () async {
|
||||||
@ -154,7 +154,7 @@ void main() {
|
|||||||
'"ResultData":{"i":0.0,"j":0.0,"not_a_metric":"something"},'
|
'"ResultData":{"i":0.0,"j":0.0,"not_a_metric":"something"},'
|
||||||
'"BenchmarkScoreKeys":["i","j"]}';
|
'"BenchmarkScoreKeys":["i","j"]}';
|
||||||
fs.file(resultsPath).writeAsStringSync(updateTaskJson);
|
fs.file(resultsPath).writeAsStringSync(updateTaskJson);
|
||||||
await cocoon.sendResultsPath(resultsPath: resultsPath);
|
await cocoon.sendTaskStatus(resultsPath: resultsPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Verify retries for task result upload', () async {
|
test('Verify retries for task result upload', () async {
|
||||||
@ -186,7 +186,7 @@ void main() {
|
|||||||
'"ResultData":{"i":0.0,"j":0.0,"not_a_metric":"something"},'
|
'"ResultData":{"i":0.0,"j":0.0,"not_a_metric":"something"},'
|
||||||
'"BenchmarkScoreKeys":["i","j"]}';
|
'"BenchmarkScoreKeys":["i","j"]}';
|
||||||
fs.file(resultsPath).writeAsStringSync(updateTaskJson);
|
fs.file(resultsPath).writeAsStringSync(updateTaskJson);
|
||||||
await cocoon.sendResultsPath(resultsPath: resultsPath);
|
await cocoon.sendTaskStatus(resultsPath: resultsPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Verify timeout and retry for task result upload', () async {
|
test('Verify timeout and retry for task result upload', () async {
|
||||||
@ -221,7 +221,7 @@ void main() {
|
|||||||
'"ResultData":{"i":0.0,"j":0.0,"not_a_metric":"something"},'
|
'"ResultData":{"i":0.0,"j":0.0,"not_a_metric":"something"},'
|
||||||
'"BenchmarkScoreKeys":["i","j"]}';
|
'"BenchmarkScoreKeys":["i","j"]}';
|
||||||
fs.file(resultsPath).writeAsStringSync(updateTaskJson);
|
fs.file(resultsPath).writeAsStringSync(updateTaskJson);
|
||||||
await cocoon.sendResultsPath(resultsPath: resultsPath);
|
await cocoon.sendTaskStatus(resultsPath: resultsPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Verify timeout does not trigger for result upload', () async {
|
test('Verify timeout does not trigger for result upload', () async {
|
||||||
@ -256,7 +256,7 @@ void main() {
|
|||||||
'"ResultData":{"i":0.0,"j":0.0,"not_a_metric":"something"},'
|
'"ResultData":{"i":0.0,"j":0.0,"not_a_metric":"something"},'
|
||||||
'"BenchmarkScoreKeys":["i","j"]}';
|
'"BenchmarkScoreKeys":["i","j"]}';
|
||||||
fs.file(resultsPath).writeAsStringSync(updateTaskJson);
|
fs.file(resultsPath).writeAsStringSync(updateTaskJson);
|
||||||
await cocoon.sendResultsPath(resultsPath: resultsPath);
|
await cocoon.sendTaskStatus(resultsPath: resultsPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Verify failure without retries for task result upload', () async {
|
test('Verify failure without retries for task result upload', () async {
|
||||||
@ -288,7 +288,7 @@ void main() {
|
|||||||
'"ResultData":{"i":0.0,"j":0.0,"not_a_metric":"something"},'
|
'"ResultData":{"i":0.0,"j":0.0,"not_a_metric":"something"},'
|
||||||
'"BenchmarkScoreKeys":["i","j"]}';
|
'"BenchmarkScoreKeys":["i","j"]}';
|
||||||
fs.file(resultsPath).writeAsStringSync(updateTaskJson);
|
fs.file(resultsPath).writeAsStringSync(updateTaskJson);
|
||||||
expect(() => cocoon.sendResultsPath(resultsPath: resultsPath), throwsA(isA<ClientException>()));
|
expect(() => cocoon.sendTaskStatus(resultsPath: resultsPath), throwsA(isA<ClientException>()));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('throws client exception on non-200 responses', () async {
|
test('throws client exception on non-200 responses', () async {
|
||||||
@ -310,10 +310,10 @@ void main() {
|
|||||||
'"ResultData":{"i":0.0,"j":0.0,"not_a_metric":"something"},'
|
'"ResultData":{"i":0.0,"j":0.0,"not_a_metric":"something"},'
|
||||||
'"BenchmarkScoreKeys":["i","j"]}';
|
'"BenchmarkScoreKeys":["i","j"]}';
|
||||||
fs.file(resultsPath).writeAsStringSync(updateTaskJson);
|
fs.file(resultsPath).writeAsStringSync(updateTaskJson);
|
||||||
expect(() => cocoon.sendResultsPath(resultsPath: resultsPath), throwsA(isA<ClientException>()));
|
expect(() => cocoon.sendTaskStatus(resultsPath: resultsPath), throwsA(isA<ClientException>()));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('does not upload results on non-supported branches', () async {
|
test('does not update on non-supported branches', () async {
|
||||||
// Any network failure would cause the upoad to fail
|
// Any network failure would cause the upoad to fail
|
||||||
mockClient = MockClient((Request request) async => Response('', 500));
|
mockClient = MockClient((Request request) async => Response('', 500));
|
||||||
|
|
||||||
@ -335,7 +335,32 @@ void main() {
|
|||||||
fs.file(resultsPath).writeAsStringSync(updateTaskJson);
|
fs.file(resultsPath).writeAsStringSync(updateTaskJson);
|
||||||
|
|
||||||
// This will fail if it decided to upload results
|
// This will fail if it decided to upload results
|
||||||
await cocoon.sendResultsPath(resultsPath: resultsPath);
|
await cocoon.sendTaskStatus(resultsPath: resultsPath);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('does not update for staging test', () async {
|
||||||
|
// Any network failure would cause the upoad to fail
|
||||||
|
mockClient = MockClient((Request request) async => Response('', 500));
|
||||||
|
|
||||||
|
cocoon = Cocoon(
|
||||||
|
serviceAccountTokenPath: serviceAccountTokenPath,
|
||||||
|
fs: fs,
|
||||||
|
httpClient: mockClient,
|
||||||
|
requestRetryLimit: 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
const String resultsPath = 'results.json';
|
||||||
|
const String updateTaskJson = '{'
|
||||||
|
'"CommitBranch":"master",'
|
||||||
|
'"CommitSha":"$commitSha",'
|
||||||
|
'"BuilderName":"Linux_staging_test",'
|
||||||
|
'"NewStatus":"Succeeded",'
|
||||||
|
'"ResultData":{"i":0.0,"j":0.0,"not_a_metric":"something"},'
|
||||||
|
'"BenchmarkScoreKeys":["i","j"]}';
|
||||||
|
fs.file(resultsPath).writeAsStringSync(updateTaskJson);
|
||||||
|
|
||||||
|
// This will fail if it decided to upload results
|
||||||
|
await cocoon.sendTaskStatus(resultsPath: resultsPath);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user