Unverified Commit 9e206a0c authored by Casey Hillers's avatar Casey Hillers Committed by GitHub

Refactor devicelab logic to use TaskResult instead of JSON (#67550)

parent b851f997
......@@ -11,6 +11,7 @@ import 'package:path/path.dart' as path;
import 'package:flutter_devicelab/framework/ab.dart';
import 'package:flutter_devicelab/framework/manifest.dart';
import 'package:flutter_devicelab/framework/runner.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
ArgResults args;
......@@ -89,7 +90,7 @@ Future<void> main(List<String> rawArgs) async {
Future<void> _runTasks() async {
for (final String taskName in _taskNames) {
section('Running task "$taskName"');
final Map<String, dynamic> result = await runTask(
final TaskResult result = await runTask(
taskName,
silent: silent,
localEngine: localEngine,
......@@ -101,7 +102,7 @@ Future<void> _runTasks() async {
print(const JsonEncoder.withIndent(' ').convert(result));
section('Finished task "$taskName"');
if (!(result['success'] as bool)) {
if (!result.succeeded) {
exitCode = 1;
if (exitOnFirstTestFailure) {
return;
......@@ -134,7 +135,7 @@ Future<void> _runABTest() async {
section('Run #$i');
print('Running with the default engine (A)');
final Map<String, dynamic> defaultEngineResult = await runTask(
final TaskResult defaultEngineResult = await runTask(
taskName,
silent: silent,
deviceId: deviceId,
......@@ -143,7 +144,7 @@ Future<void> _runABTest() async {
print('Default engine result:');
print(const JsonEncoder.withIndent(' ').convert(defaultEngineResult));
if (!(defaultEngineResult['success'] as bool)) {
if (!defaultEngineResult.succeeded) {
stderr.writeln('Task failed on the default engine.');
exit(1);
}
......@@ -151,7 +152,7 @@ Future<void> _runABTest() async {
abTest.addAResult(defaultEngineResult);
print('Running with the local engine (B)');
final Map<String, dynamic> localEngineResult = await runTask(
final TaskResult localEngineResult = await runTask(
taskName,
silent: silent,
localEngine: localEngine,
......@@ -162,7 +163,7 @@ Future<void> _runABTest() async {
print('Task localEngineResult:');
print(const JsonEncoder.withIndent(' ').convert(localEngineResult));
if (!(localEngineResult['success'] as bool)) {
if (!localEngineResult.succeeded) {
stderr.writeln('Task failed on the local engine.');
exit(1);
}
......
......@@ -6,6 +6,7 @@ import 'dart:io';
import 'package:flutter_devicelab/framework/apk_utils.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
......
......@@ -4,6 +4,7 @@
import 'package:flutter_devicelab/framework/apk_utils.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
......
......@@ -6,6 +6,7 @@ import 'dart:io';
import 'package:flutter_devicelab/framework/apk_utils.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
......
......@@ -5,6 +5,7 @@
import 'dart:io';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
......
......@@ -6,6 +6,7 @@ import 'dart:io';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/ios.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
......
......@@ -10,6 +10,7 @@ import 'package:path/path.dart' as path;
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
Future<String> runFlutterAndQuit(List<String> args, Device device) async {
......
......@@ -11,6 +11,7 @@ import 'package:vm_service_client/vm_service_client.dart';
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
void main() {
......
......@@ -4,6 +4,7 @@
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as p;
......
......@@ -6,6 +6,7 @@ import 'dart:convert';
import 'dart:io';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
......
......@@ -4,6 +4,7 @@
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
Future<String> _runWithMode(String mode, String deviceId) async {
......
......@@ -10,6 +10,7 @@ import 'dart:math';
import 'package:path/path.dart' as path;
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
void generateMain(Directory appDir, String sentinel) {
......
......@@ -5,6 +5,7 @@
import 'package:flutter_devicelab/tasks/gallery.dart';
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
Future<void> main() async {
deviceOperatingSystem = DeviceOperatingSystem.android;
......
......@@ -6,6 +6,7 @@ import 'dart:io';
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
// This test runs "//dev/integration_tests/flutter_gallery/test/live_smoketest.dart", which communicates
......
......@@ -9,6 +9,7 @@ import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:flutter_devicelab/versions/gallery.dart' show galleryVersion;
......
......@@ -7,6 +7,7 @@ import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:flutter_devicelab/versions/gallery.dart' show galleryVersion;
......
......@@ -10,6 +10,7 @@ import 'package:path/path.dart' as path;
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
final Directory flutterGalleryDir = dir(path.join(flutterDirectory.path, 'examples/hello_world'));
......
......@@ -21,6 +21,7 @@ import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
// Matches the output of the "test" package, e.g.: "00:01 +1 loading foo"
......
......@@ -4,6 +4,7 @@
import 'package:flutter_devicelab/framework/apk_utils.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
Future<void> main() async {
......
......@@ -6,6 +6,7 @@ import 'dart:io';
import 'package:flutter_devicelab/framework/apk_utils.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
......
......@@ -5,6 +5,7 @@
import 'dart:io';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
......
......@@ -5,6 +5,7 @@
import 'dart:io';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
......
......@@ -6,6 +6,7 @@ import 'dart:io';
import 'package:flutter_devicelab/framework/apk_utils.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
......
......@@ -6,6 +6,7 @@ import 'dart:io';
import 'package:flutter_devicelab/framework/apk_utils.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
......
......@@ -7,6 +7,7 @@ import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:flutter_devicelab/framework/apk_utils.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
Future<void> main() async {
......
......@@ -6,6 +6,7 @@ import 'dart:io';
import 'package:flutter_devicelab/framework/apk_utils.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
Future<void> main() async {
......
......@@ -6,6 +6,7 @@ import 'dart:io';
import 'package:flutter_devicelab/framework/apk_utils.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
......
......@@ -5,6 +5,7 @@
import 'dart:io';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
......
......@@ -7,6 +7,7 @@ import 'dart:io';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/ios.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
import 'package:meta/meta.dart';
......
......@@ -7,6 +7,7 @@ import 'dart:io';
import 'package:flutter_devicelab/framework/apk_utils.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/ios.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
......
......@@ -6,6 +6,7 @@ import 'dart:io';
import 'package:flutter_devicelab/framework/apk_utils.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
......
......@@ -6,6 +6,7 @@ import 'dart:io';
import 'package:flutter_devicelab/framework/apk_utils.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
......
......@@ -6,6 +6,7 @@ import 'dart:io';
import 'package:flutter_devicelab/framework/apk_utils.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
......
......@@ -6,6 +6,7 @@ import 'dart:io';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/ios.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
......
......@@ -6,6 +6,7 @@ import 'dart:convert';
import 'dart:io';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
......
......@@ -5,6 +5,7 @@
import 'dart:io';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
......
......@@ -10,6 +10,7 @@ import 'package:path/path.dart' as path;
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
void main() {
......
......@@ -10,6 +10,7 @@ import 'package:path/path.dart' as path;
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
void main() {
......
......@@ -8,6 +8,7 @@ import 'dart:io';
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
import 'package:vm_service_client/vm_service_client.dart';
......
......@@ -2,8 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
/// Smoke test of a successful task.
Future<void> main() async {
......
......@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
/// Smoke test of a task that fails by returning an unsuccessful response.
Future<void> main() async {
......
......@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
/// Smoke test of a successful task.
Future<void> main() async {
......
......@@ -6,6 +6,7 @@ import 'dart:io';
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'service_extensions_test.dart';
......
......@@ -6,6 +6,7 @@ import 'dart:convert';
import 'dart:io';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
......
......@@ -6,6 +6,7 @@ import 'dart:io';
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
......
......@@ -7,6 +7,7 @@ import 'dart:io';
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
......
......@@ -5,6 +5,8 @@
import 'dart:math' as math;
import 'package:meta/meta.dart';
import 'task_result.dart';
const String kBenchmarkTypeKeyName = 'benchmark_type';
const String kBenchmarkVersionKeyName = 'version';
const String kLocalEngineKeyName = 'local_engine';
......@@ -58,7 +60,7 @@ class ABTest {
/// The result may contain multiple score keys.
///
/// [result] is expected to be a serialization of [TaskResult].
void addAResult(Map<String, dynamic> result) {
void addAResult(TaskResult result) {
if (_runEnd != null) {
throw StateError('Cannot add results to ABTest after it is finalized');
}
......@@ -70,7 +72,7 @@ class ABTest {
/// The result may contain multiple score keys.
///
/// [result] is expected to be a serialization of [TaskResult].
void addBResult(Map<String, dynamic> result) {
void addBResult(TaskResult result) {
if (_runEnd != null) {
throw StateError('Cannot add results to ABTest after it is finalized');
}
......@@ -276,11 +278,9 @@ class _ScoreSummary {
}
}
void _addResult(Map<String, dynamic> result, Map<String, List<double>> results) {
final List<String> scoreKeys = (result['benchmarkScoreKeys'] as List<dynamic>).cast<String>();
final Map<String, dynamic> data = result['data'] as Map<String, dynamic>;
for (final String scoreKey in scoreKeys) {
final double score = (data[scoreKey] as num).toDouble();
void _addResult(TaskResult result, Map<String, List<double>> results) {
for (final String scoreKey in result.benchmarkScoreKeys) {
final double score = (result.data[scoreKey] as num).toDouble();
results.putIfAbsent(scoreKey, () => <double>[]).add(score);
}
}
......
......@@ -5,9 +5,9 @@
import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'task_result.dart';
import 'utils.dart';
final String platformLineSep = Platform.isWindows ? '\r\n' : '\n';
......
......@@ -13,6 +13,7 @@ import 'package:logging/logging.dart';
import 'package:stack_trace/stack_trace.dart';
import 'running_processes.dart';
import 'task_result.dart';
import 'utils.dart';
/// Represents a unit of work performed in the CI environment that can
......@@ -185,102 +186,3 @@ class _TaskRunner {
return completer.future;
}
}
/// A result of running a single task.
class TaskResult {
/// Constructs a successful result.
TaskResult.success(this.data, {
this.benchmarkScoreKeys = const <String>[],
this.detailFiles,
})
: succeeded = true,
message = 'success' {
const JsonEncoder prettyJson = JsonEncoder.withIndent(' ');
if (benchmarkScoreKeys != null) {
for (final String key in benchmarkScoreKeys) {
if (!data.containsKey(key)) {
throw 'Invalid benchmark score key "$key". It does not exist in task '
'result data ${prettyJson.convert(data)}';
} else if (data[key] is! num) {
throw 'Invalid benchmark score for key "$key". It is expected to be a num '
'but was ${data[key].runtimeType}: ${prettyJson.convert(data[key])}';
}
}
}
}
/// Constructs a successful result using JSON data stored in a file.
factory TaskResult.successFromFile(File file,
{List<String> benchmarkScoreKeys}) {
return TaskResult.success(
json.decode(file.readAsStringSync()) as Map<String, dynamic>,
benchmarkScoreKeys: benchmarkScoreKeys,
);
}
/// Constructs an unsuccessful result.
TaskResult.failure(this.message)
: succeeded = false,
data = null,
detailFiles = null,
benchmarkScoreKeys = const <String>[];
/// Whether the task succeeded.
final bool succeeded;
/// Task-specific JSON data
final Map<String, dynamic> data;
/// Files containing detail on the run (e.g. timeline trace files)
final List<String> detailFiles;
/// Keys in [data] that store scores that will be submitted to Cocoon.
///
/// Each key is also part of a benchmark's name tracked by Cocoon.
final List<String> benchmarkScoreKeys;
/// Whether the task failed.
bool get failed => !succeeded;
/// Explains the result in a human-readable format.
final String message;
/// Serializes this task result to JSON format.
///
/// The JSON format is as follows:
///
/// {
/// "success": true|false,
/// "data": arbitrary JSON data valid only for successful results,
/// "detailFiles": list of filenames containing detail on the run
/// "benchmarkScoreKeys": [
/// contains keys into "data" that represent benchmarks scores, which
/// can be uploaded, for example. to golem, valid only for successful
/// results
/// ],
/// "reason": failure reason string valid only for unsuccessful results
/// }
Map<String, dynamic> toJson() {
final Map<String, dynamic> json = <String, dynamic>{
'success': succeeded,
};
if (succeeded) {
json['data'] = data;
if (detailFiles != null)
json['detailFiles'] = detailFiles;
json['benchmarkScoreKeys'] = benchmarkScoreKeys;
} else {
json['reason'] = message;
}
return json;
}
@override
String toString() => message;
}
class TaskResultCheckProcesses extends TaskResult {
TaskResultCheckProcesses() : super.success(null);
}
......@@ -12,6 +12,8 @@ import 'package:vm_service_client/vm_service_client.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:flutter_devicelab/framework/adb.dart';
import 'task_result.dart';
/// Runs a task in a separate Dart VM and collects the result using the VM
/// service protocol.
///
......@@ -20,7 +22,7 @@ import 'package:flutter_devicelab/framework/adb.dart';
///
/// Running the task in [silent] mode will suppress standard output from task
/// processes and only print standard errors.
Future<Map<String, dynamic>> runTask(
Future<TaskResult> runTask(
String taskName, {
bool silent = false,
String localEngine,
......@@ -79,7 +81,8 @@ Future<Map<String, dynamic>> runTask(
try {
final VMIsolateRef isolate = await _connectToRunnerIsolate(await uri.future);
final Map<String, dynamic> taskResult = await isolate.invokeExtension('ext.cocoonRunTask') as Map<String, dynamic>;
final Map<String, dynamic> taskResultJson = await isolate.invokeExtension('ext.cocoonRunTask') as Map<String, dynamic>;
final TaskResult taskResult = TaskResult.fromJson(taskResultJson);
await runner.exitCode;
return taskResult;
} finally {
......
// 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';
/// A result of running a single task.
class TaskResult {
/// Constructs a successful result.
TaskResult.success(this.data, {
this.benchmarkScoreKeys = const <String>[],
this.detailFiles,
})
: succeeded = true,
message = 'success' {
const JsonEncoder prettyJson = JsonEncoder.withIndent(' ');
if (benchmarkScoreKeys != null) {
for (final String key in benchmarkScoreKeys) {
if (!data.containsKey(key)) {
throw 'Invalid benchmark score key "$key". It does not exist in task '
'result data ${prettyJson.convert(data)}';
} else if (data[key] is! num) {
throw 'Invalid benchmark score for key "$key". It is expected to be a num '
'but was ${data[key].runtimeType}: ${prettyJson.convert(data[key])}';
}
}
}
}
/// Constructs a successful result using JSON data stored in a file.
factory TaskResult.successFromFile(File file,
{List<String> benchmarkScoreKeys}) {
return TaskResult.success(
json.decode(file.readAsStringSync()) as Map<String, dynamic>,
benchmarkScoreKeys: benchmarkScoreKeys,
);
}
/// Constructs a [TaskResult] from JSON.
factory TaskResult.fromJson(Map<String, dynamic> json) {
final bool success = json['success'] as bool;
if (success) {
final List<String> benchmarkScoreKeys = (json['benchmarkScoreKeys'] as List<dynamic> ?? <String>[]).cast<String>();
return TaskResult.success(json['data'] as Map<String, dynamic>,
benchmarkScoreKeys: benchmarkScoreKeys);
}
return TaskResult.failure(json['reason'] as String);
}
/// Constructs an unsuccessful result.
TaskResult.failure(this.message)
: succeeded = false,
data = null,
detailFiles = null,
benchmarkScoreKeys = const <String>[];
/// Whether the task succeeded.
final bool succeeded;
/// Task-specific JSON data
final Map<String, dynamic> data;
/// Files containing detail on the run (e.g. timeline trace files)
final List<String> detailFiles;
/// Keys in [data] that store scores that will be submitted to Cocoon.
///
/// Each key is also part of a benchmark's name tracked by Cocoon.
final List<String> benchmarkScoreKeys;
/// Whether the task failed.
bool get failed => !succeeded;
/// Explains the result in a human-readable format.
final String message;
/// Serializes this task result to JSON format.
///
/// The JSON format is as follows:
///
/// {
/// "success": true|false,
/// "data": arbitrary JSON data valid only for successful results,
/// "detailFiles": list of filenames containing detail on the run
/// "benchmarkScoreKeys": [
/// contains keys into "data" that represent benchmarks scores, which
/// can be uploaded, for example. to golem, valid only for successful
/// results
/// ],
/// "reason": failure reason string valid only for unsuccessful results
/// }
Map<String, dynamic> toJson() {
final Map<String, dynamic> json = <String, dynamic>{
'success': succeeded,
};
if (succeeded) {
json['data'] = data;
if (detailFiles != null)
json['detailFiles'] = detailFiles;
json['benchmarkScoreKeys'] = benchmarkScoreKeys;
} else {
json['reason'] = message;
}
return json;
}
@override
String toString() => message;
}
class TaskResultCheckProcesses extends TaskResult {
TaskResultCheckProcesses() : super.success(null);
}
......@@ -12,7 +12,7 @@ import 'package:path/path.dart' as path;
import 'package:process/process.dart';
import 'package:stack_trace/stack_trace.dart';
import 'framework.dart';
import 'task_result.dart';
/// Virtual current working directory, which affect functions, such as [exec].
String cwd = Directory.current.path;
......
......@@ -6,7 +6,7 @@ import 'dart:io';
import 'package:path/path.dart' as path;
import '../framework/framework.dart';
import '../framework/task_result.dart';
import '../framework/utils.dart';
/// Run each benchmark this many times and compute average, min, max.
......
......@@ -5,6 +5,7 @@
import 'dart:io';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path;
......
......@@ -5,7 +5,7 @@
import 'dart:io';
import '../framework/adb.dart';
import '../framework/framework.dart';
import '../framework/task_result.dart';
import '../framework/utils.dart';
Future<TaskResult> runDartDefinesTask() async {
......
......@@ -6,8 +6,10 @@ import 'dart:convert';
import 'dart:io';
import 'dart:math' as math;
import '../framework/adb.dart';
import '../framework/framework.dart';
import '../framework/task_result.dart';
import '../framework/utils.dart';
TaskFunction createGalleryTransitionTest({bool semanticsEnabled = false}) {
......
......@@ -10,6 +10,7 @@ import 'package:path/path.dart' as path;
import '../framework/adb.dart';
import '../framework/framework.dart';
import '../framework/task_result.dart';
import '../framework/utils.dart';
final Directory _editedFlutterGalleryDir = dir(path.join(Directory.systemTemp.path, 'edited_flutter_gallery'));
......
......@@ -4,6 +4,7 @@
import '../framework/adb.dart';
import '../framework/framework.dart';
import '../framework/task_result.dart';
import '../framework/utils.dart';
TaskFunction createChannelsIntegrationTest() {
......
......@@ -5,7 +5,7 @@
import 'dart:io';
import '../framework/adb.dart';
import '../framework/framework.dart';
import '../framework/task_result.dart';
import '../framework/utils.dart';
Future<TaskResult> runEndToEndTests() async {
......
......@@ -10,6 +10,7 @@ import 'package:path/path.dart' as path;
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
/// Creates a device lab task that runs benchmarks in
......
......@@ -7,7 +7,7 @@ import 'dart:io';
import 'package:flutter_devicelab/tasks/perf_tests.dart';
import '../framework/framework.dart';
import '../framework/task_result.dart';
import '../framework/utils.dart';
import '../versions/gallery.dart' show galleryVersion;
......
......@@ -12,6 +12,7 @@ import 'package:path/path.dart' as path;
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
import 'package:flutter_devicelab/tasks/track_widget_creation_enabled_task.dart';
......
......@@ -6,6 +6,7 @@ import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
/// Combines several TaskFunctions with trivial success value into one.
......
......@@ -10,6 +10,7 @@ import 'package:path/path.dart' as path;
import '../framework/adb.dart';
import '../framework/framework.dart';
import '../framework/task_result.dart';
import '../framework/utils.dart';
TaskFunction createRunWithoutLeakTest(dynamic dir) {
......
......@@ -9,7 +9,7 @@ import 'package:vm_service/vm_service.dart';
import 'package:vm_service/vm_service_io.dart';
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:path/path.dart' as path;
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
final Directory integrationTestDir = Directory(
......
......@@ -14,7 +14,7 @@ import 'package:shelf/shelf_io.dart' as shelf_io;
import 'package:shelf_static/shelf_static.dart';
import 'package:flutter_devicelab/framework/browser.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart';
/// The port number used by the local benchmark server.
......
......@@ -9,6 +9,7 @@ import 'dart:io';
import 'package:path/path.dart' as path;
import '../framework/framework.dart';
import '../framework/task_result.dart';
import '../framework/utils.dart';
final Directory _editedFlutterGalleryDir = dir(path.join(Directory.systemTemp.path, 'edited_flutter_gallery'));
......
......@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'package:flutter_devicelab/framework/ab.dart';
import 'package:flutter_devicelab/framework/task_result.dart';
import 'common.dart';
......@@ -11,7 +12,8 @@ void main() {
final ABTest ab = ABTest('engine', 'test');
for (int i = 0; i < 5; i++) {
ab.addAResult(<String, dynamic>{
final TaskResult aResult = TaskResult.fromJson(<String, dynamic>{
'success': true,
'data': <String, dynamic>{
'i': i,
'j': 10 * i,
......@@ -19,14 +21,16 @@ void main() {
},
'benchmarkScoreKeys': <String>['i', 'j'],
});
ab.addBResult(<String, dynamic>{
ab.addAResult(aResult);
final TaskResult bResult = TaskResult.fromJson(<String, dynamic>{
'success': true,
'data': <String, dynamic>{
'i': i + 1,
'k': 10 * i + 1,
},
'benchmarkScoreKeys': <String>['i', 'k'],
});
ab.addBResult(bResult);
}
ab.finalize();
......
// 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:flutter_devicelab/framework/task_result.dart';
import 'common.dart';
void main() {
group('TaskResult fromJson', () {
test('succeeded', () {
final Map<String, dynamic> expectedJson = <String, dynamic>{
'success': true,
'data': <String, dynamic>{
'i': 5,
'j': 10,
'not_a_metric': 'something',
},
'benchmarkScoreKeys': <String>['i', 'j'],
};
final TaskResult result = TaskResult.fromJson(expectedJson);
expect(result.toJson(), expectedJson);
});
test('succeeded with empty data', () {
final TaskResult result = TaskResult.fromJson(<String, dynamic>{
'success': true,
});
final Map<String, dynamic> expectedJson = <String, dynamic>{
'success': true,
'data': null,
'benchmarkScoreKeys': <String>[],
};
expect(result.toJson(), expectedJson);
});
test('failed', () {
final Map<String, dynamic> expectedJson = <String, dynamic>{
'success': false,
'reason': 'failure message',
};
final TaskResult result = TaskResult.fromJson(expectedJson);
expect(result.toJson(), expectedJson);
});
});
}
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