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

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

Issue 10658025: Add a Pub source for pub.dartlang.org. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 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 | 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');
14 15
15 #import('../../../lib/unittest/unittest.dart'); 16 #import('../../../lib/unittest/unittest.dart');
16 #import('../../lib/file_system.dart', prefix: 'fs'); 17 #import('../../lib/file_system.dart', prefix: 'fs');
17 #import('../../pub/io.dart'); 18 #import('../../pub/io.dart');
19 #import('../../pub/yaml/yaml.dart');
18 20
19 /** 21 /**
20 * Creates a new [FileDescriptor] with [name] and [contents]. 22 * Creates a new [FileDescriptor] with [name] and [contents].
21 */ 23 */
22 FileDescriptor file(String name, String contents) => 24 FileDescriptor file(String name, String contents) =>
23 new FileDescriptor(name, contents); 25 new FileDescriptor(name, contents);
24 26
25 /** 27 /**
26 * Creates a new [DirectoryDescriptor] with [name] and [contents]. 28 * Creates a new [DirectoryDescriptor] with [name] and [contents].
27 */ 29 */
28 DirectoryDescriptor dir(String name, [List<Descriptor> contents]) => 30 DirectoryDescriptor dir(String name, [List<Descriptor> contents]) =>
29 new DirectoryDescriptor(name, contents); 31 new DirectoryDescriptor(name, contents);
30 32
31 /** 33 /**
32 * Creates a new [GitRepoDescriptor] with [name] and [contents]. 34 * Creates a new [GitRepoDescriptor] with [name] and [contents].
33 */ 35 */
34 DirectoryDescriptor git(String name, [List<Descriptor> contents]) => 36 DirectoryDescriptor git(String name, [List<Descriptor> contents]) =>
35 new GitRepoDescriptor(name, contents); 37 new GitRepoDescriptor(name, contents);
36 38
37 /** 39 /**
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 /**
38 * The path of the package cache directory used for tests. Relative to the 118 * The path of the package cache directory used for tests. Relative to the
39 * sandbox directory. 119 * sandbox directory.
40 */ 120 */
41 final String cachePath = "cache"; 121 final String cachePath = "cache";
42 122
43 /** 123 /**
44 * The path of the mock SDK directory used for tests. Relative to the sandbox 124 * The path of the mock SDK directory used for tests. Relative to the sandbox
45 * directory. 125 * directory.
46 */ 126 */
47 final String sdkPath = "sdk"; 127 final String sdkPath = "sdk";
(...skipping 20 matching lines...) Expand all
68 * The list of events that are scheduled to run after the sandbox directory has 148 * The list of events that are scheduled to run after the sandbox directory has
69 * been created but before Pub is run. 149 * been created but before Pub is run.
70 */ 150 */
71 List<_ScheduledEvent> _scheduledBeforePub; 151 List<_ScheduledEvent> _scheduledBeforePub;
72 152
73 /** 153 /**
74 * The list of events that are scheduled to run after Pub has been run. 154 * The list of events that are scheduled to run after Pub has been run.
75 */ 155 */
76 List<_ScheduledEvent> _scheduledAfterPub; 156 List<_ScheduledEvent> _scheduledAfterPub;
77 157
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
78 void runPub([List<String> args, Pattern output, Pattern error, 164 void runPub([List<String> args, Pattern output, Pattern error,
79 int exitCode = 0]) { 165 int exitCode = 0]) {
80 var createdSandboxDir; 166 var createdSandboxDir;
81 167
82 var asyncDone = expectAsync0(() {}); 168 var asyncDone = expectAsync0(() {});
83 169
84 deleteSandboxIfCreated(onDeleted()) { 170 Future cleanup() {
85 _scheduledBeforePub = null; 171 return _runScheduled(createdSandboxDir, _scheduledCleanup).chain((_) {
86 _scheduledAfterPub = null; 172 _scheduledBeforePub = null;
87 if (createdSandboxDir != null) { 173 _scheduledAfterPub = null;
88 deleteDir(createdSandboxDir).then((_) => onDeleted()); 174 if (createdSandboxDir != null) return deleteDir(createdSandboxDir);
89 } else { 175 return new Future.immediate(null);
90 onDeleted(); 176 });
91 }
92 } 177 }
93 178
94 String pathInSandbox(path) => join(getFullPath(createdSandboxDir), path); 179 String pathInSandbox(path) => join(getFullPath(createdSandboxDir), path);
95 180
96 final future = _setUpSandbox().chain((sandboxDir) { 181 final future = _setUpSandbox().chain((sandboxDir) {
97 createdSandboxDir = sandboxDir; 182 createdSandboxDir = sandboxDir;
98 return _runScheduled(sandboxDir, _scheduledBeforePub); 183 return _runScheduled(sandboxDir, _scheduledBeforePub);
99 }).chain((_) { 184 }).chain((_) {
100 return ensureDir(pathInSandbox(appPath)); 185 return ensureDir(pathInSandbox(appPath));
101 }).chain((_) { 186 }).chain((_) {
102 // TODO(rnystrom): Hack in the cache directory path. Should pass this 187 // TODO(rnystrom): Hack in the cache directory path. Should pass this
103 // in using environment var once #752 is done. 188 // in using environment var once #752 is done.
104 args.add('--cachedir=${pathInSandbox(cachePath)}'); 189 args.add('--cachedir=${pathInSandbox(cachePath)}');
105 190
106 // TODO(rnystrom): Hack in the SDK path. Should pass this in using 191 // TODO(rnystrom): Hack in the SDK path. Should pass this in using
107 // environment var once #752 is done. 192 // environment var once #752 is done.
108 args.add('--sdkdir=${pathInSandbox(sdkPath)}'); 193 args.add('--sdkdir=${pathInSandbox(sdkPath)}');
109 194
110 return _runPub(args, pathInSandbox(appPath)); 195 return _runPub(args, pathInSandbox(appPath), pipeStdout: output == null,
196 pipeStderr: error == null);
111 }).chain((result) { 197 }).chain((result) {
112 _validateOutput(output, result.stdout); 198 _validateOutput(output, result.stdout);
113 _validateOutput(error, result.stderr); 199 _validateOutput(error, result.stderr);
114 200
115 Expect.equals(result.exitCode, exitCode, 201 Expect.equals(result.exitCode, exitCode,
116 'Pub returned exit code ${result.exitCode}, expected $exitCode.'); 202 'Pub returned exit code ${result.exitCode}, expected $exitCode.');
117 203
118 return _runScheduled(createdSandboxDir, _scheduledAfterPub); 204 return _runScheduled(createdSandboxDir, _scheduledAfterPub);
119 }); 205 });
120 206
121 future.then((_) { 207 future.chain((_) => cleanup()).then((_) => asyncDone());
122 deleteSandboxIfCreated(asyncDone);
123 });
124 208
125 future.handleException((error) { 209 future.handleException((error) {
126 // If an error occurs during testing, delete the sandbox, throw the error so 210 // If an error occurs during testing, delete the sandbox, throw the error so
127 // that the test framework sees it, then finally call asyncDone so that the 211 // that the test framework sees it, then finally call asyncDone so that the
128 // test framework knows we're done doing asynchronous stuff. 212 // test framework knows we're done doing asynchronous stuff.
129 deleteSandboxIfCreated(() { 213 cleanup().then((_) {
130 guardAsync(() { throw error; }, asyncDone); 214 guardAsync(() { throw error; }, asyncDone);
131 }); 215 });
132 return true; 216 return true;
133 }); 217 });
134 } 218 }
135 219
136 220
137 /** 221 /**
138 * Wraps a test that needs git in order to run. This validates that the test is 222 * Wraps a test that needs git in order to run. This validates that the test is
139 * running on a builbot in which case we expect git to be installed. If we are 223 * running on a builbot in which case we expect git to be installed. If we are
140 * not running on the buildbot, we will instead see if git is installed and 224 * not running on the buildbot, we will instead see if git is installed and
141 * skip the test if not. This way, users don't need to have git installed to 225 * skip the test if not. This way, users don't need to have git installed to
142 * run the tests locally (unless they actually care about the pub git tests). 226 * run the tests locally (unless they actually care about the pub git tests).
143 */ 227 */
144 void withGit(void callback()) { 228 void withGit(void callback()) {
145 isGitInstalled.then(expectAsync1((installed) { 229 isGitInstalled.then(expectAsync1((installed) {
146 if (installed || Platform.environment.containsKey('BUILDBOT_BUILDERNAME')) { 230 if (installed || Platform.environment.containsKey('BUILDBOT_BUILDERNAME')) {
147 callback(); 231 callback();
148 } 232 }
149 })); 233 }));
150 } 234 }
151 235
152 Future<Directory> _setUpSandbox() { 236 Future<Directory> _setUpSandbox() {
153 return createTempDir('pub-test-sandbox-'); 237 return createTempDir('pub-test-sandbox-');
154 } 238 }
155 239
156 _runScheduled(Directory parentDir, List<_ScheduledEvent> scheduled) { 240 _runScheduled(Directory parentDir, List<_ScheduledEvent> scheduled) {
157 if (scheduled == null) return new Future.immediate(null); 241 if (scheduled == null) return new Future.immediate(null);
158 var future = Futures.wait(scheduled.map((event) => event(parentDir))); 242 var future = Futures.wait(scheduled.map((event) {
243 var subFuture = event(parentDir);
244 return subFuture == null ? new Future.immediate(null) : subFuture;
245 }));
159 scheduled.clear(); 246 scheduled.clear();
160 return future; 247 return future;
161 } 248 }
162 249
163 Future<ProcessResult> _runPub(List<String> pubArgs, String workingDir) { 250 Future<ProcessResult> _runPub(List<String> pubArgs, String workingDir,
251 [bool pipeStdout=false, bool pipeStderr=false]) {
164 // Find a dart executable we can use to run pub. Uses the one that the 252 // Find a dart executable we can use to run pub. Uses the one that the
165 // test infrastructure uses. We are not using new Options.executable here 253 // test infrastructure uses. We are not using new Options.executable here
166 // because that gets confused if you invoked Dart through a shell script. 254 // because that gets confused if you invoked Dart through a shell script.
167 final scriptDir = new File(new Options().script).directorySync().path; 255 final scriptDir = new File(new Options().script).directorySync().path;
168 final platform = Platform.operatingSystem; 256 final platform = Platform.operatingSystem;
169 final dartBin = join(scriptDir, '../../../tools/testing/bin/$platform/dart'); 257 final dartBin = join(scriptDir, '../../../tools/testing/bin/$platform/dart');
170 258
171 // Find the main pub entrypoint. 259 // Find the main pub entrypoint.
172 final pubPath = fs.joinPaths(scriptDir, '../../pub/pub.dart'); 260 final pubPath = fs.joinPaths(scriptDir, '../../pub/pub.dart');
173 261
174 final args = ['--enable-type-checks', '--enable-asserts', pubPath]; 262 final args = ['--enable-type-checks', '--enable-asserts', pubPath];
175 args.addAll(pubArgs); 263 args.addAll(pubArgs);
176 264
177 return runProcess(dartBin, args, workingDir); 265 return runProcess(dartBin, args, workingDir, pipeStdout, pipeStderr);
178 } 266 }
179 267
180 /** 268 /**
181 * Compares the [actual] output from running pub with [expected]. For [String] 269 * Compares the [actual] output from running pub with [expected]. For [String]
182 * patterns, ignores leading and trailing whitespace differences and tries to 270 * patterns, ignores leading and trailing whitespace differences and tries to
183 * report the offending difference in a nice way. For other [Pattern]s, just 271 * report the offending difference in a nice way. For other [Pattern]s, just
184 * reports whether the output contained the pattern. 272 * reports whether the output contained the pattern.
185 */ 273 */
186 void _validateOutput(Pattern expected, List<String> actual) { 274 void _validateOutput(Pattern expected, List<String> actual) {
187 if (expected == null) return; 275 if (expected == null) return;
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
251 abstract Future create(dir); 339 abstract Future create(dir);
252 340
253 /** 341 /**
254 * Validates that this descriptor correctly matches the corresponding file 342 * Validates that this descriptor correctly matches the corresponding file
255 * system entry within [dir]. Returns a [Future] that completes to `null` if 343 * system entry within [dir]. Returns a [Future] that completes to `null` if
256 * the entry is valid, or throws an error if it failed. 344 * the entry is valid, or throws an error if it failed.
257 */ 345 */
258 abstract Future validate(String dir); 346 abstract Future validate(String dir);
259 347
260 /** 348 /**
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 /**
261 * Schedules the directory to be created before Pub is run with [runPub]. The 355 * Schedules the directory to be created before Pub is run with [runPub]. The
262 * directory will be created relative to the sandbox directory. 356 * directory will be created relative to the sandbox directory.
263 */ 357 */
264 // TODO(nweiz): Use implicit closurization once issue 2984 is fixed. 358 // TODO(nweiz): Use implicit closurization once issue 2984 is fixed.
265 void scheduleCreate() => _scheduleBeforePub((dir) => this.create(dir)); 359 void scheduleCreate() => _scheduleBeforePub((dir) => this.create(dir));
266 360
267 /** 361 /**
268 * Schedules the directory to be validated after Pub is run with [runPub]. The 362 * Schedules the directory to be validated after Pub is run with [runPub]. The
269 * directory will be validated relative to the sandbox directory. 363 * directory will be validated relative to the sandbox directory.
270 */ 364 */
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
302 if (!exists) Expect.fail('Expected file $path does not exist.'); 396 if (!exists) Expect.fail('Expected file $path does not exist.');
303 397
304 return readTextFile(path).transform((text) { 398 return readTextFile(path).transform((text) {
305 if (text == contents) return null; 399 if (text == contents) return null;
306 400
307 Expect.fail('File $path should contain:\n\n$contents\n\n' 401 Expect.fail('File $path should contain:\n\n$contents\n\n'
308 'but contained:\n\n$text'); 402 'but contained:\n\n$text');
309 }); 403 });
310 }); 404 });
311 } 405 }
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 }
312 } 420 }
313 421
314 /** 422 /**
315 * Describes a directory and its contents. These are used both for setting up 423 * Describes a directory and its contents. These are used both for setting up
316 * an expected directory tree before running a test, and for validating that 424 * an expected directory tree before running a test, and for validating that
317 * the file system matches some expectations after running it. 425 * the file system matches some expectations after running it.
318 */ 426 */
319 class DirectoryDescriptor extends Descriptor { 427 class DirectoryDescriptor extends Descriptor {
320 /** 428 /**
321 * The files and directories contained in this directory. 429 * The files and directories contained in this directory.
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
355 * contain the stuff we do expect. 463 * contain the stuff we do expect.
356 */ 464 */
357 Future validate(String path) { 465 Future validate(String path) {
358 // Validate each of the items in this directory. 466 // Validate each of the items in this directory.
359 final entryFutures = contents.map( 467 final entryFutures = contents.map(
360 (entry) => entry.validate(join(path, name))); 468 (entry) => entry.validate(join(path, name)));
361 469
362 // If they are all valid, the directory is valid. 470 // If they are all valid, the directory is valid.
363 return Futures.wait(entryFutures).transform((entries) => null); 471 return Futures.wait(entryFutures).transform((entries) => null);
364 } 472 }
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 }
365 } 490 }
366 491
367 /** 492 /**
368 * Describes a Git repository and its contents. 493 * Describes a Git repository and its contents.
369 */ 494 */
370 class GitRepoDescriptor extends DirectoryDescriptor { 495 class GitRepoDescriptor extends DirectoryDescriptor {
371 GitRepoDescriptor(String name, List<Descriptor> contents) 496 GitRepoDescriptor(String name, List<Descriptor> contents)
372 : super(name, contents); 497 : super(name, contents);
373 498
374 /** 499 /**
(...skipping 12 matching lines...) Expand all
387 return super.create(parentDir).chain((rootDir) { 512 return super.create(parentDir).chain((rootDir) {
388 workingDir = rootDir; 513 workingDir = rootDir;
389 return runGit(['init']); 514 return runGit(['init']);
390 }).chain((_) => runGit(['add', '.'])) 515 }).chain((_) => runGit(['add', '.']))
391 .chain((_) => runGit(['commit', '-m', 'initial commit'])) 516 .chain((_) => runGit(['commit', '-m', 'initial commit']))
392 .transform((_) => workingDir); 517 .transform((_) => workingDir);
393 } 518 }
394 } 519 }
395 520
396 /** 521 /**
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 /**
397 * Schedules a callback to be called before Pub is run with [runPub]. 586 * Schedules a callback to be called before Pub is run with [runPub].
398 */ 587 */
399 void _scheduleBeforePub(_ScheduledEvent event) { 588 void _scheduleBeforePub(_ScheduledEvent event) {
400 if (_scheduledBeforePub == null) _scheduledBeforePub = []; 589 if (_scheduledBeforePub == null) _scheduledBeforePub = [];
401 _scheduledBeforePub.add(event); 590 _scheduledBeforePub.add(event);
402 } 591 }
403 592
404 /** 593 /**
405 * Schedules a callback to be called after Pub is run with [runPub]. 594 * Schedules a callback to be called after Pub is run with [runPub].
406 */ 595 */
407 void _scheduleAfterPub(_ScheduledEvent event) { 596 void _scheduleAfterPub(_ScheduledEvent event) {
408 if (_scheduledAfterPub == null) _scheduledAfterPub = []; 597 if (_scheduledAfterPub == null) _scheduledAfterPub = [];
409 _scheduledAfterPub.add(event); 598 _scheduledAfterPub.add(event);
410 } 599 }
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