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

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

Issue 2933973002: Simplify Command classes. (Closed)
Patch Set: Rename class. Created 3 years, 6 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
« no previous file with comments | « no previous file | tools/testing/dart/command_output.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) 2017, 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 import 'dart:async';
6 // We need to use the 'io' prefix here, otherwise io.exitCode will shadow
7 // CommandOutput.exitCode in subclasses of CommandOutput.
8 import 'dart:io' as io;
9
10 import 'command_output.dart';
11 import 'configuration.dart';
12 import 'expectation.dart';
13 import 'path.dart';
14 import 'utils.dart';
15
16 /// A command executed as a step in a test case.
17 class Command {
18 static Command contentShell(
19 String executable,
20 String htmlFile,
21 List<String> options,
22 List<String> dartFlags,
23 Map<String, String> environment) {
24 return new ContentShellCommand._(
25 executable, htmlFile, options, dartFlags, environment);
26 }
27
28 static Command browserTest(String url, Configuration configuration,
29 {bool retry}) {
30 return new BrowserTestCommand._(url, configuration, retry);
31 }
32
33 static Command browserHtmlTest(
34 String url, Configuration configuration, List<String> expectedMessages,
35 {bool retry}) {
36 return new BrowserHtmlTestCommand._(
37 url, configuration, expectedMessages, retry);
38 }
39
40 static Command compilation(
41 String displayName,
42 String outputFile,
43 bool neverSkipCompilation,
44 List<Uri> bootstrapDependencies,
45 String executable,
46 List<String> arguments,
47 Map<String, String> environment) {
48 return new CompilationCommand._(
49 displayName,
50 outputFile,
51 neverSkipCompilation,
52 bootstrapDependencies,
53 executable,
54 arguments,
55 environment);
56 }
57
58 static Command kernelCompilation(
59 String outputFile,
60 bool neverSkipCompilation,
61 List<Uri> bootstrapDependencies,
62 String executable,
63 List<String> arguments,
64 Map<String, String> environment) {
65 return new KernelCompilationCommand._(outputFile, neverSkipCompilation,
66 bootstrapDependencies, executable, arguments, environment);
67 }
68
69 static Command analysis(String executable, List<String> arguments,
70 Map<String, String> environmentOverrides) {
71 return new AnalysisCommand._(executable, arguments, environmentOverrides);
72 }
73
74 static Command vm(String executable, List<String> arguments,
75 Map<String, String> environmentOverrides) {
76 return new VmCommand._(executable, arguments, environmentOverrides);
77 }
78
79 static Command vmBatch(String executable, String tester,
80 List<String> arguments, Map<String, String> environmentOverrides,
81 {bool checked: true}) {
82 return new VmBatchCommand._(
83 executable, tester, arguments, environmentOverrides,
84 checked: checked);
85 }
86
87 static Command adbPrecompiled(String precompiledRunner, String processTest,
88 String testDirectory, List<String> arguments, bool useBlobs) {
89 return new AdbPrecompilationCommand._(
90 precompiledRunner, processTest, testDirectory, arguments, useBlobs);
91 }
92
93 static Command jsCommandLine(
94 String displayName, String executable, List<String> arguments,
95 [Map<String, String> environment]) {
96 return new JSCommandlineCommand._(
97 displayName, executable, arguments, environment);
98 }
99
100 static Command process(
101 String displayName, String executable, List<String> arguments,
102 [Map<String, String> environment, String workingDirectory]) {
103 return new ProcessCommand._(
104 displayName, executable, arguments, environment, workingDirectory);
105 }
106
107 static Command copy(String sourceDirectory, String destinationDirectory) {
108 return new CleanDirectoryCopyCommand._(
109 sourceDirectory, destinationDirectory);
110 }
111
112 static Command pub(String pubCommand, String pubExecutable,
113 String pubspecYamlDirectory, String pubCacheDirectory,
114 {List<String> arguments: const <String>[]}) {
115 return new PubCommand._(pubCommand, pubExecutable, pubspecYamlDirectory,
116 pubCacheDirectory, arguments);
117 }
118
119 static Command makeSymlink(String link, String target) {
120 return new MakeSymlinkCommand._(link, target);
121 }
122
123 /// A descriptive name for this command.
124 final String displayName;
125
126 /// Number of times this command *can* be retried.
127 int get maxNumRetries => 2;
128
129 /// Reproduction command.
130 String get reproductionCommand => null;
131
132 /// We compute the Command.hashCode lazily and cache it here, since it might
133 /// be expensive to compute (and hashCode is called often).
134 int _cachedHashCode;
135
136 Command._(this.displayName);
137
138 int get hashCode {
139 if (_cachedHashCode == null) {
140 var builder = new HashCodeBuilder();
141 _buildHashCode(builder);
142 _cachedHashCode = builder.value;
143 }
144 return _cachedHashCode;
145 }
146
147 operator ==(Object other) =>
148 identical(this, other) ||
149 (runtimeType == other.runtimeType && _equal(other as Command));
150
151 void _buildHashCode(HashCodeBuilder builder) {
152 builder.addJson(displayName);
153 }
154
155 bool _equal(covariant Command other) =>
156 hashCode == other.hashCode && displayName == other.displayName;
157
158 String toString() => reproductionCommand;
159
160 Future<bool> get outputIsUpToDate => new Future.value(false);
161 }
162
163 class ProcessCommand extends Command {
164 /// Path to the executable of this command.
165 String executable;
166
167 /// Command line arguments to the executable.
168 final List<String> arguments;
169
170 /// Environment for the command.
171 final Map<String, String> environmentOverrides;
172
173 /// Working directory for the command.
174 final String workingDirectory;
175
176 ProcessCommand._(String displayName, this.executable, this.arguments,
177 [this.environmentOverrides, this.workingDirectory])
178 : super._(displayName) {
179 if (io.Platform.operatingSystem == 'windows') {
180 // Windows can't handle the first command if it is a .bat file or the like
181 // with the slashes going the other direction.
182 // NOTE: Issue 1306
183 executable = executable.replaceAll('/', '\\');
184 }
185 }
186
187 void _buildHashCode(HashCodeBuilder builder) {
188 super._buildHashCode(builder);
189 builder.addJson(executable);
190 builder.addJson(workingDirectory);
191 builder.addJson(arguments);
192 builder.addJson(environmentOverrides);
193 }
194
195 bool _equal(ProcessCommand other) =>
196 super._equal(other) &&
197 executable == other.executable &&
198 deepJsonCompare(arguments, other.arguments) &&
199 workingDirectory == other.workingDirectory &&
200 deepJsonCompare(environmentOverrides, other.environmentOverrides);
201
202 String get reproductionCommand {
203 var env = new StringBuffer();
204 environmentOverrides?.forEach((key, value) =>
205 (io.Platform.operatingSystem == 'windows')
206 ? env.write('set $key=${escapeCommandLineArgument(value)} & ')
207 : env.write('$key=${escapeCommandLineArgument(value)} '));
208 var command = ([executable]..addAll(batchArguments)..addAll(arguments))
209 .map(escapeCommandLineArgument)
210 .join(' ');
211 if (workingDirectory != null) {
212 command = "$command (working directory: $workingDirectory)";
213 }
214 return "$env$command";
215 }
216
217 Future<bool> get outputIsUpToDate => new Future.value(false);
218
219 /// Arguments that are passed to the process when starting batch mode.
220 ///
221 /// In non-batch mode, they should be passed before [arguments].
222 List<String> get batchArguments => const [];
223 }
224
225 class CompilationCommand extends ProcessCommand {
226 final String _outputFile;
227 final bool _neverSkipCompilation;
228 final List<Uri> _bootstrapDependencies;
229
230 CompilationCommand._(
231 String displayName,
232 this._outputFile,
233 this._neverSkipCompilation,
234 this._bootstrapDependencies,
235 String executable,
236 List<String> arguments,
237 Map<String, String> environmentOverrides)
238 : super._(displayName, executable, arguments, environmentOverrides);
239
240 Future<bool> get outputIsUpToDate {
241 if (_neverSkipCompilation) return new Future.value(false);
242
243 Future<List<Uri>> readDepsFile(String path) {
244 var file = new io.File(new Path(path).toNativePath());
245 if (!file.existsSync()) {
246 return new Future.value(null);
247 }
248 return file.readAsLines().then((List<String> lines) {
249 var dependencies = new List<Uri>();
250 for (var line in lines) {
251 line = line.trim();
252 if (line.length > 0) {
253 dependencies.add(Uri.parse(line));
254 }
255 }
256 return dependencies;
257 });
258 }
259
260 return readDepsFile("$_outputFile.deps").then((dependencies) {
261 if (dependencies != null) {
262 dependencies.addAll(_bootstrapDependencies);
263 var jsOutputLastModified = TestUtils.lastModifiedCache
264 .getLastModified(new Uri(scheme: 'file', path: _outputFile));
265 if (jsOutputLastModified != null) {
266 for (var dependency in dependencies) {
267 var dependencyLastModified =
268 TestUtils.lastModifiedCache.getLastModified(dependency);
269 if (dependencyLastModified == null ||
270 dependencyLastModified.isAfter(jsOutputLastModified)) {
271 return false;
272 }
273 }
274 return true;
275 }
276 }
277 return false;
278 });
279 }
280
281 void _buildHashCode(HashCodeBuilder builder) {
282 super._buildHashCode(builder);
283 builder.addJson(_outputFile);
284 builder.addJson(_neverSkipCompilation);
285 builder.addJson(_bootstrapDependencies);
286 }
287
288 bool _equal(CompilationCommand other) =>
289 super._equal(other) &&
290 _outputFile == other._outputFile &&
291 _neverSkipCompilation == other._neverSkipCompilation &&
292 deepJsonCompare(_bootstrapDependencies, other._bootstrapDependencies);
293 }
294
295 class KernelCompilationCommand extends CompilationCommand {
296 KernelCompilationCommand._(
297 String outputFile,
298 bool neverSkipCompilation,
299 List<Uri> bootstrapDependencies,
300 String executable,
301 List<String> arguments,
302 Map<String, String> environmentOverrides)
303 : super._('dartk', outputFile, neverSkipCompilation,
304 bootstrapDependencies, executable, arguments, environmentOverrides);
305
306 int get maxNumRetries => 1;
307 }
308
309 /// This is just a Pair(String, Map) class with hashCode and operator ==
310 class AddFlagsKey {
311 final String flags;
312 final Map env;
313 AddFlagsKey(this.flags, this.env);
314 // Just use object identity for environment map
315 bool operator ==(Object other) =>
316 other is AddFlagsKey && flags == other.flags && env == other.env;
317 int get hashCode => flags.hashCode ^ env.hashCode;
318 }
319
320 class ContentShellCommand extends ProcessCommand {
321 ContentShellCommand._(
322 String executable,
323 String htmlFile,
324 List<String> options,
325 List<String> dartFlags,
326 Map<String, String> environmentOverrides)
327 : super._("content_shell", executable, _getArguments(options, htmlFile),
328 _getEnvironment(environmentOverrides, dartFlags));
329
330 // Cache the modified environments in a map from the old environment and
331 // the string of Dart flags to the new environment. Avoid creating new
332 // environment object for each command object.
333 static Map<AddFlagsKey, Map<String, String>> environments = {};
334
335 static Map<String, String> _getEnvironment(
336 Map<String, String> env, List<String> dartFlags) {
337 var needDartFlags = dartFlags != null && dartFlags.isNotEmpty;
338 if (needDartFlags) {
339 if (env == null) {
340 env = const <String, String>{};
341 }
342 var flags = dartFlags.join(' ');
343 return environments.putIfAbsent(
344 new AddFlagsKey(flags, env),
345 () => new Map<String, String>.from(env)
346 ..addAll({'DART_FLAGS': flags, 'DART_FORWARDING_PRINT': '1'}));
347 }
348 return env;
349 }
350
351 static List<String> _getArguments(List<String> options, String htmlFile) {
352 var arguments = options.toList();
353 arguments.add(htmlFile);
354 return arguments;
355 }
356
357 int get maxNumRetries => 3;
358 }
359
360 class BrowserTestCommand extends Command {
361 Runtime get browser => configuration.runtime;
362 final String url;
363 final Configuration configuration;
364 final bool retry;
365
366 BrowserTestCommand._(this.url, this.configuration, this.retry)
367 : super._(configuration.runtime.name);
368
369 void _buildHashCode(HashCodeBuilder builder) {
370 super._buildHashCode(builder);
371 builder.addJson(browser.name);
372 builder.addJson(url);
373 builder.add(configuration);
374 builder.add(retry);
375 }
376
377 bool _equal(BrowserTestCommand other) =>
378 super._equal(other) &&
379 browser == other.browser &&
380 url == other.url &&
381 identical(configuration, other.configuration) &&
382 retry == other.retry;
383
384 String get reproductionCommand {
385 var parts = [
386 io.Platform.resolvedExecutable,
387 'tools/testing/dart/launch_browser.dart',
388 browser.name,
389 url
390 ];
391 return parts.map(escapeCommandLineArgument).join(' ');
392 }
393
394 int get maxNumRetries => 4;
395 }
396
397 class BrowserHtmlTestCommand extends BrowserTestCommand {
398 List<String> expectedMessages;
399 BrowserHtmlTestCommand._(String url, Configuration configuration,
400 this.expectedMessages, bool retry)
401 : super._(url, configuration, retry);
402
403 void _buildHashCode(HashCodeBuilder builder) {
404 super._buildHashCode(builder);
405 builder.addJson(expectedMessages);
406 }
407
408 bool _equal(BrowserHtmlTestCommand other) =>
409 super._equal(other) &&
410 identical(expectedMessages, other.expectedMessages);
411 }
412
413 class AnalysisCommand extends ProcessCommand {
414 AnalysisCommand._(String executable, List<String> arguments,
415 Map<String, String> environmentOverrides)
416 : super._('dart2analyzer', executable, arguments, environmentOverrides);
417 }
418
419 class VmCommand extends ProcessCommand {
420 VmCommand._(String executable, List<String> arguments,
421 Map<String, String> environmentOverrides)
422 : super._('vm', executable, arguments, environmentOverrides);
423 }
424
425 class VmBatchCommand extends ProcessCommand implements VmCommand {
426 final String dartFile;
427 final bool checked;
428
429 VmBatchCommand._(String executable, String dartFile, List<String> arguments,
430 Map<String, String> environmentOverrides,
431 {this.checked: true})
432 : this.dartFile = dartFile,
433 super._('vm-batch', executable, arguments, environmentOverrides);
434
435 @override
436 List<String> get batchArguments =>
437 checked ? ['--checked', dartFile] : [dartFile];
438
439 @override
440 bool _equal(VmBatchCommand other) {
441 return super._equal(other) &&
442 dartFile == other.dartFile &&
443 checked == other.checked;
444 }
445
446 @override
447 void _buildHashCode(HashCodeBuilder builder) {
448 super._buildHashCode(builder);
449 builder.addJson(dartFile);
450 builder.addJson(checked);
451 }
452 }
453
454 class AdbPrecompilationCommand extends Command {
455 final String precompiledRunnerFilename;
456 final String processTestFilename;
457 final String precompiledTestDirectory;
458 final List<String> arguments;
459 final bool useBlobs;
460
461 AdbPrecompilationCommand._(
462 this.precompiledRunnerFilename,
463 this.processTestFilename,
464 this.precompiledTestDirectory,
465 this.arguments,
466 this.useBlobs)
467 : super._("adb_precompilation");
468
469 void _buildHashCode(HashCodeBuilder builder) {
470 super._buildHashCode(builder);
471 builder.add(precompiledRunnerFilename);
472 builder.add(precompiledTestDirectory);
473 builder.add(arguments);
474 builder.add(useBlobs);
475 }
476
477 bool _equal(AdbPrecompilationCommand other) =>
478 super._equal(other) &&
479 precompiledRunnerFilename == other.precompiledRunnerFilename &&
480 useBlobs == other.useBlobs &&
481 arguments == other.arguments &&
482 precompiledTestDirectory == other.precompiledTestDirectory;
483
484 String toString() => 'Steps to push precompiled runner and precompiled code '
485 'to an attached device. Uses (and requires) adb.';
486 }
487
488 class JSCommandlineCommand extends ProcessCommand {
489 JSCommandlineCommand._(
490 String displayName, String executable, List<String> arguments,
491 [Map<String, String> environmentOverrides = null])
492 : super._(displayName, executable, arguments, environmentOverrides);
493 }
494
495 class PubCommand extends ProcessCommand {
496 final String command;
497
498 PubCommand._(String pubCommand, String pubExecutable,
499 String pubspecYamlDirectory, String pubCacheDirectory, List<String> args)
500 : command = pubCommand,
501 super._(
502 'pub_$pubCommand',
503 new io.File(pubExecutable).absolute.path,
504 [pubCommand]..addAll(args),
505 {'PUB_CACHE': pubCacheDirectory},
506 pubspecYamlDirectory);
507
508 void _buildHashCode(HashCodeBuilder builder) {
509 super._buildHashCode(builder);
510 builder.addJson(command);
511 }
512
513 bool _equal(PubCommand other) =>
514 super._equal(other) && command == other.command;
515 }
516
517 /// [ScriptCommand]s are executed by dart code.
518 abstract class ScriptCommand extends Command {
519 ScriptCommand._(String displayName) : super._(displayName);
520
521 Future<ScriptCommandOutputImpl> run();
522 }
523
524 class CleanDirectoryCopyCommand extends ScriptCommand {
525 final String _sourceDirectory;
526 final String _destinationDirectory;
527
528 CleanDirectoryCopyCommand._(this._sourceDirectory, this._destinationDirectory)
529 : super._('dir_copy');
530
531 String get reproductionCommand =>
532 "Copying '$_sourceDirectory' to '$_destinationDirectory'.";
533
534 Future<ScriptCommandOutputImpl> run() {
535 var watch = new Stopwatch()..start();
536
537 var destination = new io.Directory(_destinationDirectory);
538
539 return destination.exists().then((bool exists) {
540 Future cleanDirectoryFuture;
541 if (exists) {
542 cleanDirectoryFuture = TestUtils.deleteDirectory(_destinationDirectory);
543 } else {
544 cleanDirectoryFuture = new Future.value(null);
545 }
546 return cleanDirectoryFuture.then((_) {
547 return TestUtils.copyDirectory(_sourceDirectory, _destinationDirectory);
548 });
549 }).then((_) {
550 return new ScriptCommandOutputImpl(
551 this, Expectation.pass, "", watch.elapsed);
552 }).catchError((error) {
553 return new ScriptCommandOutputImpl(
554 this, Expectation.fail, "An error occured: $error.", watch.elapsed);
555 });
556 }
557
558 void _buildHashCode(HashCodeBuilder builder) {
559 super._buildHashCode(builder);
560 builder.addJson(_sourceDirectory);
561 builder.addJson(_destinationDirectory);
562 }
563
564 bool _equal(CleanDirectoryCopyCommand other) =>
565 super._equal(other) &&
566 _sourceDirectory == other._sourceDirectory &&
567 _destinationDirectory == other._destinationDirectory;
568 }
569
570 /// Makes a symbolic link to another directory.
571 class MakeSymlinkCommand extends ScriptCommand {
572 String _link;
573 String _target;
574
575 MakeSymlinkCommand._(this._link, this._target) : super._('make_symlink');
576
577 String get reproductionCommand =>
578 "Make symbolic link '$_link' (target: $_target)'.";
579
580 Future<ScriptCommandOutputImpl> run() {
581 var watch = new Stopwatch()..start();
582 var targetFile = new io.Directory(_target);
583 return targetFile.exists().then((bool targetExists) {
584 if (!targetExists) {
585 throw new Exception("Target '$_target' does not exist");
586 }
587 var link = new io.Link(_link);
588
589 return link.exists().then((bool exists) {
590 if (exists) return link.delete();
591 }).then((_) => link.create(_target));
592 }).then((_) {
593 return new ScriptCommandOutputImpl(
594 this, Expectation.pass, "", watch.elapsed);
595 }).catchError((error) {
596 return new ScriptCommandOutputImpl(
597 this, Expectation.fail, "An error occured: $error.", watch.elapsed);
598 });
599 }
600
601 void _buildHashCode(HashCodeBuilder builder) {
602 super._buildHashCode(builder);
603 builder.addJson(_link);
604 builder.addJson(_target);
605 }
606
607 bool _equal(MakeSymlinkCommand other) =>
608 super._equal(other) && _link == other._link && _target == other._target;
609 }
OLDNEW
« no previous file with comments | « no previous file | tools/testing/dart/command_output.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698