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