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

Side by Side Diff: utils/testrunner/testrunner.dart

Issue 14247033: Updated testrunner: (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 years, 8 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
OLDNEW
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698