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

Side by Side Diff: tools/testing/dart/status_file_parser.dart

Issue 841193003: cleanup to tools/testing/dart (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: one last bit Created 5 years, 11 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 | Annotate | Revision Log
« no previous file with comments | « tools/testing/dart/status_expression.dart ('k') | tools/testing/dart/test_configurations.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library status_file_parser;
6
7 import "dart:async";
8 import "dart:convert" show LineSplitter, UTF8;
9 import "dart:io";
10
11 import "path.dart";
12 import "status_expression.dart";
13
14 class Expectation {
15 // Possible outcomes of running a test.
16 static Expectation PASS = byName('Pass');
17 static Expectation CRASH = byName('Crash');
18 static Expectation TIMEOUT = byName('Timeout');
19 static Expectation FAIL = byName('Fail');
20
21 // Special 'FAIL' cases
22 static Expectation RUNTIME_ERROR = byName('RuntimeError');
23 static Expectation COMPILETIME_ERROR = byName('CompileTimeError');
24 static Expectation MISSING_RUNTIME_ERROR = byName('MissingRuntimeError');
25 static Expectation MISSING_COMPILETIME_ERROR =
26 byName('MissingCompileTimeError');
27 static Expectation STATIC_WARNING = byName('StaticWarning');
28 static Expectation MISSING_STATIC_WARNING =
29 byName('MissingStaticWarning');
30 static Expectation PUB_GET_ERROR = byName('PubGetError');
31
32 // "meta expectations"
33 static Expectation OK = byName('Ok');
34 static Expectation SLOW = byName('Slow');
35 static Expectation SKIP = byName('Skip');
36 static Expectation SKIP_BY_DESIGN = byName('SkipByDesign');
37
38 static Expectation byName(String name) {
39 _initialize();
40 name = name.toLowerCase();
41 if (!_AllExpectations.containsKey(name)) {
42 throw new Exception("Expectation.byName(name='$name'): Invalid name.");
43 }
44 return _AllExpectations[name];
45 }
46
47 // Keep a map of all possible Expectation objects, initialized lazily.
48 static Map<String, Expectation> _AllExpectations;
49 static void _initialize() {
50 if (_AllExpectations == null) {
51 _AllExpectations = new Map<String, Expectation>();
52
53 Expectation build(prettyName, {group: null, isMetaExpectation: false}) {
54 var expectation = new Expectation._(prettyName,
55 group: group, isMetaExpectation: isMetaExpectation);
56 assert(!_AllExpectations.containsKey(expectation.name));
57 return _AllExpectations[expectation.name] = expectation;
58 }
59
60 var fail = build("Fail");
61 build("Pass");
62 build("Crash");
63 build("Timeout");
64
65 build("MissingCompileTimeError", group: fail);
66 build("MissingRuntimeError", group: fail);
67 build("CompileTimeError", group: fail);
68 build("RuntimeError", group: fail);
69
70 build("MissingStaticWarning", group: fail);
71 build("StaticWarning", group: fail);
72
73 build("PubGetError", group: fail);
74
75 build("Skip", isMetaExpectation: true);
76 build("SkipByDesign", isMetaExpectation: true);
77 build("Ok", isMetaExpectation: true);
78 build("Slow", isMetaExpectation: true);
79 }
80 }
81
82 final String prettyName;
83 final String name;
84 final Expectation group;
85 // Indicates whether this expectation cannot be a test outcome (i.e. it is a
86 // "meta marker").
87 final bool isMetaExpectation;
88
89 Expectation._(prettyName,
90 {Expectation this.group: null,
91 bool this.isMetaExpectation: false})
92 : prettyName = prettyName, name = prettyName.toLowerCase();
93
94 bool canBeOutcomeOf(Expectation expectation) {
95 Expectation outcome = this;
96 while (outcome != null) {
97 if (outcome == expectation) {
98 return true;
99 }
100 outcome = outcome.group;
101 }
102 return false;
103 }
104
105 String toString() => prettyName;
106 }
107
108
109 final RegExp SplitComment = new RegExp("^([^#]*)(#.*)?\$");
110 final RegExp HeaderPattern = new RegExp(r"^\[([^\]]+)\]");
111 final RegExp RulePattern = new RegExp(r"\s*([^: ]*)\s*:(.*)");
112 final RegExp IssueNumberPattern =
113 new RegExp("Issue ([0-9]+)|dartbug.com/([0-9]+)", caseSensitive: false);
114
115 class StatusFile {
116 final Path location;
117
118 StatusFile(this.location);
119 }
120
121 // TODO(whesse): Implement configuration_info library that contains data
122 // structures for test configuration, including Section.
123 class Section {
124 final StatusFile statusFile;
125
126 final BooleanExpression condition;
127 final List<TestRule> testRules;
128 final int lineNumber;
129
130 Section.always(this.statusFile, this.lineNumber)
131 : condition = null, testRules = new List<TestRule>();
132 Section(this.statusFile, this.condition, this.lineNumber)
133 : testRules = new List<TestRule>();
134
135 bool isEnabled(environment) =>
136 condition == null || condition.evaluate(environment);
137
138 String toString() {
139 return "Section: $condition";
140 }
141 }
142
143 Future<TestExpectations> ReadTestExpectations(List<String> statusFilePaths,
144 Map environment) {
145 var testExpectations = new TestExpectations();
146 return Future.wait(statusFilePaths.map((String statusFile) {
147 return ReadTestExpectationsInto(
148 testExpectations, statusFile, environment);
149 })).then((_) => testExpectations);
150 }
151
152 Future ReadTestExpectationsInto(TestExpectations expectations,
153 String statusFilePath,
154 environment) {
155 var completer = new Completer();
156 List<Section> sections = new List<Section>();
157
158 void sectionsRead() {
159 for (Section section in sections) {
160 if (section.isEnabled(environment)) {
161 for (var rule in section.testRules) {
162 expectations.addRule(rule, environment);
163 }
164 }
165 }
166 completer.complete();
167 }
168
169 ReadConfigurationInto(new Path(statusFilePath), sections, sectionsRead);
170 return completer.future;
171 }
172
173 void ReadConfigurationInto(Path path, sections, onDone) {
174 StatusFile statusFile = new StatusFile(path);
175 File file = new File(path.toNativePath());
176 if (!file.existsSync()) {
177 throw new Exception('Cannot find test status file $path');
178 }
179 int lineNumber = 0;
180 Stream<String> lines =
181 file.openRead()
182 .transform(UTF8.decoder)
183 .transform(new LineSplitter());
184
185 Section currentSection = new Section.always(statusFile, -1);
186 sections.add(currentSection);
187
188
189 lines.listen((String line) {
190 lineNumber++;
191 Match match = SplitComment.firstMatch(line);
192 line = (match == null) ? "" : match[1];
193 line = line.trim();
194 if (line.isEmpty) return;
195
196 // Extract the comment to get the issue number if needed.
197 String comment = (match == null || match[2] == null) ? "" : match[2];
198
199 match = HeaderPattern.firstMatch(line);
200 if (match != null) {
201 String condition_string = match[1].trim();
202 List<String> tokens = new Tokenizer(condition_string).tokenize();
203 ExpressionParser parser = new ExpressionParser(new Scanner(tokens));
204 currentSection =
205 new Section(statusFile, parser.parseBooleanExpression(), lineNumber);
206 sections.add(currentSection);
207 return;
208 }
209
210 match = RulePattern.firstMatch(line);
211 if (match != null) {
212 String name = match[1].trim();
213 // TODO(whesse): Handle test names ending in a wildcard (*).
214 String expression_string = match[2].trim();
215 List<String> tokens = new Tokenizer(expression_string).tokenize();
216 SetExpression expression =
217 new ExpressionParser(new Scanner(tokens)).parseSetExpression();
218
219 // Look for issue number in comment.
220 String issueString = null;
221 match = IssueNumberPattern.firstMatch(comment);
222 if (match != null) {
223 issueString = match[1];
224 if (issueString == null) issueString = match[2];
225 }
226 int issue = issueString != null ? int.parse(issueString) : null;
227 currentSection.testRules.add(
228 new TestRule(name, expression, issue, lineNumber));
229 return;
230 }
231
232 print("unmatched line: $line");
233 },
234 onDone: onDone);
235 }
236
237
238 class TestRule {
239 String name;
240 SetExpression expression;
241 int issue;
242 int lineNumber;
243
244 TestRule(this.name,
245 this.expression,
246 this.issue,
247 this.lineNumber);
248
249 bool get hasIssue => issue != null;
250
251 String toString() => 'TestRule($name, $expression, $issue)';
252 }
253
254
255 class TestExpectations {
256 // Only create one copy of each Set<Expectation>.
257 // We just use .toString as a key, so we may make a few
258 // sets that only differ in their toString element order.
259 static Map _cachedSets = new Map();
260
261 Map _map;
262 bool _preprocessed = false;
263 Map _regExpCache;
264 Map _keyToRegExps;
265
266 /**
267 * Create a TestExpectations object. See the [expectations] method
268 * for an explanation of matching.
269 */
270 TestExpectations() : _map = new Map();
271
272 /**
273 * Add a rule to the expectations.
274 */
275 void addRule(testRule, environment) {
276 // Once we have started using the expectations we cannot add more
277 // rules.
278 if (_preprocessed) {
279 throw "TestExpectations.addRule: cannot add more rules";
280 }
281 var names = testRule.expression.evaluate(environment);
282 var expectations = names.map((name) => Expectation.byName(name));
283 _map.putIfAbsent(testRule.name, () => new Set()).addAll(expectations);
284 }
285
286 /**
287 * Compute the expectations for a test based on the filename.
288 *
289 * For every (key, expectation) pair. Match the key with the file
290 * name. Return the union of the expectations for all the keys
291 * that match.
292 *
293 * Normal matching splits the key and the filename into path
294 * components and checks that the anchored regular expression
295 * "^$keyComponent\$" matches the corresponding filename component.
296 */
297 Set<Expectation> expectations(String filename) {
298 var result = new Set();
299 var splitFilename = filename.split('/');
300
301 // Create mapping from keys to list of RegExps once and for all.
302 _preprocessForMatching();
303
304 _map.forEach((key, expectation) {
305 List regExps = _keyToRegExps[key];
306 if (regExps.length > splitFilename.length) return;
307 for (var i = 0; i < regExps.length; i++) {
308 if (!regExps[i].hasMatch(splitFilename[i])) return;
309 }
310 // If all components of the status file key matches the filename
311 // add the expectations to the result.
312 result.addAll(expectation);
313 });
314
315 // If no expectations were found the expectation is that the test
316 // passes.
317 if (result.isEmpty) {
318 result.add(Expectation.PASS);
319 }
320 return _cachedSets.putIfAbsent(result.toString(), () => result);
321 }
322
323 // Preprocess the expectations for matching against
324 // filenames. Generate lists of regular expressions once and for all
325 // for each key.
326 void _preprocessForMatching() {
327 if (_preprocessed) return;
328
329 _keyToRegExps = new Map();
330 _regExpCache = new Map();
331
332 _map.forEach((key, expectations) {
333 if (_keyToRegExps[key] != null) return;
334 var splitKey = key.split('/');
335 var regExps = new List(splitKey.length);
336 for (var i = 0; i < splitKey.length; i++) {
337 var component = splitKey[i];
338 var regExp = _regExpCache[component];
339 if (regExp == null) {
340 var pattern = "^${splitKey[i]}\$".replaceAll('*', '.*');
341 regExp = new RegExp(pattern);
342 _regExpCache[component] = regExp;
343 }
344 regExps[i] = regExp;
345 }
346 _keyToRegExps[key] = regExps;
347 });
348
349 _regExpCache = null;
350 _preprocessed = true;
351 }
352 }
OLDNEW
« no previous file with comments | « tools/testing/dart/status_expression.dart ('k') | tools/testing/dart/test_configurations.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698