task_result.dart 4.16 KB
Newer Older
1 2 3 4 5 6 7 8 9
// 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 {
10 11
  TaskResult.buildOnly()
      : succeeded = true,
12 13 14 15 16
        data = null,
        detailFiles = null,
        benchmarkScoreKeys = null,
        message = 'No tests run';

17 18 19
  /// Constructs a successful result.
  TaskResult.success(this.data, {
    this.benchmarkScoreKeys = const <String>[],
20
    this.detailFiles = const <String>[],
21
    this.message = 'success',
22
  })
23
      : succeeded = true {
24 25
    const JsonEncoder prettyJson = JsonEncoder.withIndent('  ');
    if (benchmarkScoreKeys != null) {
26 27
      for (final String key in benchmarkScoreKeys!) {
        if (!data!.containsKey(key)) {
28 29
          throw 'Invalid benchmark score key "$key". It does not exist in task '
              'result data ${prettyJson.convert(data)}';
30
        } else if (data![key] is! num) {
31
          throw 'Invalid benchmark score for key "$key". It is expected to be a num '
32
              'but was ${(data![key] as Object).runtimeType}: ${prettyJson.convert(data![key])}';
33 34 35 36 37 38
        }
      }
    }
  }

  /// Constructs a successful result using JSON data stored in a file.
39 40 41 42
  factory TaskResult.successFromFile(File file, {
    List<String> benchmarkScoreKeys = const <String>[],
    List<String> detailFiles = const <String>[],
  }) {
43
    return TaskResult.success(
44
      json.decode(file.readAsStringSync()) as Map<String, dynamic>?,
45
      benchmarkScoreKeys: benchmarkScoreKeys,
46
      detailFiles: detailFiles,
47 48 49 50 51 52 53
    );
  }

  /// Constructs a [TaskResult] from JSON.
  factory TaskResult.fromJson(Map<String, dynamic> json) {
    final bool success = json['success'] as bool;
    if (success) {
54 55
      final List<String> benchmarkScoreKeys = (json['benchmarkScoreKeys'] as List<dynamic>? ?? <String>[]).cast<String>();
      final List<String> detailFiles = (json['detailFiles'] as List<dynamic>? ?? <String>[]).cast<String>();
56
      return TaskResult.success(json['data'] as Map<String, dynamic>?,
57 58
        benchmarkScoreKeys: benchmarkScoreKeys,
        detailFiles: detailFiles,
59
        message: json['reason'] as String?,
60
      );
61 62
    }

63
    return TaskResult.failure(json['reason'] as String?);
64 65 66 67 68 69 70
  }

  /// Constructs an unsuccessful result.
  TaskResult.failure(this.message)
      : succeeded = false,
        data = null,
        detailFiles = null,
71
        benchmarkScoreKeys = null;
72 73 74 75 76

  /// Whether the task succeeded.
  final bool succeeded;

  /// Task-specific JSON data
77
  final Map<String, dynamic>? data;
78 79

  /// Files containing detail on the run (e.g. timeline trace files)
80
  final List<String>? detailFiles;
81 82 83 84

  /// 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.
85
  final List<String>? benchmarkScoreKeys;
86 87 88 89 90

  /// Whether the task failed.
  bool get failed => !succeeded;

  /// Explains the result in a human-readable format.
91
  final String? message;
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114

  /// 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;
115
      json['detailFiles'] = detailFiles;
116
      json['benchmarkScoreKeys'] = benchmarkScoreKeys;
117 118 119
    }

    if (message != null || !succeeded) {
120 121 122 123 124 125 126
      json['reason'] = message;
    }

    return json;
  }

  @override
127
  String toString() => message ?? '';
128 129 130 131 132
}

class TaskResultCheckProcesses extends TaskResult {
  TaskResultCheckProcesses() : super.success(null);
}