Index: lib/src/backend/metadata.dart |
diff --git a/lib/src/backend/metadata.dart b/lib/src/backend/metadata.dart |
index d3991e23e884d7b66478f46bf9d974797ffe70c2..b8e6c0c6ace884bc0e707d870e6c8f775291b599 100644 |
--- a/lib/src/backend/metadata.dart |
+++ b/lib/src/backend/metadata.dart |
@@ -34,7 +34,7 @@ class Metadata { |
final String skipReason; |
/// A set of tags attached to a test |
- final List<String> tags; |
+ final Set<String> tags; |
/// Platform-specific metadata. |
/// |
@@ -91,14 +91,14 @@ class Metadata { |
/// [testOn] defaults to [PlatformSelector.all]. |
Metadata({PlatformSelector testOn, Timeout timeout, bool skip: false, |
this.verboseTrace: false, this.skipReason, |
- Map<PlatformSelector, Metadata> onPlatform, List<String> tags}) |
+ Map<PlatformSelector, Metadata> onPlatform, tags}) |
: testOn = testOn == null ? PlatformSelector.all : testOn, |
timeout = timeout == null ? const Timeout.factor(1) : timeout, |
skip = skip, |
onPlatform = onPlatform == null |
? const {} |
: new UnmodifiableMapView(onPlatform), |
- this.tags = tags ?? const <String>[]; |
+ this.tags = _makeSetOfTags(tags); |
nweiz
2015/11/03 00:43:16
The type-normalization should only happen in [new
yjbanov
2015/11/11 06:40:20
Done.
|
/// Creates a new Metadata, but with fields parsed from caller-friendly values |
/// where applicable. |
@@ -106,7 +106,7 @@ class Metadata { |
/// Throws a [FormatException] if any field is invalid. |
Metadata.parse({String testOn, Timeout timeout, skip, |
this.verboseTrace: false, Map<String, dynamic> onPlatform, |
- List<String> tags}) |
+ tags}) |
: testOn = testOn == null |
? PlatformSelector.all |
: new PlatformSelector.parse(testOn), |
@@ -114,14 +114,14 @@ class Metadata { |
skip = skip != null && skip != false, |
skipReason = skip is String ? skip : null, |
onPlatform = _parseOnPlatform(onPlatform), |
- this.tags = tags ?? const <String>[] { |
+ this.tags = _makeSetOfTags(tags) { |
if (skip != null && skip is! String && skip is! bool) { |
throw new ArgumentError( |
'"skip" must be a String or a bool, was "$skip".'); |
} |
} |
- /// Dezerializes the result of [Metadata.serialize] into a new [Metadata]. |
+ /// Deserializes the result of [Metadata.serialize] into a new [Metadata]. |
Metadata.deserialize(serialized) |
: testOn = serialized['testOn'] == null |
? PlatformSelector.all |
@@ -130,7 +130,7 @@ class Metadata { |
skip = serialized['skip'], |
skipReason = serialized['skipReason'], |
verboseTrace = serialized['verboseTrace'], |
- tags = serialized['tags'] ?? const <String>[], |
+ tags = _makeSetOfTags(serialized['tags']), |
onPlatform = new Map.fromIterable(serialized['onPlatform'], |
key: (pair) => new PlatformSelector.parse(pair.first), |
value: (pair) => new Metadata.deserialize(pair.last)); |
@@ -155,7 +155,7 @@ class Metadata { |
verboseTrace: verboseTrace || other.verboseTrace, |
skipReason: other.skipReason == null ? skipReason : other.skipReason, |
onPlatform: mergeMaps(onPlatform, other.onPlatform), |
- tags: mergeLists(tags, other.tags)); |
+ tags: tags.union(other.tags)); |
/// Returns a copy of [this] with the given fields changed. |
Metadata change({PlatformSelector testOn, Timeout timeout, bool skip, |
@@ -201,7 +201,7 @@ class Metadata { |
'skipReason': skipReason, |
'verboseTrace': verboseTrace, |
'onPlatform': serializedOnPlatform, |
- 'tags': tags.isEmpty ? null : tags, |
+ 'tags': tags.toList(), |
}; |
} |
@@ -216,3 +216,34 @@ class Metadata { |
}; |
} |
} |
+ |
+/// Converts [tags] to a [Set] of unique tags, unless it is already a [Set]. |
+/// |
+/// `null` becomes an empty set. [String] is split on commas. |
+Set<String> _makeSetOfTags(tags) => tags != null |
+ ? tags is Iterable |
+ ? _tagsFromIterable(tags) |
+ : tags is String |
+ ? _tagsFromString(tags) |
+ : throw new ArgumentError.value(tags, "tags", |
+ "must be either String or Iterable") |
+ : new Set<String>(); |
+ |
+Set<String> _tagsFromIterable(Iterable tags) { |
+ for (var tag in tags) { |
+ if (tag is! String) { |
+ throw new ArgumentError.value(tags, "tags", "tag name must be String"); |
+ } |
+ if (tag.trim().isEmpty) { |
nweiz
2015/11/03 00:43:16
I'd rather not trim here. If users want to use whi
yjbanov
2015/11/11 06:40:20
I thought it would be more practical given we acce
nweiz
2015/11/16 21:59:44
I'd like to keep the semantics on the Dart side in
yjbanov
2015/11/26 06:30:27
Done.
|
+ throw new ArgumentError.value(tags, "tags", |
+ "tag name must contain non-whitespace characters"); |
+ } |
+ } |
+ return tags is Set<String> ? tags : new Set<String>.from(tags); |
+} |
+ |
+Set<String> _tagsFromString(String tags) { |
+ if (tags.trim().isEmpty) return new Set<String>(); |
+ return _tagsFromIterable( |
+ tags.split(",").map((tag) => tag.trim()).where((tag) => tag.isNotEmpty)); |
nweiz
2015/11/03 00:43:16
I'd rather require users to use a real list if the
yjbanov
2015/11/11 06:40:20
I thought it would make it consistent with the com
yjbanov
2015/11/26 06:30:27
Done.
|
+} |