Index: lib/src/backend/metadata.dart |
diff --git a/lib/src/backend/metadata.dart b/lib/src/backend/metadata.dart |
index ecce4404601c0f59672c6a64e411d9409c09b7f8..0bcf5f880b66c6b7fb2facc0c30cd375cecdb110 100644 |
--- a/lib/src/backend/metadata.dart |
+++ b/lib/src/backend/metadata.dart |
@@ -4,7 +4,13 @@ |
library test.backend.metadata; |
+import 'dart:collection'; |
+ |
+import '../backend/operating_system.dart'; |
+import '../backend/test_platform.dart'; |
+import '../frontend/skip.dart'; |
import '../frontend/timeout.dart'; |
+import '../utils.dart'; |
import 'platform_selector.dart'; |
/// Metadata for a test or test suite. |
@@ -24,26 +30,81 @@ class Metadata { |
/// The reason the test or suite should be skipped, if given. |
final String skipReason; |
+ /// Platform-specific metadata. |
+ /// |
+ /// Each key identifies a platform, and its value identifies the specific |
+ /// metadata for that platform. These can be applied by calling [forPlatform]. |
+ final Map<PlatformSelector, Metadata> onPlatform; |
+ |
+ /// Parses a user-provided map into the value for [onPlatform]. |
+ static Map<PlatformSelector, Metadata> _parseOnPlatform( |
+ Map<String, dynamic> onPlatform) { |
+ if (onPlatform == null) return {}; |
+ |
+ var result = {}; |
+ onPlatform.forEach((platform, metadata) { |
+ if (metadata is Timeout || metadata is Skip) { |
+ metadata = [metadata]; |
+ } else if (metadata is! List) { |
+ throw new ArgumentError('Metadata for platform "$platform" must be a ' |
+ 'Timeout, Skip, or List of those; was "$metadata".'); |
+ } |
+ |
+ var selector = new PlatformSelector.parse(platform); |
+ |
+ var timeout; |
+ var skip; |
+ for (var metadatum in metadata) { |
+ if (metadatum is Timeout) { |
+ if (timeout != null) { |
+ throw new ArgumentError('Only a single Timeout may be declared for ' |
+ '"$platform".'); |
+ } |
+ |
+ timeout = metadatum; |
+ } else if (metadatum is Skip) { |
+ if (skip != null) { |
+ throw new ArgumentError('Only a single Skip may be declared for ' |
+ '"$platform".'); |
+ } |
+ |
+ skip = metadatum.reason == null ? true : metadatum.reason; |
+ } else { |
+ throw new ArgumentError('Metadata for platform "$platform" must be a ' |
+ 'Timeout, Skip, or List of those; was "$metadata".'); |
+ } |
+ } |
+ |
+ result[selector] = new Metadata.parse(timeout: timeout, skip: skip); |
+ }); |
+ return result; |
+ } |
+ |
/// Creates new Metadata. |
/// |
/// [testOn] defaults to [PlatformSelector.all]. |
Metadata({PlatformSelector testOn, Timeout timeout, bool skip: false, |
- this.skipReason}) |
+ this.skipReason, Map<PlatformSelector, Metadata> onPlatform}) |
: testOn = testOn == null ? PlatformSelector.all : testOn, |
timeout = timeout == null ? const Timeout.factor(1) : timeout, |
- skip = skip; |
+ skip = skip, |
+ onPlatform = onPlatform == null |
+ ? const {} |
+ : new UnmodifiableMapView(onPlatform); |
- /// Creates a new Metadata, but with fields parsed from strings where |
- /// applicable. |
+ /// Creates a new Metadata, but with fields parsed from caller-friendly values |
+ /// where applicable. |
/// |
/// Throws a [FormatException] if any field is invalid. |
- Metadata.parse({String testOn, Timeout timeout, skip}) |
+ Metadata.parse({String testOn, Timeout timeout, skip, |
+ Map<String, dynamic> onPlatform}) |
: testOn = testOn == null |
? PlatformSelector.all |
: new PlatformSelector.parse(testOn), |
timeout = timeout == null ? const Timeout.factor(1) : timeout, |
skip = skip != null && skip != false, |
- skipReason = skip is String ? skip : null { |
+ skipReason = skip is String ? skip : null, |
+ onPlatform = _parseOnPlatform(onPlatform) { |
if (skip != null && skip is! String && skip is! bool) { |
throw new ArgumentError( |
'"skip" must be a String or a bool, was "$skip".'); |
@@ -52,15 +113,18 @@ class Metadata { |
/// Dezerializes the result of [Metadata.serialize] into a new [Metadata]. |
Metadata.deserialize(serialized) |
- : this.parse( |
- testOn: serialized['testOn'], |
- timeout: serialized['timeout']['duration'] == null |
- ? new Timeout.factor(serialized['timeout']['scaleFactor']) |
- : new Timeout(new Duration( |
- microseconds: serialized['timeout']['duration'])), |
- skip: serialized['skipReason'] == null |
- ? serialized['skip'] |
- : serialized['skipReason']); |
+ : testOn = serialized['testOn'] == null |
+ ? PlatformSelector.all |
+ : new PlatformSelector.parse(serialized['testOn']), |
+ timeout = serialized['timeout']['duration'] == null |
+ ? new Timeout.factor(serialized['timeout']['scaleFactor']) |
+ : new Timeout(new Duration( |
+ microseconds: serialized['timeout']['duration'])), |
+ skip = serialized['skip'], |
+ skipReason = serialized['skipReason'], |
+ onPlatform = new Map.fromIterable(serialized['onPlatform'], |
+ key: (pair) => new PlatformSelector.parse(pair.first), |
+ value: (pair) => new Metadata.deserialize(pair.last)); |
/// Return a new [Metadata] that merges [this] with [other]. |
/// |
@@ -70,19 +134,52 @@ class Metadata { |
testOn: testOn.intersect(other.testOn), |
timeout: timeout.merge(other.timeout), |
skip: skip || other.skip, |
- skipReason: other.skipReason == null ? skipReason : other.skipReason); |
+ skipReason: other.skipReason == null ? skipReason : other.skipReason, |
+ onPlatform: mergeMaps(onPlatform, other.onPlatform)); |
+ |
+ /// Returns a copy of [this] with the given fields changed. |
+ Metadata change({PlatformSelector testOn, Timeout timeout, bool skip, |
+ String skipReason, Map<PlatformSelector, Metadata> onPlatform}) { |
+ if (testOn == null) testOn = this.testOn; |
+ if (timeout == null) timeout = this.timeout; |
+ if (skip == null) skip = this.skip; |
+ if (skipReason == null) skipReason = this.skipReason; |
+ if (onPlatform == null) onPlatform = this.onPlatform; |
+ return new Metadata(testOn: testOn, timeout: timeout, skip: skip, |
+ skipReason: skipReason, onPlatform: onPlatform); |
+ } |
+ |
+ /// Returns a copy of [this] with all platform-specific metadata from |
+ /// [onPlatform] resolved. |
+ Metadata forPlatform(TestPlatform platform, {OperatingSystem os}) { |
+ var metadata = this; |
+ onPlatform.forEach((platformSelector, platformMetadata) { |
+ if (!platformSelector.evaluate(platform, os: os)) return; |
+ metadata = metadata.merge(platformMetadata); |
+ }); |
+ return metadata.change(onPlatform: {}); |
+ } |
/// Serializes [this] into a JSON-safe object that can be deserialized using |
/// [new Metadata.deserialize]. |
- serialize() => { |
- 'testOn': testOn == PlatformSelector.all ? null : testOn.toString(), |
- 'timeout': { |
- 'duration': timeout.duration == null |
- ? null |
- : timeout.duration.inMicroseconds, |
- 'scaleFactor': timeout.scaleFactor |
- }, |
- 'skip': skip, |
- 'skipReason': skipReason |
- }; |
+ serialize() { |
+ // Make this a list to guarantee that the order is preserved. |
+ var serializedOnPlatform = []; |
+ onPlatform.forEach((key, value) { |
+ serializedOnPlatform.add([key.toString(), value.serialize()]); |
+ }); |
+ |
+ return { |
+ 'testOn': testOn == PlatformSelector.all ? null : testOn.toString(), |
+ 'timeout': { |
+ 'duration': timeout.duration == null |
+ ? null |
+ : timeout.duration.inMicroseconds, |
+ 'scaleFactor': timeout.scaleFactor |
+ }, |
+ 'skip': skip, |
+ 'skipReason': skipReason, |
+ 'onPlatform': serializedOnPlatform |
+ }; |
+ } |
} |