Index: lib/src/backend/metadata.dart |
diff --git a/lib/src/backend/metadata.dart b/lib/src/backend/metadata.dart |
index b47201cc0ebfd7df63e17193a9679f6fe366e709..6a5133c43b7be695ff1fbbc2722e54b73f0dbca3 100644 |
--- a/lib/src/backend/metadata.dart |
+++ b/lib/src/backend/metadata.dart |
@@ -42,6 +42,16 @@ class Metadata { |
/// metadata for that platform. These can be applied by calling [forPlatform]. |
final Map<PlatformSelector, Metadata> onPlatform; |
+ /// Metadata that applies only when specific tags are applied. |
+ /// |
+ /// Tag-specific metadata is applied when merging this with other metadata. |
+ /// Note that unlike [onPlatform], the base metadata takes precedence over any |
+ /// tag-specific metadata. |
+ /// |
+ /// This is guaranteed not to have any keys that appear in [tags]; those are |
+ /// resolved when the metadata is constructed. |
+ final Map<String, Metadata> forTag; |
+ |
/// Parses a user-provided map into the value for [onPlatform]. |
static Map<PlatformSelector, Metadata> _parseOnPlatform( |
Map<String, dynamic> onPlatform) { |
@@ -107,18 +117,66 @@ class Metadata { |
/// Creates new Metadata. |
/// |
/// [testOn] defaults to [PlatformSelector.all]. |
- Metadata({PlatformSelector testOn, Timeout timeout, bool skip: false, |
- this.verboseTrace: false, this.skipReason, |
- Map<PlatformSelector, Metadata> onPlatform, Iterable<String> tags}) |
+ /// |
+ /// If [forTag] contains metadata for any tags in [tags], that metadata is |
+ /// included inline in the returned value. The values directly passed to the |
+ /// constructor take precedence over tag-specific metadata. |
+ factory Metadata({PlatformSelector testOn, Timeout timeout, bool skip: false, |
+ bool verboseTrace: false, String skipReason, Iterable<String> tags, |
+ Map<PlatformSelector, Metadata> onPlatform, |
+ Map<String, Metadata> forTag}) { |
+ // If there's no tag-specific metadata, or if none of it applies, just |
+ // return the metadata as-is. |
+ if (forTag == null || tags == null || !tags.any(forTag.containsKey)) { |
+ return new Metadata._( |
+ testOn: testOn, |
+ timeout: timeout, |
+ skip: skip, |
+ verboseTrace: verboseTrace, |
+ skipReason: skipReason, |
+ tags: tags, |
+ onPlatform: onPlatform, |
+ forTag: forTag); |
+ } |
+ |
+ // Otherwise, resolve the tag-specific components. Doing this eagerly means |
+ // we only have to resolve suite- or group-level tags once, rather than |
+ // doing it for every test individually. |
+ forTag = new Map.from(forTag); |
+ var merged = tags.fold(new Metadata._(), (merged, tag) { |
+ var tagMetadata = forTag.remove(tag); |
+ return tagMetadata == null ? merged : merged.merge(tagMetadata); |
+ }); |
+ |
+ return merged.merge(new Metadata._( |
+ testOn: testOn, |
+ timeout: timeout, |
+ skip: skip, |
+ verboseTrace: verboseTrace, |
+ skipReason: skipReason, |
+ tags: tags, |
+ onPlatform: onPlatform, |
+ forTag: forTag)); |
+ } |
+ |
+ /// Creates new Metadata. |
+ /// |
+ /// Unlike [new Metadata], this assumes [forTag] is already resolved. |
+ Metadata._({PlatformSelector testOn, Timeout timeout, bool skip: false, |
+ this.verboseTrace: false, this.skipReason, Iterable<String> tags, |
+ Map<PlatformSelector, Metadata> onPlatform, |
+ Map<String, Metadata> forTag}) |
: testOn = testOn == null ? PlatformSelector.all : testOn, |
timeout = timeout == null ? const Timeout.factor(1) : timeout, |
skip = skip, |
+ tags = new UnmodifiableSetView( |
+ tags == null ? new Set() : tags.toSet()), |
onPlatform = onPlatform == null |
? const {} |
: new UnmodifiableMapView(onPlatform), |
- tags = tags == null |
- ? new Set() |
- : new UnmodifiableSetView(tags.toSet()) { |
+ forTag = forTag == null |
+ ? const {} |
+ : new UnmodifiableMapView(forTag) { |
_validateTags(); |
} |
@@ -136,7 +194,8 @@ class Metadata { |
skip = skip != null && skip != false, |
skipReason = skip is String ? skip : null, |
onPlatform = _parseOnPlatform(onPlatform), |
- tags = _parseTags(tags) { |
+ tags = _parseTags(tags), |
+ forTag = const {} { |
if (skip != null && skip is! String && skip is! bool) { |
throw new ArgumentError( |
'"skip" must be a String or a bool, was "$skip".'); |
@@ -157,7 +216,9 @@ class Metadata { |
tags = new Set.from(serialized['tags']), |
onPlatform = new Map.fromIterable(serialized['onPlatform'], |
key: (pair) => new PlatformSelector.parse(pair.first), |
- value: (pair) => new Metadata.deserialize(pair.last)); |
+ value: (pair) => new Metadata.deserialize(pair.last)), |
+ forTag = mapMap(serialized['forTag'], |
+ value: (_, nested) => new Metadata.deserialize(nested)); |
/// Deserializes timeout from the format returned by [_serializeTimeout]. |
static _deserializeTimeout(serialized) { |
@@ -186,16 +247,21 @@ class Metadata { |
/// Return a new [Metadata] that merges [this] with [other]. |
/// |
- /// If the two [Metadata]s have conflicting properties, [other] wins. |
+ /// If the two [Metadata]s have conflicting properties, [other] wins. If |
+ /// either has a [forTag] metadata for one of the other's tags, that metadata |
+ /// is merged as well. |
Metadata merge(Metadata other) => |
new Metadata( |
testOn: testOn.intersect(other.testOn), |
timeout: timeout.merge(other.timeout), |
skip: skip || other.skip, |
- verboseTrace: verboseTrace || other.verboseTrace, |
skipReason: other.skipReason == null ? skipReason : other.skipReason, |
- onPlatform: mergeMaps(onPlatform, other.onPlatform), |
- tags: tags.union(other.tags)); |
+ verboseTrace: verboseTrace || other.verboseTrace, |
+ tags: tags.union(other.tags), |
+ onPlatform: mergeMaps(onPlatform, other.onPlatform, |
+ value: (metadata1, metadata2) => metadata1.merge(metadata2)), |
+ forTag: mergeMaps(forTag, other.forTag, |
+ value: (metadata1, metadata2) => metadata1.merge(metadata2))); |
/// Returns a copy of [this] with the given fields changed. |
Metadata change({PlatformSelector testOn, Timeout timeout, bool skip, |
@@ -240,8 +306,9 @@ class Metadata { |
'skip': skip, |
'skipReason': skipReason, |
'verboseTrace': verboseTrace, |
+ 'tags': tags.toList(), |
'onPlatform': serializedOnPlatform, |
- 'tags': tags.toList() |
+ 'forTag': mapMap(forTag, value: (_, metadata) => metadata.serialize()) |
}; |
} |