| OLD | NEW | 
|---|
| 1 // Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. | 
| 4 | 4 | 
| 5 import 'dart:collection'; | 5 import 'dart:collection'; | 
| 6 | 6 | 
| 7 import 'package:collection/collection.dart'; | 7 import 'package:collection/collection.dart'; | 
| 8 | 8 | 
| 9 import '../frontend/skip.dart'; | 9 import '../frontend/skip.dart'; | 
| 10 import '../frontend/timeout.dart'; | 10 import '../frontend/timeout.dart'; | 
| (...skipping 24 matching lines...) Expand all  Loading... | 
| 35 | 35 | 
| 36   /// The user-defined tags attached to the test or suite. | 36   /// The user-defined tags attached to the test or suite. | 
| 37   final Set<String> tags; | 37   final Set<String> tags; | 
| 38 | 38 | 
| 39   /// Platform-specific metadata. | 39   /// Platform-specific metadata. | 
| 40   /// | 40   /// | 
| 41   /// Each key identifies a platform, and its value identifies the specific | 41   /// Each key identifies a platform, and its value identifies the specific | 
| 42   /// metadata for that platform. These can be applied by calling [forPlatform]. | 42   /// metadata for that platform. These can be applied by calling [forPlatform]. | 
| 43   final Map<PlatformSelector, Metadata> onPlatform; | 43   final Map<PlatformSelector, Metadata> onPlatform; | 
| 44 | 44 | 
|  | 45   /// Metadata that applies only when specific tags are applied. | 
|  | 46   /// | 
|  | 47   /// Tag-specific metadata is applied when merging this with other metadata. | 
|  | 48   /// Note that unlike [onPlatform], the base metadata takes precedence over any | 
|  | 49   /// tag-specific metadata. | 
|  | 50   /// | 
|  | 51   /// This is guaranteed not to have any keys that appear in [tags]; those are | 
|  | 52   /// resolved when the metadata is constructed. | 
|  | 53   final Map<String, Metadata> forTag; | 
|  | 54 | 
| 45   /// Parses a user-provided map into the value for [onPlatform]. | 55   /// Parses a user-provided map into the value for [onPlatform]. | 
| 46   static Map<PlatformSelector, Metadata> _parseOnPlatform( | 56   static Map<PlatformSelector, Metadata> _parseOnPlatform( | 
| 47       Map<String, dynamic> onPlatform) { | 57       Map<String, dynamic> onPlatform) { | 
| 48     if (onPlatform == null) return {}; | 58     if (onPlatform == null) return {}; | 
| 49 | 59 | 
| 50     var result = {}; | 60     var result = {}; | 
| 51     onPlatform.forEach((platform, metadata) { | 61     onPlatform.forEach((platform, metadata) { | 
| 52       if (metadata is Timeout || metadata is Skip) { | 62       if (metadata is Timeout || metadata is Skip) { | 
| 53         metadata = [metadata]; | 63         metadata = [metadata]; | 
| 54       } else if (metadata is! List) { | 64       } else if (metadata is! List) { | 
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 100     if (tags.any((tag) => tag is! String)) { | 110     if (tags.any((tag) => tag is! String)) { | 
| 101       throw new ArgumentError.value(tags, "tags", "must contain only Strings."); | 111       throw new ArgumentError.value(tags, "tags", "must contain only Strings."); | 
| 102     } | 112     } | 
| 103 | 113 | 
| 104     return new Set.from(tags); | 114     return new Set.from(tags); | 
| 105   } | 115   } | 
| 106 | 116 | 
| 107   /// Creates new Metadata. | 117   /// Creates new Metadata. | 
| 108   /// | 118   /// | 
| 109   /// [testOn] defaults to [PlatformSelector.all]. | 119   /// [testOn] defaults to [PlatformSelector.all]. | 
| 110   Metadata({PlatformSelector testOn, Timeout timeout, bool skip: false, | 120   /// | 
| 111           this.verboseTrace: false, this.skipReason, | 121   /// If [forTag] contains metadata for any tags in [tags], that metadata is | 
| 112           Map<PlatformSelector, Metadata> onPlatform, Iterable<String> tags}) | 122   /// included inline in the returned value. The values directly passed to the | 
|  | 123   /// constructor take precedence over tag-specific metadata. | 
|  | 124   factory Metadata({PlatformSelector testOn, Timeout timeout, bool skip: false, | 
|  | 125           bool verboseTrace: false, String skipReason, Iterable<String> tags, | 
|  | 126           Map<PlatformSelector, Metadata> onPlatform, | 
|  | 127           Map<String, Metadata> forTag}) { | 
|  | 128     // If there's no tag-specific metadata, or if none of it applies, just | 
|  | 129     // return the metadata as-is. | 
|  | 130     if (forTag == null || tags == null || !tags.any(forTag.containsKey)) { | 
|  | 131       return new Metadata._( | 
|  | 132           testOn: testOn, | 
|  | 133           timeout: timeout, | 
|  | 134           skip: skip, | 
|  | 135           verboseTrace: verboseTrace, | 
|  | 136           skipReason: skipReason, | 
|  | 137           tags: tags, | 
|  | 138           onPlatform: onPlatform, | 
|  | 139           forTag: forTag); | 
|  | 140     } | 
|  | 141 | 
|  | 142     // Otherwise, resolve the tag-specific components. Doing this eagerly means | 
|  | 143     // we only have to resolve suite- or group-level tags once, rather than | 
|  | 144     // doing it for every test individually. | 
|  | 145     forTag = new Map.from(forTag); | 
|  | 146     var merged = tags.fold(new Metadata._(), (merged, tag) { | 
|  | 147       var tagMetadata = forTag.remove(tag); | 
|  | 148       return tagMetadata == null ? merged : merged.merge(tagMetadata); | 
|  | 149     }); | 
|  | 150 | 
|  | 151     return merged.merge(new Metadata._( | 
|  | 152         testOn: testOn, | 
|  | 153         timeout: timeout, | 
|  | 154         skip: skip, | 
|  | 155         verboseTrace: verboseTrace, | 
|  | 156         skipReason: skipReason, | 
|  | 157         tags: tags, | 
|  | 158         onPlatform: onPlatform, | 
|  | 159         forTag: forTag)); | 
|  | 160   } | 
|  | 161 | 
|  | 162   /// Creates new Metadata. | 
|  | 163   /// | 
|  | 164   /// Unlike [new Metadata], this assumes [forTag] is already resolved. | 
|  | 165   Metadata._({PlatformSelector testOn, Timeout timeout, bool skip: false, | 
|  | 166           this.verboseTrace: false, this.skipReason, Iterable<String> tags, | 
|  | 167           Map<PlatformSelector, Metadata> onPlatform, | 
|  | 168           Map<String, Metadata> forTag}) | 
| 113       : testOn = testOn == null ? PlatformSelector.all : testOn, | 169       : testOn = testOn == null ? PlatformSelector.all : testOn, | 
| 114         timeout = timeout == null ? const Timeout.factor(1) : timeout, | 170         timeout = timeout == null ? const Timeout.factor(1) : timeout, | 
| 115         skip = skip, | 171         skip = skip, | 
|  | 172         tags = new UnmodifiableSetView( | 
|  | 173             tags == null ? new Set() : tags.toSet()), | 
| 116         onPlatform = onPlatform == null | 174         onPlatform = onPlatform == null | 
| 117             ? const {} | 175             ? const {} | 
| 118             : new UnmodifiableMapView(onPlatform), | 176             : new UnmodifiableMapView(onPlatform), | 
| 119         tags = tags == null | 177         forTag = forTag == null | 
| 120             ? new Set() | 178             ? const {} | 
| 121             : new UnmodifiableSetView(tags.toSet()) { | 179             : new UnmodifiableMapView(forTag) { | 
| 122     _validateTags(); | 180     _validateTags(); | 
| 123   } | 181   } | 
| 124 | 182 | 
| 125   /// Creates a new Metadata, but with fields parsed from caller-friendly values | 183   /// Creates a new Metadata, but with fields parsed from caller-friendly values | 
| 126   /// where applicable. | 184   /// where applicable. | 
| 127   /// | 185   /// | 
| 128   /// Throws a [FormatException] if any field is invalid. | 186   /// Throws a [FormatException] if any field is invalid. | 
| 129   Metadata.parse({String testOn, Timeout timeout, skip, | 187   Metadata.parse({String testOn, Timeout timeout, skip, | 
| 130           this.verboseTrace: false, Map<String, dynamic> onPlatform, | 188           this.verboseTrace: false, Map<String, dynamic> onPlatform, | 
| 131           tags}) | 189           tags}) | 
| 132       : testOn = testOn == null | 190       : testOn = testOn == null | 
| 133             ? PlatformSelector.all | 191             ? PlatformSelector.all | 
| 134             : new PlatformSelector.parse(testOn), | 192             : new PlatformSelector.parse(testOn), | 
| 135         timeout = timeout == null ? const Timeout.factor(1) : timeout, | 193         timeout = timeout == null ? const Timeout.factor(1) : timeout, | 
| 136         skip = skip != null && skip != false, | 194         skip = skip != null && skip != false, | 
| 137         skipReason = skip is String ? skip : null, | 195         skipReason = skip is String ? skip : null, | 
| 138         onPlatform = _parseOnPlatform(onPlatform), | 196         onPlatform = _parseOnPlatform(onPlatform), | 
| 139         tags = _parseTags(tags) { | 197         tags = _parseTags(tags), | 
|  | 198         forTag = const {} { | 
| 140     if (skip != null && skip is! String && skip is! bool) { | 199     if (skip != null && skip is! String && skip is! bool) { | 
| 141       throw new ArgumentError( | 200       throw new ArgumentError( | 
| 142           '"skip" must be a String or a bool, was "$skip".'); | 201           '"skip" must be a String or a bool, was "$skip".'); | 
| 143     } | 202     } | 
| 144 | 203 | 
| 145     _validateTags(); | 204     _validateTags(); | 
| 146   } | 205   } | 
| 147 | 206 | 
| 148   /// Deserializes the result of [Metadata.serialize] into a new [Metadata]. | 207   /// Deserializes the result of [Metadata.serialize] into a new [Metadata]. | 
| 149   Metadata.deserialize(serialized) | 208   Metadata.deserialize(serialized) | 
| 150       : testOn = serialized['testOn'] == null | 209       : testOn = serialized['testOn'] == null | 
| 151             ? PlatformSelector.all | 210             ? PlatformSelector.all | 
| 152             : new PlatformSelector.parse(serialized['testOn']), | 211             : new PlatformSelector.parse(serialized['testOn']), | 
| 153         timeout = _deserializeTimeout(serialized['timeout']), | 212         timeout = _deserializeTimeout(serialized['timeout']), | 
| 154         skip = serialized['skip'], | 213         skip = serialized['skip'], | 
| 155         skipReason = serialized['skipReason'], | 214         skipReason = serialized['skipReason'], | 
| 156         verboseTrace = serialized['verboseTrace'], | 215         verboseTrace = serialized['verboseTrace'], | 
| 157         tags = new Set.from(serialized['tags']), | 216         tags = new Set.from(serialized['tags']), | 
| 158         onPlatform = new Map.fromIterable(serialized['onPlatform'], | 217         onPlatform = new Map.fromIterable(serialized['onPlatform'], | 
| 159             key: (pair) => new PlatformSelector.parse(pair.first), | 218             key: (pair) => new PlatformSelector.parse(pair.first), | 
| 160             value: (pair) => new Metadata.deserialize(pair.last)); | 219             value: (pair) => new Metadata.deserialize(pair.last)), | 
|  | 220         forTag = mapMap(serialized['forTag'], | 
|  | 221             value: (_, nested) => new Metadata.deserialize(nested)); | 
| 161 | 222 | 
| 162   /// Deserializes timeout from the format returned by [_serializeTimeout]. | 223   /// Deserializes timeout from the format returned by [_serializeTimeout]. | 
| 163   static _deserializeTimeout(serialized) { | 224   static _deserializeTimeout(serialized) { | 
| 164     if (serialized == 'none') return Timeout.none; | 225     if (serialized == 'none') return Timeout.none; | 
| 165     var scaleFactor = serialized['scaleFactor']; | 226     var scaleFactor = serialized['scaleFactor']; | 
| 166     if (scaleFactor != null) return new Timeout.factor(scaleFactor); | 227     if (scaleFactor != null) return new Timeout.factor(scaleFactor); | 
| 167     return new Timeout( | 228     return new Timeout( | 
| 168         new Duration(microseconds: serialized['duration'])); | 229         new Duration(microseconds: serialized['duration'])); | 
| 169   } | 230   } | 
| 170 | 231 | 
| 171   /// Throws an [ArgumentError] if any tags in [tags] aren't hyphenated | 232   /// Throws an [ArgumentError] if any tags in [tags] aren't hyphenated | 
| 172   /// identifiers. | 233   /// identifiers. | 
| 173   void _validateTags() { | 234   void _validateTags() { | 
| 174     var invalidTags = tags | 235     var invalidTags = tags | 
| 175         .where((tag) => !tag.contains(anchoredHyphenatedIdentifier)) | 236         .where((tag) => !tag.contains(anchoredHyphenatedIdentifier)) | 
| 176         .map((tag) => '"$tag"') | 237         .map((tag) => '"$tag"') | 
| 177         .toList(); | 238         .toList(); | 
| 178 | 239 | 
| 179     if (invalidTags.isEmpty) return; | 240     if (invalidTags.isEmpty) return; | 
| 180 | 241 | 
| 181     throw new ArgumentError( | 242     throw new ArgumentError( | 
| 182         "Invalid ${pluralize('tag', invalidTags.length)} " | 243         "Invalid ${pluralize('tag', invalidTags.length)} " | 
| 183           "${toSentence(invalidTags)}. Tags must be (optionally hyphenated) " | 244           "${toSentence(invalidTags)}. Tags must be (optionally hyphenated) " | 
| 184           "Dart identifiers."); | 245           "Dart identifiers."); | 
| 185   } | 246   } | 
| 186 | 247 | 
| 187   /// Return a new [Metadata] that merges [this] with [other]. | 248   /// Return a new [Metadata] that merges [this] with [other]. | 
| 188   /// | 249   /// | 
| 189   /// If the two [Metadata]s have conflicting properties, [other] wins. | 250   /// If the two [Metadata]s have conflicting properties, [other] wins. If | 
|  | 251   /// either has a [forTag] metadata for one of the other's tags, that metadata | 
|  | 252   /// is merged as well. | 
| 190   Metadata merge(Metadata other) => | 253   Metadata merge(Metadata other) => | 
| 191       new Metadata( | 254       new Metadata( | 
| 192           testOn: testOn.intersect(other.testOn), | 255           testOn: testOn.intersect(other.testOn), | 
| 193           timeout: timeout.merge(other.timeout), | 256           timeout: timeout.merge(other.timeout), | 
| 194           skip: skip || other.skip, | 257           skip: skip || other.skip, | 
|  | 258           skipReason: other.skipReason == null ? skipReason : other.skipReason, | 
| 195           verboseTrace: verboseTrace || other.verboseTrace, | 259           verboseTrace: verboseTrace || other.verboseTrace, | 
| 196           skipReason: other.skipReason == null ? skipReason : other.skipReason, | 260           tags: tags.union(other.tags), | 
| 197           onPlatform: mergeMaps(onPlatform, other.onPlatform), | 261           onPlatform: mergeMaps(onPlatform, other.onPlatform, | 
| 198           tags: tags.union(other.tags)); | 262               value: (metadata1, metadata2) => metadata1.merge(metadata2)), | 
|  | 263           forTag: mergeMaps(forTag, other.forTag, | 
|  | 264               value: (metadata1, metadata2) => metadata1.merge(metadata2))); | 
| 199 | 265 | 
| 200   /// Returns a copy of [this] with the given fields changed. | 266   /// Returns a copy of [this] with the given fields changed. | 
| 201   Metadata change({PlatformSelector testOn, Timeout timeout, bool skip, | 267   Metadata change({PlatformSelector testOn, Timeout timeout, bool skip, | 
| 202       bool verboseTrace, String skipReason, | 268       bool verboseTrace, String skipReason, | 
| 203       Map<PlatformSelector, Metadata> onPlatform}) { | 269       Map<PlatformSelector, Metadata> onPlatform}) { | 
| 204     if (testOn == null) testOn = this.testOn; | 270     if (testOn == null) testOn = this.testOn; | 
| 205     if (timeout == null) timeout = this.timeout; | 271     if (timeout == null) timeout = this.timeout; | 
| 206     if (skip == null) skip = this.skip; | 272     if (skip == null) skip = this.skip; | 
| 207     if (verboseTrace == null) verboseTrace = this.verboseTrace; | 273     if (verboseTrace == null) verboseTrace = this.verboseTrace; | 
| 208     if (skipReason == null) skipReason = this.skipReason; | 274     if (skipReason == null) skipReason = this.skipReason; | 
| (...skipping 24 matching lines...) Expand all  Loading... | 
| 233     onPlatform.forEach((key, value) { | 299     onPlatform.forEach((key, value) { | 
| 234       serializedOnPlatform.add([key.toString(), value.serialize()]); | 300       serializedOnPlatform.add([key.toString(), value.serialize()]); | 
| 235     }); | 301     }); | 
| 236 | 302 | 
| 237     return { | 303     return { | 
| 238       'testOn': testOn == PlatformSelector.all ? null : testOn.toString(), | 304       'testOn': testOn == PlatformSelector.all ? null : testOn.toString(), | 
| 239       'timeout': _serializeTimeout(timeout), | 305       'timeout': _serializeTimeout(timeout), | 
| 240       'skip': skip, | 306       'skip': skip, | 
| 241       'skipReason': skipReason, | 307       'skipReason': skipReason, | 
| 242       'verboseTrace': verboseTrace, | 308       'verboseTrace': verboseTrace, | 
|  | 309       'tags': tags.toList(), | 
| 243       'onPlatform': serializedOnPlatform, | 310       'onPlatform': serializedOnPlatform, | 
| 244       'tags': tags.toList() | 311       'forTag': mapMap(forTag, value: (_, metadata) => metadata.serialize()) | 
| 245     }; | 312     }; | 
| 246   } | 313   } | 
| 247 | 314 | 
| 248   /// Serializes timeout into a JSON-safe object. | 315   /// Serializes timeout into a JSON-safe object. | 
| 249   _serializeTimeout(Timeout timeout) { | 316   _serializeTimeout(Timeout timeout) { | 
| 250     if (timeout == Timeout.none) return 'none'; | 317     if (timeout == Timeout.none) return 'none'; | 
| 251     return { | 318     return { | 
| 252       'duration': timeout.duration == null | 319       'duration': timeout.duration == null | 
| 253           ? null | 320           ? null | 
| 254           : timeout.duration.inMicroseconds, | 321           : timeout.duration.inMicroseconds, | 
| 255       'scaleFactor': timeout.scaleFactor | 322       'scaleFactor': timeout.scaleFactor | 
| 256     }; | 323     }; | 
| 257   } | 324   } | 
| 258 } | 325 } | 
| OLD | NEW | 
|---|