| 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 class Expectation { | 14 class Expectation { |
| 15 // Possible outcomes of running a test. | 15 // Possible outcomes of running a test. |
| 16 static Expectation PASS = byName('Pass'); | 16 static Expectation PASS = byName('Pass'); |
| 17 static Expectation CRASH = byName('Crash'); | 17 static Expectation CRASH = byName('Crash'); |
| 18 static Expectation TIMEOUT = byName('Timeout'); | 18 static Expectation TIMEOUT = byName('Timeout'); |
| 19 static Expectation FAIL = byName('Fail'); | 19 static Expectation FAIL = byName('Fail'); |
| 20 | 20 |
| 21 // Special 'FAIL' cases | 21 // Special 'FAIL' cases |
| 22 static Expectation RUNTIME_ERROR = byName('RuntimeError'); | 22 static Expectation RUNTIME_ERROR = byName('RuntimeError'); |
| 23 static Expectation COMPILETIME_ERROR = byName('CompileTimeError'); | 23 static Expectation COMPILETIME_ERROR = byName('CompileTimeError'); |
| 24 static Expectation MISSING_RUNTIME_ERROR = byName('MissingRuntimeError'); | 24 static Expectation MISSING_RUNTIME_ERROR = byName('MissingRuntimeError'); |
| 25 static Expectation MISSING_COMPILETIME_ERROR = | 25 static Expectation MISSING_COMPILETIME_ERROR = |
| 26 byName('MissingCompileTimeError'); | 26 byName('MissingCompileTimeError'); |
| 27 static Expectation STATIC_WARNING = byName('StaticWarning'); | 27 static Expectation STATIC_WARNING = byName('StaticWarning'); |
| 28 static Expectation MISSING_STATIC_WARNING = | 28 static Expectation MISSING_STATIC_WARNING = byName('MissingStaticWarning'); |
| 29 byName('MissingStaticWarning'); | |
| 30 static Expectation PUB_GET_ERROR = byName('PubGetError'); | 29 static Expectation PUB_GET_ERROR = byName('PubGetError'); |
| 31 | 30 |
| 32 // "meta expectations" | 31 // "meta expectations" |
| 33 static Expectation OK = byName('Ok'); | 32 static Expectation OK = byName('Ok'); |
| 34 static Expectation SLOW = byName('Slow'); | 33 static Expectation SLOW = byName('Slow'); |
| 35 static Expectation SKIP = byName('Skip'); | 34 static Expectation SKIP = byName('Skip'); |
| 36 static Expectation SKIP_SLOW = byName('SkipSlow'); | 35 static Expectation SKIP_SLOW = byName('SkipSlow'); |
| 37 static Expectation SKIP_BY_DESIGN = byName('SkipByDesign'); | 36 static Expectation SKIP_BY_DESIGN = byName('SkipByDesign'); |
| 38 | 37 |
| 39 static Expectation byName(String name) { | 38 static Expectation byName(String name) { |
| 40 _initialize(); | 39 _initialize(); |
| 41 name = name.toLowerCase(); | 40 name = name.toLowerCase(); |
| 42 if (!_AllExpectations.containsKey(name)) { | 41 if (!_AllExpectations.containsKey(name)) { |
| 43 throw new Exception("Expectation.byName(name='$name'): Invalid name."); | 42 throw new Exception("Expectation.byName(name='$name'): Invalid name."); |
| 44 } | 43 } |
| 45 return _AllExpectations[name]; | 44 return _AllExpectations[name]; |
| 46 } | 45 } |
| 47 | 46 |
| 48 // Keep a map of all possible Expectation objects, initialized lazily. | 47 // Keep a map of all possible Expectation objects, initialized lazily. |
| 49 static Map<String, Expectation> _AllExpectations; | 48 static Map<String, Expectation> _AllExpectations; |
| 50 static void _initialize() { | 49 static void _initialize() { |
| 51 if (_AllExpectations == null) { | 50 if (_AllExpectations == null) { |
| 52 _AllExpectations = new Map<String, Expectation>(); | 51 _AllExpectations = new Map<String, Expectation>(); |
| 53 | 52 |
| 54 Expectation build(prettyName, {group: null, isMetaExpectation: false}) { | 53 Expectation build(prettyName, {group: null, isMetaExpectation: false}) { |
| 55 var expectation = new Expectation._(prettyName, | 54 var expectation = new Expectation._(prettyName, |
| 56 group: group, isMetaExpectation: isMetaExpectation); | 55 group: group, isMetaExpectation: isMetaExpectation); |
| 57 assert(!_AllExpectations.containsKey(expectation.name)); | 56 assert(!_AllExpectations.containsKey(expectation.name)); |
| 58 return _AllExpectations[expectation.name] = expectation; | 57 return _AllExpectations[expectation.name] = expectation; |
| 59 } | 58 } |
| (...skipping 22 matching lines...) Expand all Loading... |
| 82 } | 81 } |
| 83 | 82 |
| 84 final String prettyName; | 83 final String prettyName; |
| 85 final String name; | 84 final String name; |
| 86 final Expectation group; | 85 final Expectation group; |
| 87 // Indicates whether this expectation cannot be a test outcome (i.e. it is a | 86 // Indicates whether this expectation cannot be a test outcome (i.e. it is a |
| 88 // "meta marker"). | 87 // "meta marker"). |
| 89 final bool isMetaExpectation; | 88 final bool isMetaExpectation; |
| 90 | 89 |
| 91 Expectation._(prettyName, | 90 Expectation._(prettyName, |
| 92 {Expectation this.group: null, | 91 {Expectation this.group: null, bool this.isMetaExpectation: false}) |
| 93 bool this.isMetaExpectation: false}) | 92 : prettyName = prettyName, |
| 94 : prettyName = prettyName, name = prettyName.toLowerCase(); | 93 name = prettyName.toLowerCase(); |
| 95 | 94 |
| 96 bool canBeOutcomeOf(Expectation expectation) { | 95 bool canBeOutcomeOf(Expectation expectation) { |
| 97 Expectation outcome = this; | 96 Expectation outcome = this; |
| 98 while (outcome != null) { | 97 while (outcome != null) { |
| 99 if (outcome == expectation) { | 98 if (outcome == expectation) { |
| 100 return true; | 99 return true; |
| 101 } | 100 } |
| 102 outcome = outcome.group; | 101 outcome = outcome.group; |
| 103 } | 102 } |
| 104 return false; | 103 return false; |
| 105 } | 104 } |
| 106 | 105 |
| 107 String toString() => prettyName; | 106 String toString() => prettyName; |
| 108 } | 107 } |
| 109 | 108 |
| 110 | |
| 111 final RegExp SplitComment = new RegExp("^([^#]*)(#.*)?\$"); | 109 final RegExp SplitComment = new RegExp("^([^#]*)(#.*)?\$"); |
| 112 final RegExp HeaderPattern = new RegExp(r"^\[([^\]]+)\]"); | 110 final RegExp HeaderPattern = new RegExp(r"^\[([^\]]+)\]"); |
| 113 final RegExp RulePattern = new RegExp(r"\s*([^: ]*)\s*:(.*)"); | 111 final RegExp RulePattern = new RegExp(r"\s*([^: ]*)\s*:(.*)"); |
| 114 final RegExp IssueNumberPattern = new RegExp("[Ii]ssue ([0-9]+)"); | 112 final RegExp IssueNumberPattern = new RegExp("[Ii]ssue ([0-9]+)"); |
| 115 | 113 |
| 116 class StatusFile { | 114 class StatusFile { |
| 117 final Path location; | 115 final Path location; |
| 118 | 116 |
| 119 StatusFile(this.location); | 117 StatusFile(this.location); |
| 120 } | 118 } |
| 121 | 119 |
| 122 // TODO(whesse): Implement configuration_info library that contains data | 120 // TODO(whesse): Implement configuration_info library that contains data |
| 123 // structures for test configuration, including Section. | 121 // structures for test configuration, including Section. |
| 124 class Section { | 122 class Section { |
| 125 final StatusFile statusFile; | 123 final StatusFile statusFile; |
| 126 | 124 |
| 127 final BooleanExpression condition; | 125 final BooleanExpression condition; |
| 128 final List<TestRule> testRules; | 126 final List<TestRule> testRules; |
| 129 final int lineNumber; | 127 final int lineNumber; |
| 130 | 128 |
| 131 Section.always(this.statusFile, this.lineNumber) | 129 Section.always(this.statusFile, this.lineNumber) |
| 132 : condition = null, testRules = new List<TestRule>(); | 130 : condition = null, |
| 131 testRules = new List<TestRule>(); |
| 133 Section(this.statusFile, this.condition, this.lineNumber) | 132 Section(this.statusFile, this.condition, this.lineNumber) |
| 134 : testRules = new List<TestRule>(); | 133 : testRules = new List<TestRule>(); |
| 135 | 134 |
| 136 bool isEnabled(environment) => | 135 bool isEnabled(environment) => |
| 137 condition == null || condition.evaluate(environment); | 136 condition == null || condition.evaluate(environment); |
| 138 | 137 |
| 139 String toString() { | 138 String toString() { |
| 140 return "Section: $condition"; | 139 return "Section: $condition"; |
| 141 } | 140 } |
| 142 } | 141 } |
| 143 | 142 |
| 144 Future<TestExpectations> ReadTestExpectations(List<String> statusFilePaths, | 143 Future<TestExpectations> ReadTestExpectations( |
| 145 Map environment) { | 144 List<String> statusFilePaths, Map environment) { |
| 146 var testExpectations = new TestExpectations(); | 145 var testExpectations = new TestExpectations(); |
| 147 return Future.wait(statusFilePaths.map((String statusFile) { | 146 return Future.wait(statusFilePaths.map((String statusFile) { |
| 148 return ReadTestExpectationsInto( | 147 return ReadTestExpectationsInto(testExpectations, statusFile, environment); |
| 149 testExpectations, statusFile, environment); | |
| 150 })).then((_) => testExpectations); | 148 })).then((_) => testExpectations); |
| 151 } | 149 } |
| 152 | 150 |
| 153 Future ReadTestExpectationsInto(TestExpectations expectations, | 151 Future ReadTestExpectationsInto( |
| 154 String statusFilePath, | 152 TestExpectations expectations, String statusFilePath, environment) { |
| 155 environment) { | |
| 156 var completer = new Completer(); | 153 var completer = new Completer(); |
| 157 List<Section> sections = new List<Section>(); | 154 List<Section> sections = new List<Section>(); |
| 158 | 155 |
| 159 void sectionsRead() { | 156 void sectionsRead() { |
| 160 for (Section section in sections) { | 157 for (Section section in sections) { |
| 161 if (section.isEnabled(environment)) { | 158 if (section.isEnabled(environment)) { |
| 162 for (var rule in section.testRules) { | 159 for (var rule in section.testRules) { |
| 163 expectations.addRule(rule, environment); | 160 expectations.addRule(rule, environment); |
| 164 } | 161 } |
| 165 } | 162 } |
| 166 } | 163 } |
| 167 completer.complete(); | 164 completer.complete(); |
| 168 } | 165 } |
| 169 | 166 |
| 170 ReadConfigurationInto(new Path(statusFilePath), sections, sectionsRead); | 167 ReadConfigurationInto(new Path(statusFilePath), sections, sectionsRead); |
| 171 return completer.future; | 168 return completer.future; |
| 172 } | 169 } |
| 173 | 170 |
| 174 void ReadConfigurationInto(Path path, sections, onDone) { | 171 void ReadConfigurationInto(Path path, sections, onDone) { |
| 175 StatusFile statusFile = new StatusFile(path); | 172 StatusFile statusFile = new StatusFile(path); |
| 176 File file = new File(path.toNativePath()); | 173 File file = new File(path.toNativePath()); |
| 177 if (!file.existsSync()) { | 174 if (!file.existsSync()) { |
| 178 throw new Exception('Cannot find test status file $path'); | 175 throw new Exception('Cannot find test status file $path'); |
| 179 } | 176 } |
| 180 int lineNumber = 0; | 177 int lineNumber = 0; |
| 181 Stream<String> lines = | 178 Stream<String> lines = |
| 182 file.openRead() | 179 file.openRead().transform(UTF8.decoder).transform(new LineSplitter()); |
| 183 .transform(UTF8.decoder) | |
| 184 .transform(new LineSplitter()); | |
| 185 | 180 |
| 186 Section currentSection = new Section.always(statusFile, -1); | 181 Section currentSection = new Section.always(statusFile, -1); |
| 187 sections.add(currentSection); | 182 sections.add(currentSection); |
| 188 | 183 |
| 189 | |
| 190 lines.listen((String line) { | 184 lines.listen((String line) { |
| 191 lineNumber++; | 185 lineNumber++; |
| 192 Match match = SplitComment.firstMatch(line); | 186 Match match = SplitComment.firstMatch(line); |
| 193 line = (match == null) ? "" : match[1]; | 187 line = (match == null) ? "" : match[1]; |
| 194 line = line.trim(); | 188 line = line.trim(); |
| 195 if (line.isEmpty) return; | 189 if (line.isEmpty) return; |
| 196 | 190 |
| 197 // Extract the comment to get the issue number if needed. | 191 // Extract the comment to get the issue number if needed. |
| 198 String comment = (match == null || match[2] == null) ? "" : match[2]; | 192 String comment = (match == null || match[2] == null) ? "" : match[2]; |
| 199 | 193 |
| (...skipping 18 matching lines...) Expand all Loading... |
| 218 new ExpressionParser(new Scanner(tokens)).parseSetExpression(); | 212 new ExpressionParser(new Scanner(tokens)).parseSetExpression(); |
| 219 | 213 |
| 220 // Look for issue number in comment. | 214 // Look for issue number in comment. |
| 221 String issueString = null; | 215 String issueString = null; |
| 222 match = IssueNumberPattern.firstMatch(comment); | 216 match = IssueNumberPattern.firstMatch(comment); |
| 223 if (match != null) { | 217 if (match != null) { |
| 224 issueString = match[1]; | 218 issueString = match[1]; |
| 225 if (issueString == null) issueString = match[2]; | 219 if (issueString == null) issueString = match[2]; |
| 226 } | 220 } |
| 227 int issue = issueString != null ? int.parse(issueString) : null; | 221 int issue = issueString != null ? int.parse(issueString) : null; |
| 228 currentSection.testRules.add( | 222 currentSection.testRules |
| 229 new TestRule(name, expression, issue, lineNumber)); | 223 .add(new TestRule(name, expression, issue, lineNumber)); |
| 230 return; | 224 return; |
| 231 } | 225 } |
| 232 | 226 |
| 233 print("unmatched line: $line"); | 227 print("unmatched line: $line"); |
| 234 }, | 228 }, onDone: onDone); |
| 235 onDone: onDone); | |
| 236 } | 229 } |
| 237 | 230 |
| 238 | |
| 239 class TestRule { | 231 class TestRule { |
| 240 String name; | 232 String name; |
| 241 SetExpression expression; | 233 SetExpression expression; |
| 242 int issue; | 234 int issue; |
| 243 int lineNumber; | 235 int lineNumber; |
| 244 | 236 |
| 245 TestRule(this.name, | 237 TestRule(this.name, this.expression, this.issue, this.lineNumber); |
| 246 this.expression, | |
| 247 this.issue, | |
| 248 this.lineNumber); | |
| 249 | 238 |
| 250 bool get hasIssue => issue != null; | 239 bool get hasIssue => issue != null; |
| 251 | 240 |
| 252 String toString() => 'TestRule($name, $expression, $issue)'; | 241 String toString() => 'TestRule($name, $expression, $issue)'; |
| 253 } | 242 } |
| 254 | 243 |
| 255 | |
| 256 class TestExpectations { | 244 class TestExpectations { |
| 257 // Only create one copy of each Set<Expectation>. | 245 // Only create one copy of each Set<Expectation>. |
| 258 // We just use .toString as a key, so we may make a few | 246 // We just use .toString as a key, so we may make a few |
| 259 // sets that only differ in their toString element order. | 247 // sets that only differ in their toString element order. |
| 260 static Map _cachedSets = new Map(); | 248 static Map _cachedSets = new Map(); |
| 261 | 249 |
| 262 Map _map; | 250 Map _map; |
| 263 bool _preprocessed = false; | 251 bool _preprocessed = false; |
| 264 Map _regExpCache; | 252 Map _regExpCache; |
| 265 Map _keyToRegExps; | 253 Map _keyToRegExps; |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 344 } | 332 } |
| 345 regExps[i] = regExp; | 333 regExps[i] = regExp; |
| 346 } | 334 } |
| 347 _keyToRegExps[key] = regExps; | 335 _keyToRegExps[key] = regExps; |
| 348 }); | 336 }); |
| 349 | 337 |
| 350 _regExpCache = null; | 338 _regExpCache = null; |
| 351 _preprocessed = true; | 339 _preprocessed = true; |
| 352 } | 340 } |
| 353 } | 341 } |
| OLD | NEW |