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 |