// 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 { TaskResult.buildOnly() : succeeded = true, data = null, detailFiles = null, benchmarkScoreKeys = null, message = 'No tests run'; /// Constructs a successful result. TaskResult.success(this.data, { this.benchmarkScoreKeys = const <String>[], this.detailFiles = const <String>[], this.message = 'success', }) : succeeded = true { 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] as Object).runtimeType}: ${prettyJson.convert(data![key])}'; } } } } /// Constructs a successful result using JSON data stored in a file. factory TaskResult.successFromFile(File file, { List<String> benchmarkScoreKeys = const <String>[], List<String> detailFiles = const <String>[], }) { return TaskResult.success( json.decode(file.readAsStringSync()) as Map<String, dynamic>?, benchmarkScoreKeys: benchmarkScoreKeys, detailFiles: detailFiles, ); } /// 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>(); final List<String> detailFiles = (json['detailFiles'] as List<dynamic>? ?? <String>[]).cast<String>(); return TaskResult.success(json['data'] as Map<String, dynamic>?, benchmarkScoreKeys: benchmarkScoreKeys, detailFiles: detailFiles, message: json['reason'] as String?, ); } return TaskResult.failure(json['reason'] as String?); } /// Constructs an unsuccessful result. TaskResult.failure(this.message) : succeeded = false, data = null, detailFiles = null, benchmarkScoreKeys = null; /// 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; json['detailFiles'] = detailFiles; json['benchmarkScoreKeys'] = benchmarkScoreKeys; } if (message != null || !succeeded) { json['reason'] = message; } return json; } @override String toString() => message ?? ''; } class TaskResultCheckProcesses extends TaskResult { TaskResultCheckProcesses() : super.success(null); }