| 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 |