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

Side by Side Diff: tools/gardening_tools/multitest/lib/src/multitest.dart

Issue 3005443002: Additional tools for gardening. (Closed)
Patch Set: Removed accidental commit Created 3 years, 3 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
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 multitest;
6
7 import "dart:io";
8 import "package:path/path.dart" as path;
9 import 'package:base_lib/base_lib.dart';
10 import "utils.dart";
11
12 // import "test_suite.dart";
13
14 // Multitests are Dart test scripts containing lines of the form
15 // " [some dart code] //# [key]: [error type]"
16 //
17 // To support legacy multi tests we also handle lines of the form
18 // " [some dart code] /// [key]: [error type]"
19 //
20 // For each key in the file, a new test file is made containing all
21 // the normal lines of the file, and all of the multitest lines containing
22 // that key, in the same order as in the source file. The new test is expected
23 // to pass if the error type listed is 'ok', or to fail if there is an error
24 // type of type 'compile-time error', 'runtime error', 'static type warning', or
25 // 'dynamic type error'. The type error tests fail only in checked mode.
26 // There is also a test created from only the untagged lines of the file,
27 // with key "none", which is expected to pass. This library extracts these
28 // tests, writes them into a temporary directory, and passes them to the test
29 // runner. These tests may be referred to in the status files with the
30 // pattern [test name]/[key].
31 //
32 // For example: file I_am_a_multitest.dart
33 // aaa
34 // bbb //# 02: runtime error
35 // ccc //# 02: continued
36 // ddd //# 07: static type warning
37 // eee //# 10: ok
38 // fff
39 //
40 // should create four tests:
41 // I_am_a_multitest_none.dart
42 // aaa
43 // fff
44 //
45 // I_am_a_multitest_02.dart
46 // aaa
47 // bbb //# 02: runtime error
48 // ccc //# 02: continued
49 // fff
50 //
51 // I_am_a_multitest_07.dart
52 // aaa
53 // ddd //# 07: static type warning
54 // fff
55 //
56 // and I_am_a_multitest_10.dart
57 // aaa
58 // eee //# 10: ok
59 // fff
60 //
61 // Note that it is possible to indicate more than one acceptable outcome
62 // in the case of dynamic and static type warnings
63 // aaa
64 // ddd //# 07: static type warning, dynamic type error
65 // fff
66
67 /// Until legacy multitests are ported we need to support both /// and //#
68 final _multitestMarker = new RegExp(r"//[/#]");
69
70 class ExtractMultitestResult {
71 Map<String, String> tests;
72 Map<String, Set<String>> outcomes;
73
74 ExtractMultitestResult(this.tests, this.outcomes);
75 }
76
77 ExtractMultitestResult ExtractTestsFromMultitest(
78 String filePath, Logger logger) {
79 // Read the entire file into a byte buffer and transform it to a
80 // String. This will treat the file as ascii but the only parts
81 // we are interested in will be ascii in any case.
82
83 Map<String, String> tests = {};
84 Map<String, Set<String>> outcomes = {};
85
86 var bytes = new File(path.absolute(filePath)).readAsBytesSync();
87 var contents = decodeUtf8(bytes);
88 var firstNewline = contents.indexOf('\n');
89 var lineSeparator =
90 (firstNewline == 0 || contents[firstNewline - 1] != '\r') ? '\n' : '\r\n';
91 var lines = contents.split(lineSeparator);
92 if (lines.last == '') lines.removeLast();
93 bytes = null;
94 contents = null;
95 var validMultitestOutcomes = [
96 'ok',
97 'compile-time error',
98 'runtime error',
99 'static type warning',
100 'dynamic type error',
101 'checked mode compile-time error'
102 ].toSet();
103
104 // Create the set of multitests, which will have a new test added each
105 // time we see a multitest line with a new key.
106 var testsAsLines = <String, List<String>>{};
107
108 // Add the default case with key "none".
109 testsAsLines['none'] = <String>[];
110 outcomes['none'] = new Set<String>();
111
112 var lineCount = 0;
113 for (var line in lines) {
114 lineCount++;
115 var annotation = new _Annotation.from(line);
116 if (annotation != null) {
117 testsAsLines.putIfAbsent(
118 annotation.key, () => new List<String>.from(testsAsLines["none"]));
119 // Add line to test with annotation.key as key, empty line to the rest.
120 for (var key in testsAsLines.keys) {
121 testsAsLines[key].add(annotation.key == key ? line : "");
122 }
123 outcomes.putIfAbsent(annotation.key, () => new Set<String>());
124 if (annotation.rest != 'continued') {
125 for (String nextOutcome in annotation.outcomesList) {
126 if (validMultitestOutcomes.contains(nextOutcome)) {
127 outcomes[annotation.key].add(nextOutcome);
128 } else {
129 logger.warning(
130 "Warning: Invalid test directive '$nextOutcome' on line "
131 "${lineCount}:\n${annotation.rest} ");
132 }
133 }
134 }
135 } else {
136 for (var test in testsAsLines.values) test.add(line);
137 }
138 }
139 // End marker, has a final line separator so we don't need to add it after
140 // joining the lines.
141 var marker =
142 '// Test created from multitest named ${path.absolute(filePath)}.'
143 '$lineSeparator';
144 for (var test in testsAsLines.values) test.add(marker);
145
146 var keysToDelete = <String>[];
147 // Check that every key (other than the none case) has at least one outcome
148 for (var outcomeKey in outcomes.keys) {
149 if (outcomeKey != 'none' && outcomes[outcomeKey].isEmpty) {
150 logger.warning(
151 "Warning: Test ${outcomeKey} has no valid annotated outcomes.\n"
152 "Expected one of: ${validMultitestOutcomes.toString()}");
153 // If this multitest doesn't have an outcome, mark the multitest for
154 // deletion.
155 keysToDelete.add(outcomeKey);
156 }
157 }
158 // If a key/multitest was marked for deletion, do the necessary cleanup.
159 keysToDelete.forEach(outcomes.remove);
160 keysToDelete.forEach(testsAsLines.remove);
161
162 // Copy all the tests into the output map tests, as multiline strings.
163 for (var key in testsAsLines.keys) {
164 tests[key] = testsAsLines[key].join(lineSeparator);
165 }
166
167 return new ExtractMultitestResult(tests, outcomes);
168 }
169
170 // Represents a mutlitest annotation in the special //# comment.
171 class _Annotation {
172 String key;
173 String rest;
174 List<String> outcomesList;
175 _Annotation() {}
176 factory _Annotation.from(String line) {
177 // Do an early return with "null" if this is not a valid multitest
178 // annotation.
179 if (!line.contains(_multitestMarker)) {
180 return null;
181 }
182 var parts = line
183 .split(_multitestMarker)[1]
184 .split(':')
185 .map((s) => s.trim())
186 .where((s) => s.length > 0)
187 .toList();
188 if (parts.length <= 1) {
189 return null;
190 }
191
192 var annotation = new _Annotation();
193 annotation.key = parts[0];
194 annotation.rest = parts[1];
195 annotation.outcomesList =
196 annotation.rest.split(',').map((s) => s.trim()).toList();
197 return annotation;
198 }
199 }
200
201 // // Find all relative imports and copy them into the dir that contains
202 // // the generated tests.
203 // Set<String> _findAllRelativeImports(String topLibrary) {
204 // var toSearch = [topLibrary].toSet();
205 // var foundImports = new Set<String>();
206 // var libraryDir = topLibrary.directoryPath;
207 // var relativeImportRegExp = new RegExp(
208 // '^(?:@.*\\s+)?' // Allow for a meta-data annotation.
209 // '(import|part)'
210 // '\\s+["\']'
211 // '(?!(dart:|dart-ext:|data:|package:|/))' // Look-ahead: not in package.
212 // '([^"\']*)' // The path to the imported file.
213 // '["\']');
214 // while (!toSearch.isEmpty) {
215 // var thisPass = toSearch;
216 // toSearch = new Set<String>();
217 // for (String filename in thisPass) {
218 // File f = new File(path.absolute(filename));
219 // for (String line in f.readAsLinesSync()) {
220 // Match match = relativeImportRegExp.firstMatch(line);
221 // if (match != null) {
222 // Path relativePath = new Path(match.group(3));
223 // if (foundImports.contains(relativePath.toString())) {
224 // continue;
225 // }
226 // if (relativePath.toString().contains('..')) {
227 // // This is just for safety reasons, we don't want
228 // // to unintentionally clobber files relative to the destination
229 // // dir when copying them ove.
230 // print("relative paths containing .. are not allowed.");
231 // exit(1);
232 // }
233 // foundImports.add(relativePath.toString());
234 // toSearch.add(libraryDir.join(relativePath));
235 // }
236 // }
237 // }
238 // }
239 // return foundImports;
240 // }
241
242 // Future doMultitest(Path filePath, String outputDir, Path suiteDir,
243 // CreateTest doTest, bool hotReload) {
244 // void writeFile(String filepath, String content) {
245 // final File file = new File(filepath);
246
247 // if (file.existsSync()) {
248 // var oldContent = file.readAsStringSync();
249 // if (oldContent == content) {
250 // // Don't write to the file if the content is the same
251 // return;
252 // }
253 // }
254 // file.writeAsStringSync(content);
255 // }
256
257 // // Each new test is a single String value in the Map tests.
258 // Map<String, String> tests = new Map<String, String>();
259 // Map<String, Set<String>> outcomes = new Map<String, Set<String>>();
260 // ExtractTestsFromMultitest(filePath, tests, outcomes);
261
262 // Path sourceDir = filePath.directoryPath;
263 // Path targetDir = createMultitestDirectory(outputDir, suiteDir, sourceDir);
264 // assert(targetDir != null);
265
266 // // Copy all the relative imports of the multitest.
267 // Set<String> importsToCopy = _findAllRelativeImports(filePath);
268 // List<Future> futureCopies = [];
269 // for (String relativeImport in importsToCopy) {
270 // Path importPath = new Path(relativeImport);
271 // // Make sure the target directory exists.
272 // Path importDir = importPath.directoryPath;
273 // if (!importDir.isEmpty) {
274 // TestUtils.mkdirRecursive(targetDir, importDir);
275 // }
276 // // Copy file.
277 // futureCopies.add(TestUtils.copyFile(
278 // sourceDir.join(importPath), targetDir.join(importPath)));
279 // }
280
281 // // Wait until all imports are copied before scheduling test cases.
282 // return Future.wait(futureCopies).then((_) {
283 // String baseFilename = filePath.filenameWithoutExtension;
284 // for (String key in tests.keys) {
285 // final Path multitestFilename =
286 // targetDir.append('${baseFilename}_$key.dart');
287 // writeFile(multitestFilename.toNativePath(), tests[key]);
288 // Set<String> outcome = outcomes[key];
289 // bool hasStaticWarning = outcome.contains('static type warning');
290 // bool hasRuntimeErrors = outcome.contains('runtime error');
291 // bool hasCompileError = outcome.contains('compile-time error');
292 // bool isNegativeIfChecked = outcome.contains('dynamic type error');
293 // bool hasCompileErrorIfChecked =
294 // outcome.contains('checked mode compile-time error');
295 // if (hotReload) {
296 // if (hasCompileError || hasCompileErrorIfChecked) {
297 // // Running a test that expects a compilation error with hot reloadi ng
298 // // is redundant with a regular run of the test.
299 // continue;
300 // }
301 // }
302 // doTest(multitestFilename, filePath, hasCompileError, hasRuntimeErrors,
303 // isNegativeIfChecked: isNegativeIfChecked,
304 // hasCompileErrorIfChecked: hasCompileErrorIfChecked,
305 // hasStaticWarning: hasStaticWarning,
306 // multitestKey: key);
307 // }
308
309 // return null;
310 // });
311 // }
312
313 // String suiteNameFromPath(Path suiteDir) {
314 // var split = suiteDir.segments();
315 // // co19 test suite is at tests/co19/src.
316 // if (split.last == 'src') {
317 // split.removeLast();
318 // }
319 // return split.last;
320 // }
321
322 // Path createMultitestDirectory(String outputDir, Path suiteDir, Path sourceDir ) {
323 // Path relative = sourceDir.relativeTo(suiteDir);
324 // Path path = new Path(outputDir)
325 // .append('generated_tests')
326 // .append(suiteNameFromPath(suiteDir))
327 // .join(relative);
328 // TestUtils.mkdirRecursive(TestUtils.currentWorkingDirectory, path);
329 // return new Path(new File(path.toNativePath()).absolute.path);
330 // }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698