Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(371)

Side by Side Diff: lib/src/backend/metadata.dart

Issue 1691173002: Support tag configuration. (Closed) Base URL: git@github.com:dart-lang/test@master
Patch Set: Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « doc/package_config.md ('k') | lib/src/frontend/tags.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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 = tags == null
173 ? new Set()
174 : new UnmodifiableSetView(tags.toSet()),
kevmoo 2016/02/12 18:29:30 put the null check inside this ctor – so even the
nweiz 2016/02/16 23:36:31 Done.
116 onPlatform = onPlatform == null 175 onPlatform = onPlatform == null
117 ? const {} 176 ? const {}
118 : new UnmodifiableMapView(onPlatform), 177 : new UnmodifiableMapView(onPlatform),
119 tags = tags == null 178 forTag = forTag == null
120 ? new Set() 179 ? const {}
121 : new UnmodifiableSetView(tags.toSet()) { 180 : new UnmodifiableMapView(forTag) {
122 _validateTags(); 181 _validateTags();
123 } 182 }
124 183
125 /// Creates a new Metadata, but with fields parsed from caller-friendly values 184 /// Creates a new Metadata, but with fields parsed from caller-friendly values
126 /// where applicable. 185 /// where applicable.
127 /// 186 ///
128 /// Throws a [FormatException] if any field is invalid. 187 /// Throws a [FormatException] if any field is invalid.
129 Metadata.parse({String testOn, Timeout timeout, skip, 188 Metadata.parse({String testOn, Timeout timeout, skip,
130 this.verboseTrace: false, Map<String, dynamic> onPlatform, 189 this.verboseTrace: false, Map<String, dynamic> onPlatform,
131 tags}) 190 tags})
132 : testOn = testOn == null 191 : testOn = testOn == null
133 ? PlatformSelector.all 192 ? PlatformSelector.all
134 : new PlatformSelector.parse(testOn), 193 : new PlatformSelector.parse(testOn),
135 timeout = timeout == null ? const Timeout.factor(1) : timeout, 194 timeout = timeout == null ? const Timeout.factor(1) : timeout,
136 skip = skip != null && skip != false, 195 skip = skip != null && skip != false,
137 skipReason = skip is String ? skip : null, 196 skipReason = skip is String ? skip : null,
138 onPlatform = _parseOnPlatform(onPlatform), 197 onPlatform = _parseOnPlatform(onPlatform),
139 tags = _parseTags(tags) { 198 tags = _parseTags(tags),
199 forTag = const {} {
140 if (skip != null && skip is! String && skip is! bool) { 200 if (skip != null && skip is! String && skip is! bool) {
141 throw new ArgumentError( 201 throw new ArgumentError(
142 '"skip" must be a String or a bool, was "$skip".'); 202 '"skip" must be a String or a bool, was "$skip".');
143 } 203 }
144 204
145 _validateTags(); 205 _validateTags();
146 } 206 }
147 207
148 /// Deserializes the result of [Metadata.serialize] into a new [Metadata]. 208 /// Deserializes the result of [Metadata.serialize] into a new [Metadata].
149 Metadata.deserialize(serialized) 209 Metadata.deserialize(serialized)
150 : testOn = serialized['testOn'] == null 210 : testOn = serialized['testOn'] == null
151 ? PlatformSelector.all 211 ? PlatformSelector.all
152 : new PlatformSelector.parse(serialized['testOn']), 212 : new PlatformSelector.parse(serialized['testOn']),
153 timeout = _deserializeTimeout(serialized['timeout']), 213 timeout = _deserializeTimeout(serialized['timeout']),
154 skip = serialized['skip'], 214 skip = serialized['skip'],
155 skipReason = serialized['skipReason'], 215 skipReason = serialized['skipReason'],
156 verboseTrace = serialized['verboseTrace'], 216 verboseTrace = serialized['verboseTrace'],
157 tags = new Set.from(serialized['tags']), 217 tags = new Set.from(serialized['tags']),
158 onPlatform = new Map.fromIterable(serialized['onPlatform'], 218 onPlatform = new Map.fromIterable(serialized['onPlatform'],
159 key: (pair) => new PlatformSelector.parse(pair.first), 219 key: (pair) => new PlatformSelector.parse(pair.first),
160 value: (pair) => new Metadata.deserialize(pair.last)); 220 value: (pair) => new Metadata.deserialize(pair.last)),
221 forTag = mapMap(serialized['forTag'],
222 value: (_, nested) => new Metadata.deserialize(nested));
161 223
162 /// Deserializes timeout from the format returned by [_serializeTimeout]. 224 /// Deserializes timeout from the format returned by [_serializeTimeout].
163 static _deserializeTimeout(serialized) { 225 static _deserializeTimeout(serialized) {
164 if (serialized == 'none') return Timeout.none; 226 if (serialized == 'none') return Timeout.none;
165 var scaleFactor = serialized['scaleFactor']; 227 var scaleFactor = serialized['scaleFactor'];
166 if (scaleFactor != null) return new Timeout.factor(scaleFactor); 228 if (scaleFactor != null) return new Timeout.factor(scaleFactor);
167 return new Timeout( 229 return new Timeout(
168 new Duration(microseconds: serialized['duration'])); 230 new Duration(microseconds: serialized['duration']));
169 } 231 }
170 232
171 /// Throws an [ArgumentError] if any tags in [tags] aren't hyphenated 233 /// Throws an [ArgumentError] if any tags in [tags] aren't hyphenated
172 /// identifiers. 234 /// identifiers.
173 void _validateTags() { 235 void _validateTags() {
174 var invalidTags = tags 236 var invalidTags = tags
175 .where((tag) => !tag.contains(anchoredHyphenatedIdentifier)) 237 .where((tag) => !tag.contains(anchoredHyphenatedIdentifier))
176 .map((tag) => '"$tag"') 238 .map((tag) => '"$tag"')
177 .toList(); 239 .toList();
178 240
179 if (invalidTags.isEmpty) return; 241 if (invalidTags.isEmpty) return;
180 242
181 throw new ArgumentError( 243 throw new ArgumentError(
182 "Invalid ${pluralize('tag', invalidTags.length)} " 244 "Invalid ${pluralize('tag', invalidTags.length)} "
183 "${toSentence(invalidTags)}. Tags must be (optionally hyphenated) " 245 "${toSentence(invalidTags)}. Tags must be (optionally hyphenated) "
184 "Dart identifiers."); 246 "Dart identifiers.");
185 } 247 }
186 248
187 /// Return a new [Metadata] that merges [this] with [other]. 249 /// Return a new [Metadata] that merges [this] with [other].
188 /// 250 ///
189 /// If the two [Metadata]s have conflicting properties, [other] wins. 251 /// If the two [Metadata]s have conflicting properties, [other] wins. If
252 /// either has a [forTag] metadata for one of the other's tags, that metadata
253 /// is merged as well.
190 Metadata merge(Metadata other) => 254 Metadata merge(Metadata other) =>
191 new Metadata( 255 new Metadata(
192 testOn: testOn.intersect(other.testOn), 256 testOn: testOn.intersect(other.testOn),
193 timeout: timeout.merge(other.timeout), 257 timeout: timeout.merge(other.timeout),
194 skip: skip || other.skip, 258 skip: skip || other.skip,
259 skipReason: other.skipReason == null ? skipReason : other.skipReason,
195 verboseTrace: verboseTrace || other.verboseTrace, 260 verboseTrace: verboseTrace || other.verboseTrace,
196 skipReason: other.skipReason == null ? skipReason : other.skipReason, 261 tags: tags.union(other.tags),
197 onPlatform: mergeMaps(onPlatform, other.onPlatform), 262 onPlatform: mergeMaps(onPlatform, other.onPlatform,
198 tags: tags.union(other.tags)); 263 value: (metadata1, metadata2) => metadata1.merge(metadata2)),
264 forTag: mergeMaps(forTag, other.forTag,
265 value: (metadata1, metadata2) => metadata1.merge(metadata2)));
199 266
200 /// Returns a copy of [this] with the given fields changed. 267 /// Returns a copy of [this] with the given fields changed.
201 Metadata change({PlatformSelector testOn, Timeout timeout, bool skip, 268 Metadata change({PlatformSelector testOn, Timeout timeout, bool skip,
202 bool verboseTrace, String skipReason, 269 bool verboseTrace, String skipReason,
203 Map<PlatformSelector, Metadata> onPlatform}) { 270 Map<PlatformSelector, Metadata> onPlatform}) {
204 if (testOn == null) testOn = this.testOn; 271 if (testOn == null) testOn = this.testOn;
205 if (timeout == null) timeout = this.timeout; 272 if (timeout == null) timeout = this.timeout;
206 if (skip == null) skip = this.skip; 273 if (skip == null) skip = this.skip;
207 if (verboseTrace == null) verboseTrace = this.verboseTrace; 274 if (verboseTrace == null) verboseTrace = this.verboseTrace;
208 if (skipReason == null) skipReason = this.skipReason; 275 if (skipReason == null) skipReason = this.skipReason;
(...skipping 24 matching lines...) Expand all
233 onPlatform.forEach((key, value) { 300 onPlatform.forEach((key, value) {
234 serializedOnPlatform.add([key.toString(), value.serialize()]); 301 serializedOnPlatform.add([key.toString(), value.serialize()]);
235 }); 302 });
236 303
237 return { 304 return {
238 'testOn': testOn == PlatformSelector.all ? null : testOn.toString(), 305 'testOn': testOn == PlatformSelector.all ? null : testOn.toString(),
239 'timeout': _serializeTimeout(timeout), 306 'timeout': _serializeTimeout(timeout),
240 'skip': skip, 307 'skip': skip,
241 'skipReason': skipReason, 308 'skipReason': skipReason,
242 'verboseTrace': verboseTrace, 309 'verboseTrace': verboseTrace,
310 'tags': tags.toList(),
243 'onPlatform': serializedOnPlatform, 311 'onPlatform': serializedOnPlatform,
244 'tags': tags.toList() 312 'forTag': mapMap(forTag, value: (_, metadata) => metadata.serialize())
245 }; 313 };
246 } 314 }
247 315
248 /// Serializes timeout into a JSON-safe object. 316 /// Serializes timeout into a JSON-safe object.
249 _serializeTimeout(Timeout timeout) { 317 _serializeTimeout(Timeout timeout) {
250 if (timeout == Timeout.none) return 'none'; 318 if (timeout == Timeout.none) return 'none';
251 return { 319 return {
252 'duration': timeout.duration == null 320 'duration': timeout.duration == null
253 ? null 321 ? null
254 : timeout.duration.inMicroseconds, 322 : timeout.duration.inMicroseconds,
255 'scaleFactor': timeout.scaleFactor 323 'scaleFactor': timeout.scaleFactor
256 }; 324 };
257 } 325 }
258 } 326 }
OLDNEW
« no previous file with comments | « doc/package_config.md ('k') | lib/src/frontend/tags.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698