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

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

Issue 645533002: Clean up test_runner Command subclass hash/equality. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 2 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 // 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 /** 5 /**
6 * Classes and methods for executing tests. 6 * Classes and methods for executing tests.
7 * 7 *
8 * This module includes: 8 * This module includes:
9 * - Managing parallel execution of tests, including timeout checks. 9 * - Managing parallel execution of tests, including timeout checks.
10 * - Evaluating the output of each test as pass/fail/crash/timeout. 10 * - Evaluating the output of each test as pass/fail/crash/timeout.
(...skipping 30 matching lines...) Expand all
41 const List<String> EXCLUDED_ENVIRONMENT_VARIABLES = 41 const List<String> EXCLUDED_ENVIRONMENT_VARIABLES =
42 const ['http_proxy', 'https_proxy', 'no_proxy', 42 const ['http_proxy', 'https_proxy', 'no_proxy',
43 'HTTP_PROXY', 'HTTPS_PROXY', 'NO_PROXY']; 43 'HTTP_PROXY', 'HTTPS_PROXY', 'NO_PROXY'];
44 44
45 45
46 /** A command executed as a step in a test case. */ 46 /** A command executed as a step in a test case. */
47 class Command { 47 class Command {
48 /** A descriptive name for this command. */ 48 /** A descriptive name for this command. */
49 String displayName; 49 String displayName;
50 50
51 /** The actual command line that will be executed. */
52 String commandLine;
53
54 /** Number of times this command *can* be retried */ 51 /** Number of times this command *can* be retried */
55 int get maxNumRetries => 2; 52 int get maxNumRetries => 2;
56 53
57 /** Reproduction command */ 54 /** Reproduction command */
58 String get reproductionCommand => null; 55 String get reproductionCommand => null;
59 56
60 // We compute the Command.hashCode lazily and cache it here, since it might 57 // We compute the Command.hashCode lazily and cache it here, since it might
61 // be expensive to compute (and hashCode is called often). 58 // be expensive to compute (and hashCode is called often).
62 int _cachedHashCode; 59 int _cachedHashCode;
63 60
64 Command._(this.displayName); 61 Command._(this.displayName);
65 62
66 int get hashCode { 63 int get hashCode {
67 if (_cachedHashCode == null) { 64 if (_cachedHashCode == null) {
68 var builder = new HashCodeBuilder(); 65 var builder = new HashCodeBuilder();
69 _buildHashCode(builder); 66 _buildHashCode(builder);
70 _cachedHashCode = builder.value; 67 _cachedHashCode = builder.value;
71 } 68 }
72 return _cachedHashCode; 69 return _cachedHashCode;
73 } 70 }
74 71
75 operator ==(other) { 72 operator ==(other) => identical(this, other) ||
76 if (other is Command) { 73 (runtimeType == other.runtimeType && _equal(other));
77 return identical(this, other) || _equal(other as Command); 74
78 } 75 void _buildHashCode(HashCodeBuilder builder) {
79 return false; 76 builder.addJson(displayName);
80 } 77 }
81 78
82 void _buildHashCode(HashCodeBuilder builder) { 79 bool _equal(Command other) =>
83 builder.add(commandLine); 80 hashCode == other.hashCode &&
84 builder.add(displayName); 81 displayName == other.displayName;
85 }
86
87 bool _equal(Command other) {
88 return hashCode == other.hashCode &&
89 commandLine == other.commandLine &&
90 displayName == other.displayName;
91 }
92 82
93 String toString() => reproductionCommand; 83 String toString() => reproductionCommand;
94 84
95 Future<bool> get outputIsUpToDate => new Future.value(false); 85 Future<bool> get outputIsUpToDate => new Future.value(false);
96 } 86 }
97 87
98 class ProcessCommand extends Command { 88 class ProcessCommand extends Command {
99 /** Path to the executable of this command. */ 89 /** Path to the executable of this command. */
100 String executable; 90 String executable;
101 91
(...skipping 14 matching lines...) Expand all
116 if (io.Platform.operatingSystem == 'windows') { 106 if (io.Platform.operatingSystem == 'windows') {
117 // Windows can't handle the first command if it is a .bat file or the like 107 // Windows can't handle the first command if it is a .bat file or the like
118 // with the slashes going the other direction. 108 // with the slashes going the other direction.
119 // NOTE: Issue 1306 109 // NOTE: Issue 1306
120 executable = executable.replaceAll('/', '\\'); 110 executable = executable.replaceAll('/', '\\');
121 } 111 }
122 } 112 }
123 113
124 void _buildHashCode(HashCodeBuilder builder) { 114 void _buildHashCode(HashCodeBuilder builder) {
125 super._buildHashCode(builder); 115 super._buildHashCode(builder);
126 builder.add(executable); 116 builder.addJson(executable);
127 builder.add(workingDirectory); 117 builder.addJson(workingDirectory);
128 for (var object in arguments) builder.add(object); 118 builder.addJson(arguments);
129 if (environmentOverrides != null) { 119 builder.addJson(environmentOverrides);
130 for (var key in environmentOverrides.keys) {
131 builder.add(key);
132 builder.add(environmentOverrides[key]);
133 }
134 }
135 } 120 }
136 121
137 bool _equal(Command other) { 122 bool _equal(ProcessCommand other) =>
138 if (other is ProcessCommand) { 123 super._equal(other) &&
139 if (!super._equal(other)) return false; 124 executable == other.executable &&
140 125 deepJsonCompare(arguments, other.arguments) &&
141 if (hashCode != other.hashCode || 126 workingDirectory == other.workingDirectory &&
142 executable != other.executable || 127 deepJsonCompare(environmentOverrides, other.environmentOverrides);
143 arguments.length != other.arguments.length) {
144 return false;
145 }
146
147 if (!deepJsonCompare(arguments, other.arguments)) return false;
148 if (workingDirectory != other.workingDirectory) return false;
149 if (!deepJsonCompare(environmentOverrides, other.environmentOverrides)) {
150 return false;
151 }
152
153 return true;
154 }
155 return false;
156 }
157 128
158 String get reproductionCommand { 129 String get reproductionCommand {
159 var command = ([executable]..addAll(arguments)) 130 var command = ([executable]..addAll(arguments))
160 .map(escapeCommandLineArgument).join(' '); 131 .map(escapeCommandLineArgument).join(' ');
161 if (workingDirectory != null) { 132 if (workingDirectory != null) {
162 command = "$command (working directory: $workingDirectory)"; 133 command = "$command (working directory: $workingDirectory)";
163 } 134 }
164 return command; 135 return command;
165 } 136 }
166 137
167 Future<bool> get outputIsUpToDate => new Future.value(false); 138 Future<bool> get outputIsUpToDate => new Future.value(false);
168 } 139 }
169 140
170 class CompilationCommand extends ProcessCommand { 141 class CompilationCommand extends ProcessCommand {
171 String _outputFile; 142 final String _outputFile;
172 bool _neverSkipCompilation; 143 final bool _neverSkipCompilation;
173 List<Uri> _bootstrapDependencies; 144 final List<Uri> _bootstrapDependencies;
174 145
175 CompilationCommand._(String displayName, 146 CompilationCommand._(String displayName,
176 this._outputFile, 147 this._outputFile,
177 this._neverSkipCompilation, 148 this._neverSkipCompilation,
178 List<Uri> bootstrapDependencies, 149 this._bootstrapDependencies,
179 String executable, 150 String executable,
180 List<String> arguments, 151 List<String> arguments,
181 Map<String, String> environmentOverrides) 152 Map<String, String> environmentOverrides)
182 : super._(displayName, executable, arguments, environmentOverrides) { 153 : super._(displayName, executable, arguments, environmentOverrides);
183 // We sort here, so we can do a fast hashCode/operator==
184 _bootstrapDependencies = new List.from(bootstrapDependencies);
185 _bootstrapDependencies.sort();
Bill Hesse 2014/10/09 12:03:50 All actual uses have only one entry, and if we mak
186 }
187 154
188 Future<bool> get outputIsUpToDate { 155 Future<bool> get outputIsUpToDate {
189 if (_neverSkipCompilation) return new Future.value(false); 156 if (_neverSkipCompilation) return new Future.value(false);
190 157
191 Future<List<Uri>> readDepsFile(String path) { 158 Future<List<Uri>> readDepsFile(String path) {
192 var file = new io.File(new Path(path).toNativePath()); 159 var file = new io.File(new Path(path).toNativePath());
193 if (!file.existsSync()) { 160 if (!file.existsSync()) {
194 return new Future.value(null); 161 return new Future.value(null);
195 } 162 }
196 return file.readAsLines().then((List<String> lines) { 163 return file.readAsLines().then((List<String> lines) {
(...skipping 24 matching lines...) Expand all
221 } 188 }
222 return true; 189 return true;
223 } 190 }
224 } 191 }
225 return false; 192 return false;
226 }); 193 });
227 } 194 }
228 195
229 void _buildHashCode(HashCodeBuilder builder) { 196 void _buildHashCode(HashCodeBuilder builder) {
230 super._buildHashCode(builder); 197 super._buildHashCode(builder);
231 builder.add(_outputFile); 198 builder.addJson(_outputFile);
232 builder.add(_neverSkipCompilation); 199 builder.addJson(_neverSkipCompilation);
233 for (var uri in _bootstrapDependencies) builder.add(uri); 200 builder.addJson(_bootstrapDependencies);
234 } 201 }
235 202
236 bool _equal(Command other) { 203 bool _equal(CompilationCommand other) =>
237 if (other is CompilationCommand && 204 super._equal(other) &&
238 super._equal(other) && 205 _outputFile == other._outputFile &&
239 _outputFile == other._outputFile && 206 _neverSkipCompilation == other._neverSkipCompilation &&
240 _neverSkipCompilation == other._neverSkipCompilation && 207 deepJsonCompare(_bootstrapDependencies, other._bootstrapDependencies);
241 _bootstrapDependencies.length == other._bootstrapDependencies.length) {
242 for (var i = 0; i < _bootstrapDependencies.length; i++) {
243 if (_bootstrapDependencies[i] != other._bootstrapDependencies[i]) {
244 return false;
245 }
246 }
247 return true;
248 }
249 return false;
250 }
251 } 208 }
252 209
253 class ContentShellCommand extends ProcessCommand { 210 class ContentShellCommand extends ProcessCommand {
254 ContentShellCommand._(String executable, 211 ContentShellCommand._(String executable,
255 String htmlFile, 212 String htmlFile,
256 List<String> options, 213 List<String> options,
257 List<String> dartFlags, 214 List<String> dartFlags,
258 Map<String, String> environmentOverrides) 215 Map<String, String> environmentOverrides)
259 : super._("content_shell", 216 : super._("content_shell",
260 executable, 217 executable,
261 _getArguments(options, htmlFile), 218 _getArguments(options, htmlFile),
262 _getEnvironment(environmentOverrides, dartFlags)); 219 _getEnvironment(environmentOverrides, dartFlags));
263 220
221 // Cache the modified environments in a map from the old environment and
222 // the string of Dart flags to the new environment. Avoid creating new
223 // environment object for each command object.
224 static Map<Map, Map<String, Map>> _modifiedEnvironments =
ricow1 2014/10/09 12:16:48 you may want to do a utility class for this, my mi
Bill Hesse 2014/10/10 11:59:15 Done. Use a key pair class, to uncurry the map to
225 new Map<Map, Map<String, Map>>();
226
264 static Map _getEnvironment(Map<String, String> env, List<String> dartFlags) { 227 static Map _getEnvironment(Map<String, String> env, List<String> dartFlags) {
265 var needDartFlags = dartFlags != null && dartFlags.length > 0; 228 var needDartFlags = dartFlags != null && dartFlags.length > 0;
266
267 if (needDartFlags) { 229 if (needDartFlags) {
268 if (env != null) { 230 if (env == null) {
269 env = new Map<String, String>.from(env); 231 env = const { };
270 } else {
271 env = new Map<String, String>();
272 } 232 }
273 env['DART_FLAGS'] = dartFlags.join(" "); 233 var flags = dartFlags.join(' ');
274 env['DART_FORWARDING_PRINT'] = '1'; 234 var envFromString =
235 _modifiedEnvironments.putIfAbsent(env, () => new Map<String, Map>());
236 return envFromString.putIfAbsent(flags, () =>
237 new Map<String, String>.from(env)..addAll({
238 'DART_FLAGS': flags,
239 'DART_FORWARDING_PRINT': '1'
240 }));
275 } 241 }
276
277 return env; 242 return env;
278 } 243 }
279 244
280 static List<String> _getArguments(List<String> options, String htmlFile) { 245 static List<String> _getArguments(List<String> options, String htmlFile) {
281 var arguments = new List.from(options); 246 var arguments = new List.from(options);
282 arguments.add(htmlFile); 247 arguments.add(htmlFile);
283 return arguments; 248 return arguments;
284 } 249 }
285 250
286 bool _equal(Command other) { 251 bool _equal(Command other) => super._equal(other); // We could omit this.
ricow1 2014/10/09 12:16:48 so why don't we
Bill Hesse 2014/10/10 11:59:15 Done.
287 return other is ContentShellCommand && super._equal(other);
288 }
289 252
290 int get maxNumRetries => 3; 253 int get maxNumRetries => 3;
291 } 254 }
292 255
293 class BrowserTestCommand extends Command { 256 class BrowserTestCommand extends Command {
294 final String browser; 257 final String browser;
295 final String url; 258 final String url;
296 final Map configuration; 259 final Map configuration;
297 260
298 BrowserTestCommand._(String _browser, 261 BrowserTestCommand._(String _browser,
299 this.url, 262 this.url,
300 this.configuration) 263 this.configuration)
301 : super._(_browser), browser = _browser; 264 : super._(_browser), browser = _browser;
302 265
303 void _buildHashCode(HashCodeBuilder builder) { 266 void _buildHashCode(HashCodeBuilder builder) {
304 super._buildHashCode(builder); 267 super._buildHashCode(builder);
305 builder.add(browser); 268 builder.add(browser);
306 builder.add(url); 269 builder.add(url);
307 builder.add(configuration); 270 builder.add(configuration);
308 } 271 }
309 272
310 bool _equal(Command other) { 273 bool _equal(BrowserTestCommand other) =>
311 return 274 super._equal(other) &&
312 other is BrowserTestCommand && 275 browser == other.browser &&
313 super._equal(other) && 276 url == other.url &&
314 browser == other.browser && 277 identical(configuration, other.configuration);
315 url == other.url &&
316 identical(configuration, other.configuration);
317 }
318 278
319 String get reproductionCommand { 279 String get reproductionCommand {
320 var parts = [TestUtils.dartTestExecutable.toString(), 280 var parts = [TestUtils.dartTestExecutable.toString(),
321 'tools/testing/dart/launch_browser.dart', 281 'tools/testing/dart/launch_browser.dart',
322 browser, 282 browser,
323 url]; 283 url];
324 return parts.map(escapeCommandLineArgument).join(' '); 284 return parts.map(escapeCommandLineArgument).join(' ');
325 } 285 }
326 } 286 }
327 287
328 class AnalysisCommand extends ProcessCommand { 288 class AnalysisCommand extends ProcessCommand {
329 final String flavor; 289 final String flavor;
330 290
331 AnalysisCommand._(this.flavor, 291 AnalysisCommand._(this.flavor,
332 String displayName, 292 String displayName,
333 String executable, 293 String executable,
334 List<String> arguments, 294 List<String> arguments,
335 Map<String, String> environmentOverrides) 295 Map<String, String> environmentOverrides)
336 : super._(displayName, executable, arguments, environmentOverrides); 296 : super._(displayName, executable, arguments, environmentOverrides);
337 297
338 void _buildHashCode(HashCodeBuilder builder) { 298 void _buildHashCode(HashCodeBuilder builder) {
339 super._buildHashCode(builder); 299 super._buildHashCode(builder);
340 builder.add(flavor); 300 builder.add(flavor);
341 } 301 }
342 302
343 bool _equal(Command other) { 303 bool _equal(AnalysisCommand other) =>
344 return 304 super._equal(other) &&
345 other is AnalysisCommand && 305 flavor == other.flavor;
346 super._equal(other) &&
347 flavor == other.flavor;
348 }
349 } 306 }
350 307
351 class VmCommand extends ProcessCommand { 308 class VmCommand extends ProcessCommand {
352 VmCommand._(String executable, 309 VmCommand._(String executable,
353 List<String> arguments, 310 List<String> arguments,
354 Map<String,String> environmentOverrides) 311 Map<String,String> environmentOverrides)
355 : super._("vm", executable, arguments, environmentOverrides); 312 : super._("vm", executable, arguments, environmentOverrides);
356 } 313 }
357 314
358 class JSCommandlineCommand extends ProcessCommand { 315 class JSCommandlineCommand extends ProcessCommand {
(...skipping 18 matching lines...) Expand all
377 new io.File(pubExecutable).absolute.path, 334 new io.File(pubExecutable).absolute.path,
378 [pubCommand], 335 [pubCommand],
379 {'PUB_CACHE' : pubCacheDirectory}, 336 {'PUB_CACHE' : pubCacheDirectory},
380 pubspecYamlDirectory), command = pubCommand; 337 pubspecYamlDirectory), command = pubCommand;
381 338
382 void _buildHashCode(HashCodeBuilder builder) { 339 void _buildHashCode(HashCodeBuilder builder) {
383 super._buildHashCode(builder); 340 super._buildHashCode(builder);
384 builder.add(command); 341 builder.add(command);
385 } 342 }
386 343
387 bool _equal(Command other) { 344 bool _equal(PubCommand other) =>
388 return 345 super._equal(other) &&
389 other is PubCommand && 346 command == other.command;
390 super._equal(other) &&
391 command == other.command;
392 }
393 } 347 }
394 348
395 /* [ScriptCommand]s are executed by dart code. */ 349 /* [ScriptCommand]s are executed by dart code. */
396 abstract class ScriptCommand extends Command { 350 abstract class ScriptCommand extends Command {
397 ScriptCommand._(String displayName) : super._(displayName); 351 ScriptCommand._(String displayName) : super._(displayName);
398 352
399 Future<ScriptCommandOutputImpl> run(); 353 Future<ScriptCommandOutputImpl> run();
400 } 354 }
401 355
402 class CleanDirectoryCopyCommand extends ScriptCommand { 356 class CleanDirectoryCopyCommand extends ScriptCommand {
(...skipping 30 matching lines...) Expand all
433 this, Expectation.FAIL, "An error occured: $error.", watch.elapsed); 387 this, Expectation.FAIL, "An error occured: $error.", watch.elapsed);
434 }); 388 });
435 } 389 }
436 390
437 void _buildHashCode(HashCodeBuilder builder) { 391 void _buildHashCode(HashCodeBuilder builder) {
438 super._buildHashCode(builder); 392 super._buildHashCode(builder);
439 builder.add(_sourceDirectory); 393 builder.add(_sourceDirectory);
440 builder.add(_destinationDirectory); 394 builder.add(_destinationDirectory);
441 } 395 }
442 396
443 bool _equal(Command other) { 397 bool _equal(CleanDirectoryCopyCommand other) =>
444 return 398 super._equal(other) &&
445 other is CleanDirectoryCopyCommand && 399 _sourceDirectory == other._sourceDirectory &&
446 super._equal(other) && 400 _destinationDirectory == other._destinationDirectory;
447 _sourceDirectory == other._sourceDirectory &&
448 _destinationDirectory == other._destinationDirectory;
449 }
450 } 401 }
451 402
452 class ModifyPubspecYamlCommand extends ScriptCommand { 403 class ModifyPubspecYamlCommand extends ScriptCommand {
453 String _pubspecYamlFile; 404 String _pubspecYamlFile;
454 String _destinationFile; 405 String _destinationFile;
455 Map<String, Map> _dependencyOverrides; 406 Map<String, Map> _dependencyOverrides;
456 407
457 ModifyPubspecYamlCommand._(this._pubspecYamlFile, 408 ModifyPubspecYamlCommand._(this._pubspecYamlFile,
458 this._destinationFile, 409 this._destinationFile,
459 this._dependencyOverrides) 410 this._dependencyOverrides)
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
503 return new ScriptCommandOutputImpl( 454 return new ScriptCommandOutputImpl(
504 this, Expectation.PASS, "", watch.elapsed); 455 this, Expectation.PASS, "", watch.elapsed);
505 }).catchError((error) { 456 }).catchError((error) {
506 return new ScriptCommandOutputImpl( 457 return new ScriptCommandOutputImpl(
507 this, Expectation.FAIL, "An error occured: $error.", watch.elapsed); 458 this, Expectation.FAIL, "An error occured: $error.", watch.elapsed);
508 }); 459 });
509 } 460 }
510 461
511 void _buildHashCode(HashCodeBuilder builder) { 462 void _buildHashCode(HashCodeBuilder builder) {
512 super._buildHashCode(builder); 463 super._buildHashCode(builder);
513 builder.add(_pubspecYamlFile); 464 builder.addJson(_pubspecYamlFile);
514 builder.add(_destinationFile); 465 builder.addJson(_destinationFile);
515 builder.addJson(_dependencyOverrides); 466 builder.addJson(_dependencyOverrides);
516 } 467 }
517 468
518 bool _equal(Command other) { 469 bool _equal(ModifyPubspecYamlCommand other) =>
519 return 470 super._equal(other) &&
520 other is ModifyPubspecYamlCommand && 471 _pubspecYamlFile == other._pubspecYamlFile &&
521 super._equal(other) && 472 _destinationFile == other._destinationFile &&
522 _pubspecYamlFile == other._pubspecYamlFile && 473 deepJsonCompare(_dependencyOverrides, other._dependencyOverrides);
523 _destinationFile == other._destinationFile &&
524 deepJsonCompare(_dependencyOverrides, other._dependencyOverrides);
525 }
526 } 474 }
527 475
528 /* 476 /*
529 * [MakeSymlinkCommand] makes a symbolic link to another directory. 477 * [MakeSymlinkCommand] makes a symbolic link to another directory.
530 */ 478 */
531 class MakeSymlinkCommand extends ScriptCommand { 479 class MakeSymlinkCommand extends ScriptCommand {
532 String _link; 480 String _link;
533 String _target; 481 String _target;
534 482
535 MakeSymlinkCommand._(this._link, this._target) : super._('make_symlink'); 483 MakeSymlinkCommand._(this._link, this._target) : super._('make_symlink');
(...skipping 21 matching lines...) Expand all
557 this, Expectation.FAIL, "An error occured: $error.", watch.elapsed); 505 this, Expectation.FAIL, "An error occured: $error.", watch.elapsed);
558 }); 506 });
559 } 507 }
560 508
561 void _buildHashCode(HashCodeBuilder builder) { 509 void _buildHashCode(HashCodeBuilder builder) {
562 super._buildHashCode(builder); 510 super._buildHashCode(builder);
563 builder.add(_link); 511 builder.add(_link);
564 builder.add(_target); 512 builder.add(_target);
565 } 513 }
566 514
567 bool _equal(Command other) { 515 bool _equal(MakeSymlinkCommand other) =>
568 return 516 super._equal(other) &&
569 other is MakeSymlinkCommand && 517 _link == other._link &&
570 super._equal(other) && 518 _target == other._target;
571 _link == other._link &&
572 _target == other._target;
573 }
574 } 519 }
575 520
576 class CommandBuilder { 521 class CommandBuilder {
577 static final CommandBuilder instance = new CommandBuilder._(); 522 static final CommandBuilder instance = new CommandBuilder._();
578 523
579 bool _cleared = false; 524 bool _cleared = false;
580 final _cachedCommands = new Map<Command, Command>(); 525 final _cachedCommands = new Map<Command, Command>();
581 526
582 CommandBuilder._(); 527 CommandBuilder._();
583 528
(...skipping 2380 matching lines...) Expand 10 before | Expand all | Expand 10 after
2964 } 2909 }
2965 } 2910 }
2966 2911
2967 void eventAllTestsDone() { 2912 void eventAllTestsDone() {
2968 for (var listener in _eventListener) { 2913 for (var listener in _eventListener) {
2969 listener.allDone(); 2914 listener.allDone();
2970 } 2915 }
2971 _allDone(); 2916 _allDone();
2972 } 2917 }
2973 } 2918 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698