OLD | NEW |
---|---|
1 //#!/usr/bin/env dart | 1 //#!/usr/bin/env dart |
2 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 2 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
3 // for details. All rights reserved. Use of this source code is governed by a | 3 // for details. All rights reserved. Use of this source code is governed by a |
4 // BSD-style license that can be found in the LICENSE file. | 4 // BSD-style license that can be found in the LICENSE file. |
5 | 5 |
6 /** | 6 /** |
7 * testrunner is a program to run Dart unit tests. Unlike $DART/tools/test.dart, | 7 * testrunner is a program to run Dart unit tests. Unlike $DART/tools/test.dart, |
8 * this program is intended for 3rd parties to be able to run unit tests in | 8 * this program is intended for 3rd parties to be able to run unit tests in |
9 * a batched fashion. As such, it adds some features and removes others. Some | 9 * a batched fashion. As such, it adds some features and removes others. Some |
10 * of the removed features are: | 10 * of the removed features are: |
(...skipping 25 matching lines...) Expand all Loading... | |
36 * | 36 * |
37 * Options can be specified on the command line, via a configuration | 37 * Options can be specified on the command line, via a configuration |
38 * file (`--config`) or via a test.config file in the test directory, | 38 * file (`--config`) or via a test.config file in the test directory, |
39 * in decreasing order of priority. | 39 * in decreasing order of priority. |
40 * | 40 * |
41 * The three runtimes are: | 41 * The three runtimes are: |
42 * | 42 * |
43 * vm - run native Dart in the VM; i.e. using $DARTSDK/dart-sdk/bin/dart. | 43 * vm - run native Dart in the VM; i.e. using $DARTSDK/dart-sdk/bin/dart. |
44 * drt-dart - run native Dart in DumpRenderTree, the headless version of | 44 * drt-dart - run native Dart in DumpRenderTree, the headless version of |
45 * Dartium, which is located in $DARTSDK/chromium/DumpRenderTree, if | 45 * Dartium, which is located in $DARTSDK/chromium/DumpRenderTree, if |
46 * you intsalled the SDK that is bundled with the editor, or available | 46 * you installed the SDK that is bundled with the editor, or available |
47 * from http://gsdview.appspot.com/dartium-archive/continuous/ | 47 * from http://gsdview.appspot.com/dartium-archive/continuous/ |
48 * otherwise. | 48 * otherwise. |
49 * | 49 * |
50 * drt-js - run Dart compiled to Javascript in DumpRenderTree. | 50 * drt-js - run Dart compiled to Javascript in DumpRenderTree. |
51 * | 51 * |
52 * testrunner supports simple DOM render tests. These can use expected values | 52 * testrunner supports simple DOM render tests. These can use expected values |
53 * for the render output from DumpRenderTree, either are textual DOM | 53 * for the render output from DumpRenderTree, either are textual DOM |
54 * descriptions (`--layout-tests`) or pixel renderings (`--pixel-tests`). | 54 * descriptions (`--layout-tests`) or pixel renderings (`--pixel-tests`). |
55 * When running layout tests, testrunner will see if there is a file with | 55 * When running layout tests, testrunner will see if there is a file with |
56 * a .png or a .txt extension in a directory with the same name as the | 56 * a .png or a .txt extension in a directory with the same name as the |
57 * test file (without extension) and with the test name as the file name. | 57 * test file (without extension) and with the test name as the file name. |
58 * For example, if there is a test file foo_test.dart with tests 'test1' | 58 * For example, if there is a test file foo_test.dart with tests 'test1' |
59 * and 'test2', it will look for foo_test/test1.txt and foo_test/test2.txt | 59 * and 'test2', it will look for foo_test/test1.txt and foo_test/test2.txt |
60 * for text render layout files. If these exist it will do additional checks | 60 * for text render layout files. If these exist it will do additional checks |
61 * of the rendered layout; if not, the test will fail. | 61 * of the rendered layout; if not, the test will fail. |
62 * | 62 * |
63 * Layout file (re)generation can be done using `--regenerate`. This will | 63 * Layout file (re)generation can be done using `--regenerate`. This will |
64 * create or update the layout files (and implicitly pass the tests). | 64 * create or update the layout files (and implicitly pass the tests). |
65 * | 65 * |
66 * The wrapping and execution of test files is handled by test_pipeline.dart, | 66 * The wrapping and execution of test files is handled by test_pipeline.dart, |
67 * which is run in an isolate. The `--pipeline` argument can be used to | 67 * which is run in an isolate. The `--pipeline` argument can be used to |
68 * specify a different script for running a test file pipeline, allowing | 68 * specify a different script for running a test file pipeline, allowing |
69 * customization of the pipeline. | 69 * customization of the pipeline. |
70 * | |
71 * Wrapper files are created for tests in the tmp directory, which can be | |
72 * overridden with --tempdir. These files are not removed after the tests | |
73 * are complete, primarily to reduce the amount of times pub must be | |
74 * executed. You can use --clean-files to force file cleanup. The temp | |
75 * directories will have pubspec.yaml files auto-generated unless the | |
76 * original test file directories have such files; in that case the existing | |
77 * files will be copied. Whenever a new pubspec file is copied or | |
78 * created pub will be run (but not otherwise - so if you want to do | |
79 * the equivelent of pub update you should use --clean-files and the rerun | |
80 * the tests). | |
70 */ | 81 */ |
71 | 82 |
72 // TODO - layout tests that use PNGs rather than DRT text render dumps. | |
73 library testrunner; | 83 library testrunner; |
84 import 'dart:async'; | |
74 import 'dart:io'; | 85 import 'dart:io'; |
75 import 'dart:isolate'; | 86 import 'dart:isolate'; |
76 import 'dart:math'; | 87 import 'dart:math'; |
77 import 'package:args/args.dart'; | 88 import 'package:args/args.dart'; |
78 | 89 |
79 part 'options.dart'; | 90 part 'options.dart'; |
80 part 'utils.dart'; | 91 part 'utils.dart'; |
81 | 92 |
82 /** The set of [PipelineRunner]s to execute. */ | 93 /** The set of [PipelineRunner]s to execute. */ |
83 List _tasks; | 94 List _tasks; |
84 | 95 |
85 /** The maximum number of pipelines that can run concurrently. */ | 96 /** The maximum number of pipelines that can run concurrently. */ |
86 int _maxTasks; | 97 int _maxTasks; |
87 | 98 |
88 /** The number of pipelines currently running. */ | 99 /** The number of pipelines currently running. */ |
89 int _numTasks; | 100 int _numTasks; |
90 | 101 |
91 /** The index of the next pipeline runner to execute. */ | 102 /** The index of the next pipeline runner to execute. */ |
92 int _nextTask; | 103 int _nextTask; |
93 | 104 |
94 /** The stream to use for high-value messages, like test results. */ | 105 /** The sink to use for high-value messages, like test results. */ |
95 OutputStream _outStream; | 106 IOSink _outSink; |
96 | 107 |
97 /** The stream to use for low-value messages, like verbose output. */ | 108 /** The sink to use for low-value messages, like verbose output. */ |
98 OutputStream _logStream; | 109 IOSink _logSink; |
99 | 110 |
100 /** | 111 /** |
112 * The last temp test directory we accessed; we use this to know if we | |
113 * need to check the pub configuration. | |
114 */ | |
115 String _testDir; | |
116 | |
117 /** | |
101 * The user can specify output streams on the command line, using 'none', | 118 * The user can specify output streams on the command line, using 'none', |
102 * 'stdout', 'stderr', or a file path; [getStream] will take such a name | 119 * 'stdout', 'stderr', or a file path; [getSink] will take such a name |
103 * and return an appropriate [OutputStream]. | 120 * and return an appropriate [IOSink]. |
104 */ | 121 */ |
105 OutputStream getStream(String name) { | 122 IOSink getSink(String name) { |
106 if (name == null || name == 'none') { | 123 if (name == null || name == 'none') { |
107 return null; | 124 return null; |
108 } | 125 } |
109 if (name == 'stdout') { | 126 if (name == 'stdout') { |
110 return stdout; | 127 return stdout; |
111 } | 128 } |
112 if (name == 'stderr') { | 129 if (name == 'stderr') { |
113 return stderr; | 130 return stderr; |
114 } | 131 } |
115 return new File(name).openOutputStream(FileMode.WRITE); | 132 var f = new File(name); |
133 return f.openWrite(); | |
116 } | 134 } |
117 | 135 |
118 /** | 136 /** |
119 * Given a [List] of [testFiles], either print the list or create | 137 * Given a [List] of [testFiles], either print the list or create |
120 * and execute pipelines for the files. | 138 * and execute pipelines for the files. |
121 */ | 139 */ |
122 void processTests(Map config, List testFiles) { | 140 void processTests(Map config, List testFiles) { |
123 _outStream = getStream(config['out']); | 141 _outSink = getSink(config['out']); |
124 _logStream = getStream(config['log']); | 142 _logSink = getSink(config['log']); |
125 if (config['list-files']) { | 143 if (config['list-files']) { |
126 if (_outStream != null) { | 144 if (_outSink != null) { |
127 for (var i = 0; i < testFiles.length; i++) { | 145 for (var i = 0; i < testFiles.length; i++) { |
128 _outStream.writeString(testFiles[i]); | 146 _outSink.write(testFiles[i]); |
129 _outStream.writeString('\n'); | 147 _outSink.write('\n'); |
130 } | 148 } |
131 } | 149 } |
132 } else { | 150 } else { |
133 _maxTasks = min(config['tasks'], testFiles.length); | 151 _maxTasks = min(config['tasks'], testFiles.length); |
134 _numTasks = 0; | 152 _numTasks = 0; |
135 _nextTask = 0; | 153 _nextTask = 0; |
136 spawnTasks(config, testFiles); | 154 spawnTasks(config, testFiles); |
137 } | 155 } |
138 } | 156 } |
139 | 157 |
158 /** | |
159 * Create or update a pubspec for the target test directory. We use the | |
160 * source directory pubspec if available; otherwise we create a minimal one. | |
161 * We return a Future if we are running pub install, or null otherwise. | |
162 */ | |
163 Future doPubConfig(Path sourcePath, String sourceDir, | |
Siggi Cherem (dart-lang)
2013/04/19 21:39:39
why do we need to run pub?
when users run it, pu
Siggi Cherem (dart-lang)
2013/04/19 21:48:38
after our discussion, I now understand that you ar
gram
2013/04/22 23:54:27
Done.
gram
2013/04/22 23:54:27
Done.
| |
164 Path targetPath, String targetDir, | |
165 String pub, String runtime) { | |
166 // Make sure the target directory exists. | |
167 var d = new Directory(targetDir); | |
168 if (!d.existsSync()) { | |
169 d.createSync(recursive: true); | |
170 } | |
171 | |
172 // If the source has no pubspec, but the dest does, leave | |
173 // things as they are. If neither do, create one in dest. | |
174 | |
175 var sourcePubSpecName = new Path(sourceDir).append("pubspec.yaml"). | |
Siggi Cherem (dart-lang)
2013/04/19 21:48:38
one option here is that you could simply simlink t
gram
2013/04/22 23:54:27
Done.
| |
176 toNativePath(); | |
177 var targetPubSpecName = new Path(targetDir).append("pubspec.yaml"). | |
178 toNativePath(); | |
179 var sourcePubSpec = new File(sourcePubSpecName); | |
180 var targetPubSpec = new File(targetPubSpecName); | |
181 | |
182 if (!sourcePubSpec.existsSync()) { | |
183 if (targetPubSpec.existsSync()) { | |
Siggi Cherem (dart-lang)
2013/04/19 21:48:38
is this branch possible? (won't they use pub to ge
gram
2013/04/22 23:54:27
It's possible if there is no source pub, but they
| |
184 return null; | |
185 } else { | |
186 // Create one. | |
187 if (runtime == 'vm') { | |
188 writeFile(targetPubSpecName, | |
189 "name: testrunner\ndependencies:\n unittest: any\n"); | |
190 } else { | |
191 writeFile(targetPubSpecName, | |
192 "name: testrunner\ndependencies:\n unittest: any\n browser: any\n"); | |
Siggi Cherem (dart-lang)
2013/04/19 21:48:38
make sure you include 'browser' as a testrunner de
gram
2013/04/22 23:54:27
Browser is already there at the end of the line :-
Siggi Cherem (dart-lang)
2013/04/23 01:10:04
Sorry, what I trying to say was that if you use th
gram
2013/04/23 22:53:40
The tests are run directly; testrunner just genera
Siggi Cherem (dart-lang)
2013/04/24 16:34:15
Just to summarize our offline discussion - users d
| |
193 } | |
194 } | |
195 } else { | |
196 if (targetPubSpec.existsSync()) { | |
197 // If there is a source one, and it is older than the target, | |
198 // leave the target as is. | |
199 if (sourcePubSpec.lastModifiedSync().millisecondsSinceEpoch < | |
200 targetPubSpec.lastModifiedSync().millisecondsSinceEpoch) { | |
201 return null; | |
202 } | |
203 } | |
204 // Source exists and is newer than target or there is no target; | |
205 // copy the source to the target. | |
206 var s = sourcePubSpec.readAsStringSync(); | |
207 targetPubSpec.writeAsStringSync(s); | |
208 } | |
209 // A new target pubspec was created so run pub install. | |
210 return _processHelper(pub, [ 'install' ], workingDir: targetDir); | |
211 } | |
212 | |
140 /** Execute as many tasks as possible up to the maxTasks limit. */ | 213 /** Execute as many tasks as possible up to the maxTasks limit. */ |
141 void spawnTasks(Map config, List testFiles) { | 214 void spawnTasks(Map config, List testFiles) { |
142 var verbose = config['verbose']; | 215 var verbose = config['verbose']; |
143 // If we were running in the VM and the immediate flag was set, we have | 216 // If we were running in the VM and the immediate flag was set, we have |
144 // already printed the important messages (i.e. prefixed with ###), | 217 // already printed the important messages (i.e. prefixed with ###), |
145 // so we should skip them now. | 218 // so we should skip them now. |
146 var skipNonVerbose = config['immediate'] && config['runtime'] == 'vm'; | 219 var skipNonVerbose = config['immediate'] && config['runtime'] == 'vm'; |
147 while (_numTasks < _maxTasks && _nextTask < testFiles.length) { | 220 while (_numTasks < _maxTasks && _nextTask < testFiles.length) { |
148 ++_numTasks; | 221 ++_numTasks; |
149 var testfile = testFiles[_nextTask++]; | 222 var testfile = testFiles[_nextTask++]; |
150 config['testfile'] = testfile; | 223 config['testfile'] = testfile; |
151 ReceivePort port = new ReceivePort(); | 224 ReceivePort port = new ReceivePort(); |
152 port.receive((msg, _) { | 225 port.receive((msg, _) { |
153 List stdout = msg[0]; | 226 List stdout = msg[0]; |
154 List stderr = msg[1]; | 227 List stderr = msg[1]; |
155 List log = msg[2]; | 228 List log = msg[2]; |
156 int exitCode = msg[3]; | 229 int exitCode = msg[3]; |
157 writelog(stdout, _outStream, _logStream, verbose, skipNonVerbose); | 230 writelog(stdout, _outSink, _logSink, verbose, skipNonVerbose); |
158 writelog(stderr, _outStream, _logStream, true, skipNonVerbose); | 231 writelog(stderr, _outSink, _logSink, true, skipNonVerbose); |
159 writelog(log, _outStream, _logStream, verbose, skipNonVerbose); | 232 writelog(log, _outSink, _logSink, verbose, skipNonVerbose); |
160 port.close(); | 233 port.close(); |
161 --_numTasks; | 234 --_numTasks; |
162 if (exitCode == 0 || !config['stopOnFailure']) { | 235 if (exitCode == 0 || !config['stop-on-failure']) { |
163 spawnTasks(config, testFiles); | 236 spawnTasks(config, testFiles); |
164 } | 237 } |
165 if (_numTasks == 0) { | 238 if (_numTasks == 0) { |
166 // No outstanding tasks; we're all done. | 239 // No outstanding tasks; we're all done. |
167 // We could later print a summary report here. | 240 // We could later print a summary report here. |
168 } | 241 } |
169 }); | 242 }); |
170 SendPort s = spawnUri(config['pipeline']); | 243 SendPort s = spawnUri(config['pipeline']); |
171 s.send(config, port.toSendPort()); | 244 |
245 // Get the names of the source and target test files and containing | |
246 // directories. | |
247 var testPath = new Path(testfile); | |
248 var sourcePath = testPath.directoryPath; | |
249 var sourceDir = sourcePath.toNativePath(); | |
250 | |
251 var targetPath = new Path(config["tempdir"]); | |
252 var normalizedTarget = testPath.directoryPath.toNativePath() | |
253 .replaceAll(Platform.pathSeparator, '_') | |
254 .replaceAll(':', '_'); | |
255 targetPath = targetPath.append("${normalizedTarget}_${config['runtime']}"); | |
256 var targetDir = targetPath.toNativePath(); | |
257 | |
258 config['targetDir'] = targetDir; | |
259 // If this is a new target dir, we need to redo the pub check. | |
260 var f = null; | |
261 if (targetDir != _testDir) { | |
262 f = doPubConfig(sourcePath, sourceDir, targetPath, targetDir, | |
263 config['pub'], config['runtime']); | |
264 _testDir = targetDir; | |
265 } | |
266 if (f == null) { | |
267 s.send(config, port.toSendPort()); | |
268 } else { | |
269 f.then((_) { | |
270 s.send(config, port.toSendPort()); | |
271 }); | |
272 break; // Don't do any more until pub is done. | |
273 } | |
172 } | 274 } |
173 } | 275 } |
174 | 276 |
175 /** | 277 /** |
176 * Our tests are configured so that critical messages have a '###' prefix. | 278 * Our tests are configured so that critical messages have a '###' prefix. |
177 * [writeLog] takes the output from a pipeline execution and writes it to | 279 * [writelog] takes the output from a pipeline execution and writes it to |
178 * our output streams. It will strip the '###' if necessary on critical | 280 * our output sinks. It will strip the '###' if necessary on critical |
179 * messages; other messages will only be written if verbose output was | 281 * messages; other messages will only be written if verbose output was |
180 * specified. | 282 * specified. |
181 */ | 283 */ |
182 void writelog(List messages, OutputStream out, OutputStream log, | 284 void writelog(List messages, IOSink out, IOSink log, |
183 bool includeVerbose, bool skipNonVerbose) { | 285 bool includeVerbose, bool skipNonVerbose) { |
184 for (var i = 0; i < messages.length; i++) { | 286 for (var i = 0; i < messages.length; i++) { |
185 var msg = messages[i]; | 287 var msg = messages[i]; |
186 if (msg.startsWith('###')) { | 288 if (msg.startsWith('###')) { |
187 if (!skipNonVerbose && out != null) { | 289 if (!skipNonVerbose && out != null) { |
188 out.writeString(msg.substring(3)); | 290 out.write(msg.substring(3)); |
189 out.writeString('\n'); | 291 out.write('\n'); |
190 } | 292 } |
191 } else if (msg.startsWith('CONSOLE MESSAGE:')) { | 293 } else if (msg.startsWith('CONSOLE MESSAGE:')) { |
192 if (!skipNonVerbose && out != null) { | 294 if (!skipNonVerbose && out != null) { |
193 int idx = msg.indexOf('###'); | 295 int idx = msg.indexOf('###'); |
194 if (idx > 0) { | 296 if (idx > 0) { |
195 out.writeString(msg.substring(idx + 3)); | 297 out.write(msg.substring(idx + 3)); |
196 out.writeString('\n'); | 298 out.write('\n'); |
197 } | 299 } |
198 } | 300 } |
199 } else if (includeVerbose && log != null) { | 301 } else if (includeVerbose && log != null) { |
200 log.writeString(msg); | 302 log.write(msg); |
201 log.writeString('\n'); | 303 log.write('\n'); |
202 } | 304 } |
203 } | 305 } |
204 } | 306 } |
205 | 307 |
206 sanitizeConfig(Map config, ArgParser parser) { | 308 normalizeFilter(List filter) { |
309 // We want the filter to be a quoted string or list of quoted | |
310 // strings. | |
311 for (var i = 0; i < filter.length; i++) { | |
312 var f = filter[i]; | |
313 if (f[0] != "'" && f[0] != '"') { | |
314 filter[i] = "'$f'"; // TODO(gram): Quote embedded quotes. | |
315 } | |
316 } | |
317 return filter; | |
318 } | |
319 | |
320 void sanitizeConfig(Map config, ArgParser parser) { | |
207 config['layout'] = config['layout-text'] || config['layout-pixel']; | 321 config['layout'] = config['layout-text'] || config['layout-pixel']; |
208 | |
209 // TODO - check if next three are actually used. | |
210 config['runInBrowser'] = (config['runtime'] != 'vm'); | |
211 config['verbose'] = (config['log'] != 'none' && !config['list-groups']); | 322 config['verbose'] = (config['log'] != 'none' && !config['list-groups']); |
212 config['filtering'] = (config['include'].length > 0 || | |
213 config['exclude'].length > 0); | |
214 | |
215 config['timeout'] = int.parse(config['timeout']); | 323 config['timeout'] = int.parse(config['timeout']); |
216 config['tasks'] = int.parse(config['tasks']); | 324 config['tasks'] = int.parse(config['tasks']); |
217 | 325 |
218 var dartsdk = config['dartsdk']; | 326 var dartsdk = config['dartsdk']; |
219 var pathSep = Platform.pathSeparator; | 327 var pathSep = Platform.pathSeparator; |
220 | 328 |
221 if (dartsdk != null) { | 329 if (dartsdk == null) { |
222 if (parser.getDefault('dart2js') == config['dart2js']) { | 330 var opt = new Options(); |
223 config['dart2js'] = | 331 var runner = opt.executable; |
224 '$dartsdk${pathSep}dart-sdk${pathSep}bin${pathSep}dart2js'; | 332 var idx = runner.indexOf('dart-sdk'); |
333 if (idx < 0) { | |
334 print("Please use --dartsdk option or run using the dart executable " | |
335 "from the Dart SDK"); | |
336 exit(0); | |
225 } | 337 } |
226 if (parser.getDefault('dart') == config['dart']) { | 338 dartsdk = runner.substring(0, idx); |
227 config['dart'] = '$dartsdk${pathSep}dart-sdk${pathSep}bin${pathSep}dart'; | 339 } |
228 } | 340 if (Platform.operatingSystem == 'macos') { |
229 if (parser.getDefault('drt') == config['drt']) { | 341 config['dart2js'] = |
230 config['drt'] = '$dartsdk${pathSep}chromium${pathSep}DumpRenderTree'; | 342 '$dartsdk${pathSep}dart-sdk${pathSep}bin${pathSep}dart2js'; |
231 } | 343 config['dart'] = '$dartsdk${pathSep}dart-sdk${pathSep}bin${pathSep}dart'; |
344 config['pub'] = '$dartsdk${pathSep}dart-sdk${pathSep}bin${pathSep}pub'; | |
345 config['drt'] = | |
346 '$dartsdk/chromium/DumpRenderTree.app/Contents/MacOS/DumpRenderTree'; | |
347 } else if (Platform.operatingSystem == 'linux') { | |
348 config['dart2js'] = | |
349 '$dartsdk${pathSep}dart-sdk${pathSep}bin${pathSep}dart2js'; | |
350 config['dart'] = '$dartsdk${pathSep}dart-sdk${pathSep}bin${pathSep}dart'; | |
351 config['pub'] = '$dartsdk${pathSep}dart-sdk${pathSep}bin${pathSep}pub'; | |
352 config['drt'] = '$dartsdk${pathSep}chromium${pathSep}DumpRenderTree'; | |
353 } else { | |
354 config['dart2js'] = | |
355 '$dartsdk${pathSep}dart-sdk${pathSep}bin${pathSep}dart2js.bat'; | |
356 config['dart'] = '$dartsdk${pathSep}dart-sdk${pathSep}bin${pathSep}dart.exe' ; | |
357 config['pub'] = '$dartsdk${pathSep}dart-sdk${pathSep}bin${pathSep}pub.bat'; | |
358 config['drt'] = '$dartsdk${pathSep}chromium${pathSep}DumpRenderTree.exe'; | |
232 } | 359 } |
233 | 360 |
234 config['unittest'] = makePathAbsolute(config['unittest']); | 361 for (var prog in [ 'drt', 'dart', 'pub', 'dart2js' ]) { |
235 config['drt'] = makePathAbsolute(config['drt']); | 362 config[prog] = makePathAbsolute(config[prog]); |
236 config['dart'] = makePathAbsolute(config['dart']); | 363 } |
237 config['dart2js'] = makePathAbsolute(config['dart2js']); | |
238 config['runnerDir'] = runnerDirectory; | 364 config['runnerDir'] = runnerDirectory; |
365 config['include'] = normalizeFilter(config['include']); | |
366 config['exclude'] = normalizeFilter(config['exclude']); | |
239 } | 367 } |
240 | 368 |
241 main() { | 369 main() { |
242 var optionsParser = getOptionParser(); | 370 var optionsParser = getOptionParser(); |
243 var options = loadConfiguration(optionsParser); | 371 var options = loadConfiguration(optionsParser); |
244 if (isSane(options)) { | 372 if (isSane(options)) { |
245 if (options['list-options']) { | 373 if (options['list-options']) { |
246 printOptions(optionsParser, options, false, stdout); | 374 printOptions(optionsParser, options, false, stdout); |
247 } else if (options['list-all-options']) { | 375 } else if (options['list-all-options']) { |
248 printOptions(optionsParser, options, true, stdout); | 376 printOptions(optionsParser, options, true, stdout); |
249 } else { | 377 } else { |
250 var config = new Map(); | 378 var config = new Map(); |
251 for (var option in options.options) { | 379 for (var option in options.options) { |
252 config[option] = options[option]; | 380 config[option] = options[option]; |
253 } | 381 } |
254 var rest = []; | 382 var rest = []; |
255 // Process the remmaining command line args. If they look like | 383 // Process the remmaining command line args. If they look like |
256 // options then split them up and add them to the map; they may be for | 384 // options then split them up and add them to the map; they may be for |
257 // custom pipelines. | 385 // custom pipelines. |
258 for (var other in options.rest) { | 386 for (var other in options.rest) { |
259 var idx; | 387 var idx; |
260 if (other.startsWith('--') && (idx = other.indexOf('=')) > 0) { | 388 if (other.startsWith('--') && (idx = other.indexOf('=')) > 0) { |
261 var optName = other.substring(2, idx); | 389 var optName = other.substring(2, idx); |
262 var optValue = other.substring(idx+1); | 390 var optValue = other.substring(idx+1); |
263 config[optName] = optValue; | 391 config[optName] = optValue; |
264 } else { | 392 } else { |
265 rest.add(other); | 393 rest.add(other); |
266 } | 394 } |
267 } | 395 } |
268 | 396 |
269 sanitizeConfig(config, optionsParser); | 397 sanitizeConfig(config, optionsParser); |
270 | 398 |
271 // Build the list of tests and then execute them. | 399 // Build the list of tests and then execute them. |
272 List dirs = rest; | 400 List dirs = rest; |
273 if (dirs.length == 0) { | 401 if (dirs.length == 0) { |
274 dirs.add('.'); // Use current working directory as default. | 402 dirs.add('.'); // Use current working directory as default. |
275 } | 403 } |
276 buildFileList(dirs, | 404 var f = buildFileList(dirs, |
277 new RegExp(options['test-file-pattern']), options['recurse'], | 405 new RegExp(config['test-file-pattern']), config['recurse']); |
278 (f) => processTests(config, f)); | 406 if (config['sort']) f.sort(); |
407 processTests(config, f); | |
279 } | 408 } |
280 } | 409 } |
281 } | 410 } |
OLD | NEW |