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

Side by Side Diff: utils/tests/pub/test_pub.dart

Issue 10659019: Reverting 9090 (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 5 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
« no previous file with comments | « utils/tests/pub/pub_test.dart ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 * Test infrastructure for testing pub. Unlike typical unit tests, most pub 6 * Test infrastructure for testing pub. Unlike typical unit tests, most pub
7 * tests are integration tests that stage some stuff on the file system, run 7 * tests are integration tests that stage some stuff on the file system, run
8 * pub, and then validate the results. This library provides an API to build 8 * pub, and then validate the results. This library provides an API to build
9 * tests like that. 9 * tests like that.
10 */ 10 */
11 #library('test_pub'); 11 #library('test_pub');
12 12
13 #import('dart:io'); 13 #import('dart:io');
14 #import('dart:uri');
15 14
16 #import('../../../lib/unittest/unittest.dart'); 15 #import('../../../lib/unittest/unittest.dart');
17 #import('../../lib/file_system.dart', prefix: 'fs'); 16 #import('../../lib/file_system.dart', prefix: 'fs');
18 #import('../../pub/io.dart'); 17 #import('../../pub/io.dart');
19 #import('../../pub/yaml/yaml.dart');
20 18
21 /** 19 /**
22 * Creates a new [FileDescriptor] with [name] and [contents]. 20 * Creates a new [FileDescriptor] with [name] and [contents].
23 */ 21 */
24 FileDescriptor file(String name, String contents) => 22 FileDescriptor file(String name, String contents) =>
25 new FileDescriptor(name, contents); 23 new FileDescriptor(name, contents);
26 24
27 /** 25 /**
28 * Creates a new [DirectoryDescriptor] with [name] and [contents]. 26 * Creates a new [DirectoryDescriptor] with [name] and [contents].
29 */ 27 */
30 DirectoryDescriptor dir(String name, [List<Descriptor> contents]) => 28 DirectoryDescriptor dir(String name, [List<Descriptor> contents]) =>
31 new DirectoryDescriptor(name, contents); 29 new DirectoryDescriptor(name, contents);
32 30
33 /** 31 /**
34 * Creates a new [GitRepoDescriptor] with [name] and [contents]. 32 * Creates a new [GitRepoDescriptor] with [name] and [contents].
35 */ 33 */
36 DirectoryDescriptor git(String name, [List<Descriptor> contents]) => 34 DirectoryDescriptor git(String name, [List<Descriptor> contents]) =>
37 new GitRepoDescriptor(name, contents); 35 new GitRepoDescriptor(name, contents);
38 36
39 /** 37 /**
40 * Creates a new [TarFileDescriptor] with [name] and [contents].
41 */
42 TarFileDescriptor tar(String name, [List<Descriptor> contents]) =>
43 new TarFileDescriptor(name, contents);
44
45 /**
46 * Creates an HTTP server to serve [contents] as static files. This server will
47 * exist only for the duration of the pub run.
48 */
49 void serve(String host, int port, [List<Descriptor> contents]) {
50 var baseDir = dir("serve-dir", contents);
51 if (host == 'localhost') {
52 host = '127.0.0.1';
53 }
54
55 _scheduleBeforePub((_) {
56 var server = new HttpServer();
57 server.defaultRequestHandler = (request, response) {
58 var path = request.uri.replaceFirst("/", "").split("/");
59 var stream = baseDir.load(path);
60 response.persistentConnection = false;
61 if (stream == null) {
62 response.statusCode = 404;
63 response.outputStream.close();
64 return;
65 }
66
67 var future = consumeInputStream(stream);
68 future.then((data) {
69 response.statusCode = 200;
70 response.contentLength = data.length;
71 response.outputStream.write(data);
72 response.outputStream.close();
73 });
74
75 future.handleException((e) {
76 print("Exception while handling ${request.uri}: $e");
77 response.statusCode = 500;
78 response.reasonPhrase = e.message;
79 response.outputStream.close();
80 });
81 };
82 server.listen(host, port);
83 _scheduleCleanup((_) => server.close());
84
85 return new Future.immediate(null);
86 });
87 }
88
89 /**
90 * Creates an HTTP server that replicates the structure of pub.dartlang.org.
91 * [pubspecs] is a list of YAML-format pubspecs representing the packages to
92 * serve.
93 */
94 void servePackages(String host, int port, List<String> pubspecs) {
95 var packages = <Map<String, String>>{};
96 pubspecs.forEach((spec) {
97 var parsed = loadYaml(spec);
98 var name = parsed['name'];
99 var version = parsed['version'];
100 packages.putIfAbsent(name, () => <String>{})[version] = spec;
101 });
102
103 serve(host, port, [
104 dir('packages', packages.getKeys().map((name) {
105 return dir(name, [
106 dir('versions', packages[name].getKeys().map((version) {
107 return tar('$version.tar.gz', [
108 file('pubspec.yaml', packages[name][version]),
109 file('$name.dart', 'main() => print("$name $version");')
110 ]);
111 }))
112 ]);
113 }))
114 ]);
115 }
116
117 /**
118 * The path of the package cache directory used for tests. Relative to the 38 * The path of the package cache directory used for tests. Relative to the
119 * sandbox directory. 39 * sandbox directory.
120 */ 40 */
121 final String cachePath = "cache"; 41 final String cachePath = "cache";
122 42
123 /** 43 /**
124 * The path of the mock SDK directory used for tests. Relative to the sandbox 44 * The path of the mock SDK directory used for tests. Relative to the sandbox
125 * directory. 45 * directory.
126 */ 46 */
127 final String sdkPath = "sdk"; 47 final String sdkPath = "sdk";
(...skipping 20 matching lines...) Expand all
148 * The list of events that are scheduled to run after the sandbox directory has 68 * The list of events that are scheduled to run after the sandbox directory has
149 * been created but before Pub is run. 69 * been created but before Pub is run.
150 */ 70 */
151 List<_ScheduledEvent> _scheduledBeforePub; 71 List<_ScheduledEvent> _scheduledBeforePub;
152 72
153 /** 73 /**
154 * The list of events that are scheduled to run after Pub has been run. 74 * The list of events that are scheduled to run after Pub has been run.
155 */ 75 */
156 List<_ScheduledEvent> _scheduledAfterPub; 76 List<_ScheduledEvent> _scheduledAfterPub;
157 77
158 /**
159 * The list of events that are scheduled to run after Pub has been run, even if
160 * it failed.
161 */
162 List<_ScheduledEvent> _scheduledCleanup;
163
164 void runPub([List<String> args, Pattern output, Pattern error, 78 void runPub([List<String> args, Pattern output, Pattern error,
165 int exitCode = 0]) { 79 int exitCode = 0]) {
166 var createdSandboxDir; 80 var createdSandboxDir;
167 81
168 var asyncDone = expectAsync0(() {}); 82 var asyncDone = expectAsync0(() {});
169 83
170 Future cleanup() { 84 deleteSandboxIfCreated(onDeleted()) {
171 return _runScheduled(createdSandboxDir, _scheduledCleanup).chain((_) { 85 _scheduledBeforePub = null;
172 _scheduledBeforePub = null; 86 _scheduledAfterPub = null;
173 _scheduledAfterPub = null; 87 if (createdSandboxDir != null) {
174 if (createdSandboxDir != null) return deleteDir(createdSandboxDir); 88 deleteDir(createdSandboxDir).then((_) => onDeleted());
175 return new Future.immediate(null); 89 } else {
176 }); 90 onDeleted();
91 }
177 } 92 }
178 93
179 String pathInSandbox(path) => join(getFullPath(createdSandboxDir), path); 94 String pathInSandbox(path) => join(getFullPath(createdSandboxDir), path);
180 95
181 final future = _setUpSandbox().chain((sandboxDir) { 96 final future = _setUpSandbox().chain((sandboxDir) {
182 createdSandboxDir = sandboxDir; 97 createdSandboxDir = sandboxDir;
183 return _runScheduled(sandboxDir, _scheduledBeforePub); 98 return _runScheduled(sandboxDir, _scheduledBeforePub);
184 }).chain((_) { 99 }).chain((_) {
185 return ensureDir(pathInSandbox(appPath)); 100 return ensureDir(pathInSandbox(appPath));
186 }).chain((_) { 101 }).chain((_) {
187 // TODO(rnystrom): Hack in the cache directory path. Should pass this 102 // TODO(rnystrom): Hack in the cache directory path. Should pass this
188 // in using environment var once #752 is done. 103 // in using environment var once #752 is done.
189 args.add('--cachedir=${pathInSandbox(cachePath)}'); 104 args.add('--cachedir=${pathInSandbox(cachePath)}');
190 105
191 // TODO(rnystrom): Hack in the SDK path. Should pass this in using 106 // TODO(rnystrom): Hack in the SDK path. Should pass this in using
192 // environment var once #752 is done. 107 // environment var once #752 is done.
193 args.add('--sdkdir=${pathInSandbox(sdkPath)}'); 108 args.add('--sdkdir=${pathInSandbox(sdkPath)}');
194 109
195 return _runPub(args, pathInSandbox(appPath), pipeStdout: output == null, 110 return _runPub(args, pathInSandbox(appPath));
196 pipeStderr: error == null);
197 }).chain((result) { 111 }).chain((result) {
198 _validateOutput(output, result.stdout); 112 _validateOutput(output, result.stdout);
199 _validateOutput(error, result.stderr); 113 _validateOutput(error, result.stderr);
200 114
201 Expect.equals(result.exitCode, exitCode, 115 Expect.equals(result.exitCode, exitCode,
202 'Pub returned exit code ${result.exitCode}, expected $exitCode.'); 116 'Pub returned exit code ${result.exitCode}, expected $exitCode.');
203 117
204 return _runScheduled(createdSandboxDir, _scheduledAfterPub); 118 return _runScheduled(createdSandboxDir, _scheduledAfterPub);
205 }); 119 });
206 120
207 future.chain((_) => cleanup()).then((_) => asyncDone()); 121 future.then((_) {
122 deleteSandboxIfCreated(asyncDone);
123 });
208 124
209 future.handleException((error) { 125 future.handleException((error) {
210 // If an error occurs during testing, delete the sandbox, throw the error so 126 // If an error occurs during testing, delete the sandbox, throw the error so
211 // that the test framework sees it, then finally call asyncDone so that the 127 // that the test framework sees it, then finally call asyncDone so that the
212 // test framework knows we're done doing asynchronous stuff. 128 // test framework knows we're done doing asynchronous stuff.
213 cleanup().then((_) { 129 deleteSandboxIfCreated(() {
214 guardAsync(() { throw error; }, asyncDone); 130 guardAsync(() { throw error; }, asyncDone);
215 }); 131 });
216 return true; 132 return true;
217 }); 133 });
218 } 134 }
219 135
220 136
221 /** 137 /**
222 * Wraps a test that needs git in order to run. This validates that the test is 138 * Wraps a test that needs git in order to run. This validates that the test is
223 * running on a builbot in which case we expect git to be installed. If we are 139 * running on a builbot in which case we expect git to be installed. If we are
224 * not running on the buildbot, we will instead see if git is installed and 140 * not running on the buildbot, we will instead see if git is installed and
225 * skip the test if not. This way, users don't need to have git installed to 141 * skip the test if not. This way, users don't need to have git installed to
226 * run the tests locally (unless they actually care about the pub git tests). 142 * run the tests locally (unless they actually care about the pub git tests).
227 */ 143 */
228 void withGit(void callback()) { 144 void withGit(void callback()) {
229 isGitInstalled.then(expectAsync1((installed) { 145 isGitInstalled.then(expectAsync1((installed) {
230 if (installed || Platform.environment.containsKey('BUILDBOT_BUILDERNAME')) { 146 if (installed || Platform.environment.containsKey('BUILDBOT_BUILDERNAME')) {
231 callback(); 147 callback();
232 } 148 }
233 })); 149 }));
234 } 150 }
235 151
236 Future<Directory> _setUpSandbox() { 152 Future<Directory> _setUpSandbox() {
237 return createTempDir('pub-test-sandbox-'); 153 return createTempDir('pub-test-sandbox-');
238 } 154 }
239 155
240 _runScheduled(Directory parentDir, List<_ScheduledEvent> scheduled) { 156 _runScheduled(Directory parentDir, List<_ScheduledEvent> scheduled) {
241 if (scheduled == null) return new Future.immediate(null); 157 if (scheduled == null) return new Future.immediate(null);
242 var future = Futures.wait(scheduled.map((event) { 158 var future = Futures.wait(scheduled.map((event) => event(parentDir)));
243 var subFuture = event(parentDir);
244 return subFuture == null ? new Future.immediate(null) : subFuture;
245 }));
246 scheduled.clear(); 159 scheduled.clear();
247 return future; 160 return future;
248 } 161 }
249 162
250 Future<ProcessResult> _runPub(List<String> pubArgs, String workingDir, 163 Future<ProcessResult> _runPub(List<String> pubArgs, String workingDir) {
251 [bool pipeStdout=false, bool pipeStderr=false]) {
252 // Find a dart executable we can use to run pub. Uses the one that the 164 // Find a dart executable we can use to run pub. Uses the one that the
253 // test infrastructure uses. We are not using new Options.executable here 165 // test infrastructure uses. We are not using new Options.executable here
254 // because that gets confused if you invoked Dart through a shell script. 166 // because that gets confused if you invoked Dart through a shell script.
255 final scriptDir = new File(new Options().script).directorySync().path; 167 final scriptDir = new File(new Options().script).directorySync().path;
256 final platform = Platform.operatingSystem; 168 final platform = Platform.operatingSystem;
257 final dartBin = join(scriptDir, '../../../tools/testing/bin/$platform/dart'); 169 final dartBin = join(scriptDir, '../../../tools/testing/bin/$platform/dart');
258 170
259 // Find the main pub entrypoint. 171 // Find the main pub entrypoint.
260 final pubPath = fs.joinPaths(scriptDir, '../../pub/pub.dart'); 172 final pubPath = fs.joinPaths(scriptDir, '../../pub/pub.dart');
261 173
262 final args = ['--enable-type-checks', '--enable-asserts', pubPath]; 174 final args = ['--enable-type-checks', '--enable-asserts', pubPath];
263 args.addAll(pubArgs); 175 args.addAll(pubArgs);
264 176
265 return runProcess(dartBin, args, workingDir, pipeStdout, pipeStderr); 177 return runProcess(dartBin, args, workingDir);
266 } 178 }
267 179
268 /** 180 /**
269 * Compares the [actual] output from running pub with [expected]. For [String] 181 * Compares the [actual] output from running pub with [expected]. For [String]
270 * patterns, ignores leading and trailing whitespace differences and tries to 182 * patterns, ignores leading and trailing whitespace differences and tries to
271 * report the offending difference in a nice way. For other [Pattern]s, just 183 * report the offending difference in a nice way. For other [Pattern]s, just
272 * reports whether the output contained the pattern. 184 * reports whether the output contained the pattern.
273 */ 185 */
274 void _validateOutput(Pattern expected, List<String> actual) { 186 void _validateOutput(Pattern expected, List<String> actual) {
275 if (expected == null) return; 187 if (expected == null) return;
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
339 abstract Future create(dir); 251 abstract Future create(dir);
340 252
341 /** 253 /**
342 * Validates that this descriptor correctly matches the corresponding file 254 * Validates that this descriptor correctly matches the corresponding file
343 * system entry within [dir]. Returns a [Future] that completes to `null` if 255 * system entry within [dir]. Returns a [Future] that completes to `null` if
344 * the entry is valid, or throws an error if it failed. 256 * the entry is valid, or throws an error if it failed.
345 */ 257 */
346 abstract Future validate(String dir); 258 abstract Future validate(String dir);
347 259
348 /** 260 /**
349 * Loads the file at [path] from within this descriptor. If [path] is empty,
350 * loads the contents of the descriptor itself.
351 */
352 abstract InputStream load(List<String> path);
353
354 /**
355 * Schedules the directory to be created before Pub is run with [runPub]. The 261 * Schedules the directory to be created before Pub is run with [runPub]. The
356 * directory will be created relative to the sandbox directory. 262 * directory will be created relative to the sandbox directory.
357 */ 263 */
358 // TODO(nweiz): Use implicit closurization once issue 2984 is fixed. 264 // TODO(nweiz): Use implicit closurization once issue 2984 is fixed.
359 void scheduleCreate() => _scheduleBeforePub((dir) => this.create(dir)); 265 void scheduleCreate() => _scheduleBeforePub((dir) => this.create(dir));
360 266
361 /** 267 /**
362 * Schedules the directory to be validated after Pub is run with [runPub]. The 268 * Schedules the directory to be validated after Pub is run with [runPub]. The
363 * directory will be validated relative to the sandbox directory. 269 * directory will be validated relative to the sandbox directory.
364 */ 270 */
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
396 if (!exists) Expect.fail('Expected file $path does not exist.'); 302 if (!exists) Expect.fail('Expected file $path does not exist.');
397 303
398 return readTextFile(path).transform((text) { 304 return readTextFile(path).transform((text) {
399 if (text == contents) return null; 305 if (text == contents) return null;
400 306
401 Expect.fail('File $path should contain:\n\n$contents\n\n' 307 Expect.fail('File $path should contain:\n\n$contents\n\n'
402 'but contained:\n\n$text'); 308 'but contained:\n\n$text');
403 }); 309 });
404 }); 310 });
405 } 311 }
406
407 /**
408 * Loads the contents of the file.
409 */
410 InputStream load(List<String> path) {
411 if (!path.isEmpty()) {
412 var joinedPath = Strings.join('/', path);
413 throw "Can't load $joinedPath from within $name: not a directory.";
414 }
415
416 var stream = new ListInputStream();
417 stream.write(contents.charCodes());
418 return stream;
419 }
420 } 312 }
421 313
422 /** 314 /**
423 * Describes a directory and its contents. These are used both for setting up 315 * Describes a directory and its contents. These are used both for setting up
424 * an expected directory tree before running a test, and for validating that 316 * an expected directory tree before running a test, and for validating that
425 * the file system matches some expectations after running it. 317 * the file system matches some expectations after running it.
426 */ 318 */
427 class DirectoryDescriptor extends Descriptor { 319 class DirectoryDescriptor extends Descriptor {
428 /** 320 /**
429 * The files and directories contained in this directory. 321 * The files and directories contained in this directory.
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
463 * contain the stuff we do expect. 355 * contain the stuff we do expect.
464 */ 356 */
465 Future validate(String path) { 357 Future validate(String path) {
466 // Validate each of the items in this directory. 358 // Validate each of the items in this directory.
467 final entryFutures = contents.map( 359 final entryFutures = contents.map(
468 (entry) => entry.validate(join(path, name))); 360 (entry) => entry.validate(join(path, name)));
469 361
470 // If they are all valid, the directory is valid. 362 // If they are all valid, the directory is valid.
471 return Futures.wait(entryFutures).transform((entries) => null); 363 return Futures.wait(entryFutures).transform((entries) => null);
472 } 364 }
473
474 /**
475 * Loads [path] from within this directory.
476 */
477 InputStream load(List<String> path) {
478 if (path.isEmpty()) {
479 throw "Can't load the contents of $name: is a directory.";
480 }
481
482 for (var descriptor in contents) {
483 if (descriptor.name == path[0]) {
484 return descriptor.load(path.getRange(1, path.length - 1));
485 }
486 }
487
488 throw "Directory $name doesn't contain ${Strings.join('/', path)}.";
489 }
490 } 365 }
491 366
492 /** 367 /**
493 * Describes a Git repository and its contents. 368 * Describes a Git repository and its contents.
494 */ 369 */
495 class GitRepoDescriptor extends DirectoryDescriptor { 370 class GitRepoDescriptor extends DirectoryDescriptor {
496 GitRepoDescriptor(String name, List<Descriptor> contents) 371 GitRepoDescriptor(String name, List<Descriptor> contents)
497 : super(name, contents); 372 : super(name, contents);
498 373
499 /** 374 /**
(...skipping 12 matching lines...) Expand all
512 return super.create(parentDir).chain((rootDir) { 387 return super.create(parentDir).chain((rootDir) {
513 workingDir = rootDir; 388 workingDir = rootDir;
514 return runGit(['init']); 389 return runGit(['init']);
515 }).chain((_) => runGit(['add', '.'])) 390 }).chain((_) => runGit(['add', '.']))
516 .chain((_) => runGit(['commit', '-m', 'initial commit'])) 391 .chain((_) => runGit(['commit', '-m', 'initial commit']))
517 .transform((_) => workingDir); 392 .transform((_) => workingDir);
518 } 393 }
519 } 394 }
520 395
521 /** 396 /**
522 * Describes a gzipped tar file and its contents.
523 */
524 class TarFileDescriptor extends Descriptor {
525 final List<Descriptor> contents;
526
527 TarFileDescriptor(String name, this.contents)
528 : super(name);
529
530 /**
531 * Creates the files and directories within this tar file, then archives them,
532 * compresses them, and saves the result to [parentDir].
533 */
534 Future<File> create(parentDir) {
535 var tempDir;
536 return parentDir.createTemp().chain((_tempDir) {
537 tempDir = _tempDir;
538 return Futures.wait(contents.map((child) => child.create(tempDir)));
539 }).chain((_) {
540 var args = ["--directory", tempDir.path, "--create", "--gzip", "--file",
541 join(parentDir, name)];
542 args.addAll(contents.map((child) => child.name));
543 return runProcess("tar", args);
544 }).chain((result) {
545 if (!result.success) {
546 throw "Failed to create tar file $name.\n"
547 "STDERR: ${Strings.join(result.stderr, "\n")}";
548 }
549 return deleteDir(tempDir);
550 }).transform((_) {
551 return new File(join(parentDir, name));
552 });
553 }
554
555 /**
556 * Validates that the `.tar.gz` file at [path] contains the expected contents.
557 */
558 Future validate(String path) {
559 throw "TODO(nweiz): implement this";
560 }
561
562 /**
563 * Loads the contents of this tar file.
564 */
565 InputStream load(List<String> path) {
566 if (!path.isEmpty()) {
567 var joinedPath = Strings.join('/', path);
568 throw "Can't load $joinedPath from within $name: not a directory.";
569 }
570
571 var stream = new ListInputStream();
572 var tempDir;
573 // TODO(nweiz): propagate any errors to the return value. See issue 3657.
574 createTempDir("pub-test-tmp-").chain((_tempDir) {
575 tempDir = _tempDir;
576 return create(tempDir);
577 }).then((tar) {
578 pipeInputToInput(tar.openInputStream(), stream);
579 tempDir.deleteRecursively();
580 });
581 return stream;
582 }
583 }
584
585 /**
586 * Schedules a callback to be called before Pub is run with [runPub]. 397 * Schedules a callback to be called before Pub is run with [runPub].
587 */ 398 */
588 void _scheduleBeforePub(_ScheduledEvent event) { 399 void _scheduleBeforePub(_ScheduledEvent event) {
589 if (_scheduledBeforePub == null) _scheduledBeforePub = []; 400 if (_scheduledBeforePub == null) _scheduledBeforePub = [];
590 _scheduledBeforePub.add(event); 401 _scheduledBeforePub.add(event);
591 } 402 }
592 403
593 /** 404 /**
594 * Schedules a callback to be called after Pub is run with [runPub]. 405 * Schedules a callback to be called after Pub is run with [runPub].
595 */ 406 */
596 void _scheduleAfterPub(_ScheduledEvent event) { 407 void _scheduleAfterPub(_ScheduledEvent event) {
597 if (_scheduledAfterPub == null) _scheduledAfterPub = []; 408 if (_scheduledAfterPub == null) _scheduledAfterPub = [];
598 _scheduledAfterPub.add(event); 409 _scheduledAfterPub.add(event);
599 } 410 }
600
601 /**
602 * Schedules a callback to be called after Pub is run with [runPub], even if it
603 * fails.
604 */
605 void _scheduleCleanup(_ScheduledEvent event) {
606 if (_scheduledCleanup == null) _scheduledCleanup = [];
607 _scheduledCleanup.add(event);
608 }
OLDNEW
« no previous file with comments | « utils/tests/pub/pub_test.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698