OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 library test.backend.metadata; |
| 6 |
| 7 import 'dart:collection'; |
| 8 |
| 9 import '../backend/operating_system.dart'; |
| 10 import '../backend/test_platform.dart'; |
| 11 import '../frontend/skip.dart'; |
| 12 import '../frontend/timeout.dart'; |
| 13 import '../utils.dart'; |
| 14 import 'platform_selector.dart'; |
| 15 |
| 16 /// Metadata for a test or test suite. |
| 17 /// |
| 18 /// This metadata comes from declarations on the test itself; it doesn't include |
| 19 /// configuration from the user. |
| 20 class Metadata { |
| 21 /// The selector indicating which platforms the suite supports. |
| 22 final PlatformSelector testOn; |
| 23 |
| 24 /// The modification to the timeout for the test or suite. |
| 25 final Timeout timeout; |
| 26 |
| 27 /// Whether the test or suite should be skipped. |
| 28 final bool skip; |
| 29 |
| 30 /// Whether to use verbose stack traces. |
| 31 final bool verboseTrace; |
| 32 |
| 33 /// The reason the test or suite should be skipped, if given. |
| 34 final String skipReason; |
| 35 |
| 36 /// Platform-specific metadata. |
| 37 /// |
| 38 /// Each key identifies a platform, and its value identifies the specific |
| 39 /// metadata for that platform. These can be applied by calling [forPlatform]. |
| 40 final Map<PlatformSelector, Metadata> onPlatform; |
| 41 |
| 42 /// Parses a user-provided map into the value for [onPlatform]. |
| 43 static Map<PlatformSelector, Metadata> _parseOnPlatform( |
| 44 Map<String, dynamic> onPlatform) { |
| 45 if (onPlatform == null) return {}; |
| 46 |
| 47 var result = {}; |
| 48 onPlatform.forEach((platform, metadata) { |
| 49 if (metadata is Timeout || metadata is Skip) { |
| 50 metadata = [metadata]; |
| 51 } else if (metadata is! List) { |
| 52 throw new ArgumentError('Metadata for platform "$platform" must be a ' |
| 53 'Timeout, Skip, or List of those; was "$metadata".'); |
| 54 } |
| 55 |
| 56 var selector = new PlatformSelector.parse(platform); |
| 57 |
| 58 var timeout; |
| 59 var skip; |
| 60 for (var metadatum in metadata) { |
| 61 if (metadatum is Timeout) { |
| 62 if (timeout != null) { |
| 63 throw new ArgumentError('Only a single Timeout may be declared for ' |
| 64 '"$platform".'); |
| 65 } |
| 66 |
| 67 timeout = metadatum; |
| 68 } else if (metadatum is Skip) { |
| 69 if (skip != null) { |
| 70 throw new ArgumentError('Only a single Skip may be declared for ' |
| 71 '"$platform".'); |
| 72 } |
| 73 |
| 74 skip = metadatum.reason == null ? true : metadatum.reason; |
| 75 } else { |
| 76 throw new ArgumentError('Metadata for platform "$platform" must be a ' |
| 77 'Timeout, Skip, or List of those; was "$metadata".'); |
| 78 } |
| 79 } |
| 80 |
| 81 result[selector] = new Metadata.parse(timeout: timeout, skip: skip); |
| 82 }); |
| 83 return result; |
| 84 } |
| 85 |
| 86 /// Creates new Metadata. |
| 87 /// |
| 88 /// [testOn] defaults to [PlatformSelector.all]. |
| 89 Metadata({PlatformSelector testOn, Timeout timeout, bool skip: false, |
| 90 this.verboseTrace: false, this.skipReason, |
| 91 Map<PlatformSelector, Metadata> onPlatform}) |
| 92 : testOn = testOn == null ? PlatformSelector.all : testOn, |
| 93 timeout = timeout == null ? const Timeout.factor(1) : timeout, |
| 94 skip = skip, |
| 95 onPlatform = onPlatform == null |
| 96 ? const {} |
| 97 : new UnmodifiableMapView(onPlatform); |
| 98 |
| 99 /// Creates a new Metadata, but with fields parsed from caller-friendly values |
| 100 /// where applicable. |
| 101 /// |
| 102 /// Throws a [FormatException] if any field is invalid. |
| 103 Metadata.parse({String testOn, Timeout timeout, skip, |
| 104 this.verboseTrace: false, Map<String, dynamic> onPlatform}) |
| 105 : testOn = testOn == null |
| 106 ? PlatformSelector.all |
| 107 : new PlatformSelector.parse(testOn), |
| 108 timeout = timeout == null ? const Timeout.factor(1) : timeout, |
| 109 skip = skip != null && skip != false, |
| 110 skipReason = skip is String ? skip : null, |
| 111 onPlatform = _parseOnPlatform(onPlatform) { |
| 112 if (skip != null && skip is! String && skip is! bool) { |
| 113 throw new ArgumentError( |
| 114 '"skip" must be a String or a bool, was "$skip".'); |
| 115 } |
| 116 } |
| 117 |
| 118 /// Dezerializes the result of [Metadata.serialize] into a new [Metadata]. |
| 119 Metadata.deserialize(serialized) |
| 120 : testOn = serialized['testOn'] == null |
| 121 ? PlatformSelector.all |
| 122 : new PlatformSelector.parse(serialized['testOn']), |
| 123 timeout = _deserializeTimeout(serialized['timeout']), |
| 124 skip = serialized['skip'], |
| 125 skipReason = serialized['skipReason'], |
| 126 verboseTrace = serialized['verboseTrace'], |
| 127 onPlatform = new Map.fromIterable(serialized['onPlatform'], |
| 128 key: (pair) => new PlatformSelector.parse(pair.first), |
| 129 value: (pair) => new Metadata.deserialize(pair.last)); |
| 130 |
| 131 /// Deserializes timeout from the format returned by [_serializeTimeout]. |
| 132 static _deserializeTimeout(serialized) { |
| 133 if (serialized == 'none') return Timeout.none; |
| 134 var scaleFactor = serialized['scaleFactor']; |
| 135 if (scaleFactor != null) return new Timeout.factor(scaleFactor); |
| 136 return new Timeout( |
| 137 new Duration(microseconds: serialized['duration'])); |
| 138 } |
| 139 |
| 140 /// Return a new [Metadata] that merges [this] with [other]. |
| 141 /// |
| 142 /// If the two [Metadata]s have conflicting properties, [other] wins. |
| 143 Metadata merge(Metadata other) => |
| 144 new Metadata( |
| 145 testOn: testOn.intersect(other.testOn), |
| 146 timeout: timeout.merge(other.timeout), |
| 147 skip: skip || other.skip, |
| 148 verboseTrace: verboseTrace || other.verboseTrace, |
| 149 skipReason: other.skipReason == null ? skipReason : other.skipReason, |
| 150 onPlatform: mergeMaps(onPlatform, other.onPlatform)); |
| 151 |
| 152 /// Returns a copy of [this] with the given fields changed. |
| 153 Metadata change({PlatformSelector testOn, Timeout timeout, bool skip, |
| 154 bool verboseTrace, String skipReason, |
| 155 Map<PlatformSelector, Metadata> onPlatform}) { |
| 156 if (testOn == null) testOn = this.testOn; |
| 157 if (timeout == null) timeout = this.timeout; |
| 158 if (skip == null) skip = this.skip; |
| 159 if (verboseTrace == null) verboseTrace = this.verboseTrace; |
| 160 if (skipReason == null) skipReason = this.skipReason; |
| 161 if (onPlatform == null) onPlatform = this.onPlatform; |
| 162 return new Metadata(testOn: testOn, timeout: timeout, skip: skip, |
| 163 verboseTrace: verboseTrace, skipReason: skipReason, |
| 164 onPlatform: onPlatform); |
| 165 } |
| 166 |
| 167 /// Returns a copy of [this] with all platform-specific metadata from |
| 168 /// [onPlatform] resolved. |
| 169 Metadata forPlatform(TestPlatform platform, {OperatingSystem os}) { |
| 170 if (onPlatform.isEmpty) return this; |
| 171 |
| 172 var metadata = this; |
| 173 onPlatform.forEach((platformSelector, platformMetadata) { |
| 174 if (!platformSelector.evaluate(platform, os: os)) return; |
| 175 metadata = metadata.merge(platformMetadata); |
| 176 }); |
| 177 return metadata.change(onPlatform: {}); |
| 178 } |
| 179 |
| 180 /// Serializes [this] into a JSON-safe object that can be deserialized using |
| 181 /// [new Metadata.deserialize]. |
| 182 serialize() { |
| 183 // Make this a list to guarantee that the order is preserved. |
| 184 var serializedOnPlatform = []; |
| 185 onPlatform.forEach((key, value) { |
| 186 serializedOnPlatform.add([key.toString(), value.serialize()]); |
| 187 }); |
| 188 |
| 189 return { |
| 190 'testOn': testOn == PlatformSelector.all ? null : testOn.toString(), |
| 191 'timeout': _serializeTimeout(timeout), |
| 192 'skip': skip, |
| 193 'skipReason': skipReason, |
| 194 'verboseTrace': verboseTrace, |
| 195 'onPlatform': serializedOnPlatform |
| 196 }; |
| 197 } |
| 198 |
| 199 /// Serializes timeout into a JSON-safe object. |
| 200 _serializeTimeout(Timeout timeout) { |
| 201 if (timeout == Timeout.none) return 'none'; |
| 202 return { |
| 203 'duration': timeout.duration == null |
| 204 ? null |
| 205 : timeout.duration.inMicroseconds, |
| 206 'scaleFactor': timeout.scaleFactor |
| 207 }; |
| 208 } |
| 209 } |
OLD | NEW |