OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 status_file_parser; | 5 library status_file_parser; |
6 | 6 |
7 import "dart:async"; | 7 import "dart:async"; |
8 import "dart:convert" show LineSplitter, UTF8; | 8 import "dart:convert" show LineSplitter, UTF8; |
9 import "dart:io"; | 9 import "dart:io"; |
10 | 10 |
11 import "path.dart"; | 11 import "path.dart"; |
12 import "status_expression.dart"; | 12 import "status_expression.dart"; |
13 | 13 |
| 14 typedef void Action(); |
| 15 |
14 class Expectation { | 16 class Expectation { |
15 // Possible outcomes of running a test. | 17 // Possible outcomes of running a test. |
16 static Expectation PASS = byName('Pass'); | 18 static Expectation PASS = byName('Pass'); |
17 static Expectation CRASH = byName('Crash'); | 19 static Expectation CRASH = byName('Crash'); |
18 static Expectation TIMEOUT = byName('Timeout'); | 20 static Expectation TIMEOUT = byName('Timeout'); |
19 static Expectation FAIL = byName('Fail'); | 21 static Expectation FAIL = byName('Fail'); |
20 | 22 |
21 // Special 'FAIL' cases | 23 // Special 'FAIL' cases |
22 static Expectation RUNTIME_ERROR = byName('RuntimeError'); | 24 static Expectation RUNTIME_ERROR = byName('RuntimeError'); |
23 static Expectation COMPILETIME_ERROR = byName('CompileTimeError'); | 25 static Expectation COMPILETIME_ERROR = byName('CompileTimeError'); |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
58 } | 60 } |
59 return _AllExpectations[name]; | 61 return _AllExpectations[name]; |
60 } | 62 } |
61 | 63 |
62 // Keep a map of all possible Expectation objects, initialized lazily. | 64 // Keep a map of all possible Expectation objects, initialized lazily. |
63 static Map<String, Expectation> _AllExpectations; | 65 static Map<String, Expectation> _AllExpectations; |
64 static void _initialize() { | 66 static void _initialize() { |
65 if (_AllExpectations == null) { | 67 if (_AllExpectations == null) { |
66 _AllExpectations = new Map<String, Expectation>(); | 68 _AllExpectations = new Map<String, Expectation>(); |
67 | 69 |
68 Expectation build(prettyName, {group: null, isMetaExpectation: false}) { | 70 Expectation build(String prettyName, |
| 71 {Expectation group, bool isMetaExpectation: false}) { |
69 var expectation = new Expectation._(prettyName, | 72 var expectation = new Expectation._(prettyName, |
70 group: group, isMetaExpectation: isMetaExpectation); | 73 group: group, isMetaExpectation: isMetaExpectation); |
71 assert(!_AllExpectations.containsKey(expectation.name)); | 74 assert(!_AllExpectations.containsKey(expectation.name)); |
72 return _AllExpectations[expectation.name] = expectation; | 75 return _AllExpectations[expectation.name] = expectation; |
73 } | 76 } |
74 | 77 |
75 var fail = build("Fail"); | 78 var fail = build("Fail"); |
76 var crash = build("Crash"); | 79 var crash = build("Crash"); |
77 var timeout = build("Timeout"); | 80 var timeout = build("Timeout"); |
78 build("Pass"); | 81 build("Pass"); |
(...skipping 23 matching lines...) Expand all Loading... |
102 } | 105 } |
103 } | 106 } |
104 | 107 |
105 final String prettyName; | 108 final String prettyName; |
106 final String name; | 109 final String name; |
107 final Expectation group; | 110 final Expectation group; |
108 // Indicates whether this expectation cannot be a test outcome (i.e. it is a | 111 // Indicates whether this expectation cannot be a test outcome (i.e. it is a |
109 // "meta marker"). | 112 // "meta marker"). |
110 final bool isMetaExpectation; | 113 final bool isMetaExpectation; |
111 | 114 |
112 Expectation._(prettyName, | 115 Expectation._(this.prettyName, |
113 {Expectation this.group: null, bool this.isMetaExpectation: false}) | 116 {Expectation this.group: null, bool this.isMetaExpectation: false}) |
114 : prettyName = prettyName, | 117 : name = prettyName.toLowerCase(); |
115 name = prettyName.toLowerCase(); | |
116 | 118 |
117 bool canBeOutcomeOf(Expectation expectation) { | 119 bool canBeOutcomeOf(Expectation expectation) { |
118 Expectation outcome = this; | 120 Expectation outcome = this; |
119 if (outcome == IGNORE) return true; | 121 if (outcome == IGNORE) return true; |
120 while (outcome != null) { | 122 while (outcome != null) { |
121 if (outcome == expectation) { | 123 if (outcome == expectation) { |
122 return true; | 124 return true; |
123 } | 125 } |
124 outcome = outcome.group; | 126 outcome = outcome.group; |
125 } | 127 } |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
157 | 159 |
158 bool isEnabled(environment) => | 160 bool isEnabled(environment) => |
159 condition == null || condition.evaluate(environment); | 161 condition == null || condition.evaluate(environment); |
160 | 162 |
161 String toString() { | 163 String toString() { |
162 return "Section: $condition"; | 164 return "Section: $condition"; |
163 } | 165 } |
164 } | 166 } |
165 | 167 |
166 Future<TestExpectations> ReadTestExpectations( | 168 Future<TestExpectations> ReadTestExpectations( |
167 List<String> statusFilePaths, Map environment) { | 169 List<String> statusFilePaths, Map<String, String> environment) { |
168 var testExpectations = new TestExpectations(); | 170 var testExpectations = new TestExpectations(); |
169 return Future.wait(statusFilePaths.map((String statusFile) { | 171 return Future.wait(statusFilePaths.map((String statusFile) { |
170 return ReadTestExpectationsInto(testExpectations, statusFile, environment); | 172 return ReadTestExpectationsInto(testExpectations, statusFile, environment); |
171 })).then((_) => testExpectations); | 173 })).then((_) => testExpectations); |
172 } | 174 } |
173 | 175 |
174 Future ReadTestExpectationsInto( | 176 Future ReadTestExpectationsInto(TestExpectations expectations, |
175 TestExpectations expectations, String statusFilePath, environment) { | 177 String statusFilePath, Map<String, String> environment) { |
176 var completer = new Completer(); | 178 var completer = new Completer<Null>(); |
177 List<Section> sections = new List<Section>(); | 179 var sections = <Section>[]; |
178 | 180 |
179 void sectionsRead() { | 181 void sectionsRead() { |
180 for (Section section in sections) { | 182 for (Section section in sections) { |
181 if (section.isEnabled(environment)) { | 183 if (section.isEnabled(environment)) { |
182 for (var rule in section.testRules) { | 184 for (var rule in section.testRules) { |
183 expectations.addRule(rule, environment); | 185 expectations.addRule(rule, environment); |
184 } | 186 } |
185 } | 187 } |
186 } | 188 } |
187 completer.complete(); | 189 completer.complete(); |
188 } | 190 } |
189 | 191 |
190 ReadConfigurationInto(new Path(statusFilePath), sections, sectionsRead); | 192 ReadConfigurationInto(new Path(statusFilePath), sections, sectionsRead); |
191 return completer.future; | 193 return completer.future; |
192 } | 194 } |
193 | 195 |
194 void ReadConfigurationInto(Path path, sections, onDone) { | 196 void ReadConfigurationInto(Path path, List<Section> sections, Action onDone) { |
195 StatusFile statusFile = new StatusFile(path); | 197 StatusFile statusFile = new StatusFile(path); |
196 File file = new File(path.toNativePath()); | 198 File file = new File(path.toNativePath()); |
197 if (!file.existsSync()) { | 199 if (!file.existsSync()) { |
198 throw new Exception('Cannot find test status file $path'); | 200 throw new Exception('Cannot find test status file $path'); |
199 } | 201 } |
200 int lineNumber = 0; | 202 int lineNumber = 0; |
201 Stream<String> lines = | 203 Stream<String> lines = |
202 file.openRead().transform(UTF8.decoder).transform(new LineSplitter()); | 204 file.openRead().transform(UTF8.decoder).transform(new LineSplitter()); |
203 | 205 |
204 Section currentSection = new Section.always(statusFile, -1); | 206 Section currentSection = new Section.always(statusFile, -1); |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
261 | 263 |
262 bool get hasIssue => issue != null; | 264 bool get hasIssue => issue != null; |
263 | 265 |
264 String toString() => 'TestRule($name, $expression, $issue)'; | 266 String toString() => 'TestRule($name, $expression, $issue)'; |
265 } | 267 } |
266 | 268 |
267 class TestExpectations { | 269 class TestExpectations { |
268 // Only create one copy of each Set<Expectation>. | 270 // Only create one copy of each Set<Expectation>. |
269 // We just use .toString as a key, so we may make a few | 271 // We just use .toString as a key, so we may make a few |
270 // sets that only differ in their toString element order. | 272 // sets that only differ in their toString element order. |
271 static Map _cachedSets = new Map(); | 273 static Map<String, Set<Expectation>> _cachedSets = {}; |
272 | 274 |
273 Map _map; | 275 Map<String, Set<Expectation>> _map; |
274 bool _preprocessed = false; | 276 bool _preprocessed = false; |
275 Map _regExpCache; | 277 Map<String, RegExp> _regExpCache; |
276 Map _keyToRegExps; | 278 Map<String, List<RegExp>> _keyToRegExps; |
277 | 279 |
278 /** | 280 /** |
279 * Create a TestExpectations object. See the [expectations] method | 281 * Create a TestExpectations object. See the [expectations] method |
280 * for an explanation of matching. | 282 * for an explanation of matching. |
281 */ | 283 */ |
282 TestExpectations() : _map = new Map(); | 284 TestExpectations() : _map = {}; |
283 | 285 |
284 /** | 286 /** |
285 * Add a rule to the expectations. | 287 * Add a rule to the expectations. |
286 */ | 288 */ |
287 void addRule(testRule, environment) { | 289 void addRule(TestRule testRule, environment) { |
288 // Once we have started using the expectations we cannot add more | 290 // Once we have started using the expectations we cannot add more |
289 // rules. | 291 // rules. |
290 if (_preprocessed) { | 292 if (_preprocessed) { |
291 throw "TestExpectations.addRule: cannot add more rules"; | 293 throw "TestExpectations.addRule: cannot add more rules"; |
292 } | 294 } |
293 var names = testRule.expression.evaluate(environment); | 295 var names = testRule.expression.evaluate(environment); |
294 var expectations = names.map((name) => Expectation.byName(name)); | 296 var expectations = names.map((name) => Expectation.byName(name)); |
295 _map.putIfAbsent(testRule.name, () => new Set()).addAll(expectations); | 297 _map |
| 298 .putIfAbsent(testRule.name, () => new Set<Expectation>()) |
| 299 .addAll(expectations); |
296 } | 300 } |
297 | 301 |
298 /** | 302 /** |
299 * Compute the expectations for a test based on the filename. | 303 * Compute the expectations for a test based on the filename. |
300 * | 304 * |
301 * For every (key, expectation) pair. Match the key with the file | 305 * For every (key, expectation) pair. Match the key with the file |
302 * name. Return the union of the expectations for all the keys | 306 * name. Return the union of the expectations for all the keys |
303 * that match. | 307 * that match. |
304 * | 308 * |
305 * Normal matching splits the key and the filename into path | 309 * Normal matching splits the key and the filename into path |
306 * components and checks that the anchored regular expression | 310 * components and checks that the anchored regular expression |
307 * "^$keyComponent\$" matches the corresponding filename component. | 311 * "^$keyComponent\$" matches the corresponding filename component. |
308 */ | 312 */ |
309 Set<Expectation> expectations(String filename) { | 313 Set<Expectation> expectations(String filename) { |
310 var result = new Set(); | 314 var result = new Set<Expectation>(); |
311 var splitFilename = filename.split('/'); | 315 var splitFilename = filename.split('/'); |
312 | 316 |
313 // Create mapping from keys to list of RegExps once and for all. | 317 // Create mapping from keys to list of RegExps once and for all. |
314 _preprocessForMatching(); | 318 _preprocessForMatching(); |
315 | 319 |
316 _map.forEach((key, expectation) { | 320 _map.forEach((key, Set<Expectation> expectations) { |
317 List regExps = _keyToRegExps[key]; | 321 List<RegExp> regExps = _keyToRegExps[key]; |
318 if (regExps.length > splitFilename.length) return; | 322 if (regExps.length > splitFilename.length) return; |
319 for (var i = 0; i < regExps.length; i++) { | 323 for (var i = 0; i < regExps.length; i++) { |
320 if (!regExps[i].hasMatch(splitFilename[i])) return; | 324 if (!regExps[i].hasMatch(splitFilename[i])) return; |
321 } | 325 } |
322 // If all components of the status file key matches the filename | 326 // If all components of the status file key matches the filename |
323 // add the expectations to the result. | 327 // add the expectations to the result. |
324 result.addAll(expectation); | 328 result.addAll(expectations); |
325 }); | 329 }); |
326 | 330 |
327 // If no expectations were found the expectation is that the test | 331 // If no expectations were found the expectation is that the test |
328 // passes. | 332 // passes. |
329 if (result.isEmpty) { | 333 if (result.isEmpty) { |
330 result.add(Expectation.PASS); | 334 result.add(Expectation.PASS); |
331 } | 335 } |
332 return _cachedSets.putIfAbsent(result.toString(), () => result); | 336 return _cachedSets.putIfAbsent(result.toString(), () => result); |
333 } | 337 } |
334 | 338 |
335 // Preprocess the expectations for matching against | 339 // Preprocess the expectations for matching against |
336 // filenames. Generate lists of regular expressions once and for all | 340 // filenames. Generate lists of regular expressions once and for all |
337 // for each key. | 341 // for each key. |
338 void _preprocessForMatching() { | 342 void _preprocessForMatching() { |
339 if (_preprocessed) return; | 343 if (_preprocessed) return; |
340 | 344 |
341 _keyToRegExps = new Map(); | 345 _keyToRegExps = {}; |
342 _regExpCache = new Map(); | 346 _regExpCache = {}; |
343 | 347 |
344 _map.forEach((key, expectations) { | 348 _map.forEach((key, expectations) { |
345 if (_keyToRegExps[key] != null) return; | 349 if (_keyToRegExps[key] != null) return; |
346 var splitKey = key.split('/'); | 350 var splitKey = key.split('/'); |
347 var regExps = new List(splitKey.length); | 351 var regExps = new List<RegExp>(splitKey.length); |
348 for (var i = 0; i < splitKey.length; i++) { | 352 for (var i = 0; i < splitKey.length; i++) { |
349 var component = splitKey[i]; | 353 var component = splitKey[i]; |
350 var regExp = _regExpCache[component]; | 354 var regExp = _regExpCache[component]; |
351 if (regExp == null) { | 355 if (regExp == null) { |
352 var pattern = "^${splitKey[i]}\$".replaceAll('*', '.*'); | 356 var pattern = "^${splitKey[i]}\$".replaceAll('*', '.*'); |
353 regExp = new RegExp(pattern); | 357 regExp = new RegExp(pattern); |
354 _regExpCache[component] = regExp; | 358 _regExpCache[component] = regExp; |
355 } | 359 } |
356 regExps[i] = regExp; | 360 regExps[i] = regExp; |
357 } | 361 } |
358 _keyToRegExps[key] = regExps; | 362 _keyToRegExps[key] = regExps; |
359 }); | 363 }); |
360 | 364 |
361 _regExpCache = null; | 365 _regExpCache = null; |
362 _preprocessed = true; | 366 _preprocessed = true; |
363 } | 367 } |
364 } | 368 } |
OLD | NEW |