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

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

Issue 10382094: Refactor the pub tests. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 8 years, 7 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
« lib/unittest/unittest.dart ('K') | « 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 */
(...skipping 10 matching lines...) Expand all
21 */ 21 */
22 FileDescriptor file(String name, String contents) => 22 FileDescriptor file(String name, String contents) =>
23 new FileDescriptor(name, contents); 23 new FileDescriptor(name, contents);
24 24
25 /** 25 /**
26 * Creates a new [DirectoryDescriptor] with [name] and [contents]. 26 * Creates a new [DirectoryDescriptor] with [name] and [contents].
27 */ 27 */
28 DirectoryDescriptor dir(String name, [List<Descriptor> contents]) => 28 DirectoryDescriptor dir(String name, [List<Descriptor> contents]) =>
29 new DirectoryDescriptor(name, contents); 29 new DirectoryDescriptor(name, contents);
30 30
31 void testPub(String description, [List<Descriptor> cache, Descriptor app, 31 /**
32 List<String> args, List<Descriptor> expectedPackageDir, 32 * The path of the package cache directory used for tests. Relative to the
33 List<Descriptor> sdk, String output, int exitCode = 0]) { 33 * sandbox directory.
34 asyncTest(description, 1, () { 34 */
35 var createdSandboxDir; 35 final String cachePath = "cache";
36 var createdAppDir;
37 var createdSdkDir;
38 36
39 deleteSandboxIfCreated() { 37 /**
40 if (createdSandboxDir != null) { 38 * The path of the mock SDK directory used for tests. Relative to the sandbox
41 deleteDir(createdSandboxDir).then((_) { 39 * directory.
42 callbackDone(); 40 */
43 }); 41 final String sdkPath = "sdk";
44 } else { 42
45 callbackDone(); 43 /**
46 } 44 * The path of the mock app directory used for tests. Relative to the sandbox
45 * directory.
46 */
47 final String appPath = "myapp";
48
49 /**
50 * The path of the packages directory in the mock app used for tests. Relative
51 * to the sandbox directory.
52 */
53 final String packagesPath = "$appPath/packages";
54
55 /**
56 * The type for callbacks that will be fired during [runPub]. Takes the sandbox
57 * directory as a parameter.
58 */
59 typedef Future _ScheduledEvent(Directory parentDir);
60
61 /**
62 * The list of events that are scheduled to run after the sandbox directory has
63 * been created but before Pub is run.
64 */
65 List<_ScheduledEvent> _scheduledBeforePub;
66
67 /**
68 * The list of events that are scheduled to run after Pub has been run.
69 */
70 List<_ScheduledEvent> _scheduledAfterPub;
71
72 void runPub([List<String> args, String output, int exitCode = 0]) {
73 var createdSandboxDir;
74
75 var asyncDone = expectAsync0(() {});
76
77 deleteSandboxIfCreated(onDeleted()) {
78 _scheduledBeforePub = [];
79 _scheduledAfterPub = [];
Bob Nystrom 2012/05/10 20:15:10 I would set these to null instead of an empty list
nweiz 2012/05/10 21:01:09 Done.
80 if (createdSandboxDir != null) {
81 deleteDir(createdSandboxDir).then((_) => onDeleted());
82 } else {
83 onDeleted();
47 } 84 }
85 }
48 86
49 final future = _setUpSandbox().chain((sandboxDir) { 87 String pathInSandbox(path) => join(getFullPath(createdSandboxDir), path);
50 createdSandboxDir = sandboxDir;
51 return _setUpApp(sandboxDir, app);
52 }).chain((appDir) {
53 createdAppDir = appDir;
54 return _setUpSdk(createdSandboxDir, sdk);
55 }).chain((sdkDir) {
56 createdSdkDir = sdkDir;
57 return _setUpCache(createdSandboxDir, cache);
58 }).chain((cacheDir) {
59 var workingDir;
60 if (createdAppDir != null) workingDir = createdAppDir.path;
61 88
62 if (cacheDir != null) { 89 Future runScheduled(List<_ScheduledEvent> scheduled) {
Bob Nystrom 2012/05/10 20:15:10 I would break this out into a (private) top-level
nweiz 2012/05/10 21:01:09 Done.
63 // TODO(rnystrom): Hack in the cache directory path. Should pass this 90 if (scheduled == null) return new Future.immediate(null);
64 // in using environment var once #752 is done. 91 var future = Futures.wait(
65 args.add('--cachedir=${getFullPath(cacheDir)}'); 92 scheduled.map((event) => event(createdSandboxDir)));
66 } 93 scheduled.clear();
94 return future;
95 }
67 96
68 if (createdSdkDir != null) { 97 final future = _setUpSandbox().chain((sandboxDir) {
69 // TODO(rnystrom): Hack in the SDK path. Should pass this in using 98 createdSandboxDir = sandboxDir;
70 // environment var once #752 is done. 99 return runScheduled(_scheduledBeforePub);
71 args.add('--sdkdir=${getFullPath(createdSdkDir)}'); 100 }).chain((_) {
72 } 101 return ensureDir(pathInSandbox(appPath));
102 }).chain((_) {
103 // TODO(rnystrom): Hack in the cache directory path. Should pass this
104 // in using environment var once #752 is done.
105 args.add('--cachedir=${pathInSandbox(cachePath)}');
73 106
74 return _runPub(args, workingDir); 107 // TODO(rnystrom): Hack in the SDK path. Should pass this in using
75 }).chain((result) { 108 // environment var once #752 is done.
76 _validateOutput(output, result.stdout); 109 args.add('--sdkdir=${pathInSandbox(sdkPath)}');
77 110
78 Expect.equals(result.stderr.length, 0, 111 return _runPub(args, pathInSandbox(appPath));
79 'Did not expect any output on stderr, and got:\n' + 112 }).chain((result) {
80 Strings.join(result.stderr, '\n')); 113 _validateOutput(output, result.stdout);
81 114
82 Expect.equals(result.exitCode, exitCode, 115 Expect.equals(result.stderr.length, 0,
83 'Pub returned exit code ${result.exitCode}, expected $exitCode.'); 116 'Did not expect any output on stderr, and got:\n' +
117 Strings.join(result.stderr, '\n'));
84 118
85 return _validateExpectedPackages(createdAppDir, expectedPackageDir); 119 Expect.equals(result.exitCode, exitCode,
120 'Pub returned exit code ${result.exitCode}, expected $exitCode.');
121
122 return runScheduled(_scheduledAfterPub);
123 });
124
125 future.then((_) {
126 deleteSandboxIfCreated(asyncDone);
127 });
128
129 future.handleException((error) {
130 // If an error occurs during testing, delete the sandbox, throw the error so
131 // that the test framework sees it, then finally call asyncDone so that the
132 // test framework knows we're done doing asynchronous stuff.
133 deleteSandboxIfCreated(() {
134 guard(() { throw error; }, asyncDone);
86 }); 135 });
87 136 return true;
88 future.then((error) {
89 // Null means there were no errors.
90 if (error != null) Expect.fail(error);
91
92 deleteSandboxIfCreated();
93 });
94
95 future.handleException((error) {
96 deleteSandboxIfCreated();
97 // If we encounter an error, we want to pass it to the test framework. In
98 // order to get the stack trace information, we need to re-throw and
99 // re-catch it.
100 try {
101 throw error;
102 } catch (var e, var stack) {
103 reportTestError('$e', '$stack');
104 }
105 return true;
106 });
107 }); 137 });
108 } 138 }
109 139
110 Future<Directory> _setUpSandbox() { 140 Future<Directory> _setUpSandbox() {
111 return createTempDir('pub-test-sandbox-'); 141 return createTempDir('pub-test-sandbox-');
112 } 142 }
113 143
114 Future _setUpCache(Directory sandboxDir, List<Descriptor> cache) { 144 Future _setUpCache(Directory sandboxDir, List<Descriptor> cache) {
115 // No cache. 145 // No cache.
116 if (cache == null) return new Future.immediate(null); 146 if (cache == null) return new Future.immediate(null);
(...skipping 25 matching lines...) Expand all
142 // Find the main pub entrypoint. 172 // Find the main pub entrypoint.
143 final pubPath = fs.joinPaths(scriptDir, '../../pub/pub.dart'); 173 final pubPath = fs.joinPaths(scriptDir, '../../pub/pub.dart');
144 174
145 final args = ['--enable-type-checks', '--enable-asserts', pubPath]; 175 final args = ['--enable-type-checks', '--enable-asserts', pubPath];
146 args.addAll(pubArgs); 176 args.addAll(pubArgs);
147 177
148 return runProcess(dartBin, args, workingDir); 178 return runProcess(dartBin, args, workingDir);
149 } 179 }
150 180
151 /** 181 /**
152 * Validates the contents of the "packages" directory inside [appDir] against
153 * [expectedPackageDir].
154 */
155 Future<String> _validateExpectedPackages(Directory appDir,
156 List<Descriptor> expectedPackageDir) {
157 // No expectation.
158 if (expectedPackageDir == null) return new Future.immediate(null);
159
160 return dir('packages', expectedPackageDir).validate(appDir.path);
161 }
162
163 /**
164 * Compares the [actual] output from running pub with [expectedText]. Ignores 182 * Compares the [actual] output from running pub with [expectedText]. Ignores
165 * leading and trailing whitespace differences and tries to report the 183 * leading and trailing whitespace differences and tries to report the
166 * offending difference in a nice way. 184 * offending difference in a nice way.
167 */ 185 */
168 void _validateOutput(String expectedText, List<String> actual) { 186 void _validateOutput(String expectedText, List<String> actual) {
169 final expected = expectedText.split('\n'); 187 final expected = expectedText.split('\n');
170 188
171 // Strip off the last line. This lets us have expected multiline strings 189 // Strip off the last line. This lets us have expected multiline strings
172 // where the closing ''' is on its own line. It also fixes '' expected output 190 // where the closing ''' is on its own line. It also fixes '' expected output
173 // to expect zero lines of output, not a single empty line. 191 // to expect zero lines of output, not a single empty line.
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
219 237
220 /** 238 /**
221 * Creates the file or directory within [dir]. Returns a [Future] that is 239 * Creates the file or directory within [dir]. Returns a [Future] that is
222 * completed after the creation is done. 240 * completed after the creation is done.
223 */ 241 */
224 abstract Future create(dir); 242 abstract Future create(dir);
225 243
226 /** 244 /**
227 * Validates that this descriptor correctly matches the corresponding file 245 * Validates that this descriptor correctly matches the corresponding file
228 * system entry within [dir]. Returns a [Future] that completes to `null` if 246 * system entry within [dir]. Returns a [Future] that completes to `null` if
229 * the entry is valid, or a message describing the error if it failed. 247 * the entry is valid, or throws an error if it failed.
230 */ 248 */
231 abstract Future<String> validate(String dir); 249 abstract Future validate(String dir);
250
251 /**
252 * Schedules the directory to be created before Pub is run with [runPub]. The
253 * directory will be created relative to the sandbox directory.
254 */
255 void scheduleCreate() => _scheduleBeforePub(create);
256
257 /**
258 * Schedules the directory to be validated after Pub is run with [runPub]. The
259 * directory will be validated relative to the sandbox directory.
260 */
261 void scheduleValidate() =>
262 _scheduleAfterPub((parentDir) => validate(parentDir));
232 } 263 }
233 264
234 /** 265 /**
235 * Describes a file. These are used both for setting up an expected directory 266 * Describes a file. These are used both for setting up an expected directory
236 * tree before running a test, and for validating that the file system matches 267 * tree before running a test, and for validating that the file system matches
237 * some expectations after running it. 268 * some expectations after running it.
238 */ 269 */
239 class FileDescriptor extends Descriptor { 270 class FileDescriptor extends Descriptor {
240 /** 271 /**
241 * The text contents of the file. 272 * The text contents of the file.
242 */ 273 */
243 final String contents; 274 final String contents;
244 275
245 FileDescriptor(String name, this.contents) : super(name); 276 FileDescriptor(String name, this.contents) : super(name);
246 277
247 /** 278 /**
248 * Creates the file within [dir]. Returns a [Future] that is completed after 279 * Creates the file within [dir]. Returns a [Future] that is completed after
249 * the creation is done. 280 * the creation is done.
250 */ 281 */
251 Future<File> create(dir) { 282 Future<File> create(dir) {
252 return writeTextFile(join(dir, name), contents); 283 return writeTextFile(join(dir, name), contents);
253 } 284 }
254 285
255 /** 286 /**
256 * Validates that this file correctly matches the actual file at [path]. 287 * Validates that this file correctly matches the actual file at [path].
257 */ 288 */
258 Future<String> validate(String path) { 289 Future validate(String path) {
259 path = join(path, name); 290 path = join(path, name);
260 return fileExists(path).chain((exists) { 291 return fileExists(path).chain((exists) {
261 if (!exists) { 292 if (!exists) Expect.fail('Expected file $path does not exist.');
262 return new Future.immediate('Expected file $path does not exist.');
263 }
264 293
265 return readTextFile(path).transform((text) { 294 return readTextFile(path).transform((text) {
266 if (text == contents) return null; 295 if (text == contents) return null;
267 296
268 return 'File $path should contain:\n\n$contents\n\n' 297 Expect.fail('File $path should contain:\n\n$contents\n\n'
269 'but contained:\n\n$text'; 298 'but contained:\n\n$text');
270 }); 299 });
271 }); 300 });
272 } 301 }
273 } 302 }
274 303
275 /** 304 /**
276 * Describes a directory and its contents. These are used both for setting up 305 * Describes a directory and its contents. These are used both for setting up
277 * an expected directory tree before running a test, and for validating that 306 * an expected directory tree before running a test, and for validating that
278 * the file system matches some expectations after running it. 307 * the file system matches some expectations after running it.
279 */ 308 */
(...skipping 28 matching lines...) Expand all
308 337
309 return completer.future; 338 return completer.future;
310 } 339 }
311 340
312 /** 341 /**
313 * Validates that the directory at [path] contains all of the expected 342 * Validates that the directory at [path] contains all of the expected
314 * contents in this descriptor. Note that this does *not* check that the 343 * contents in this descriptor. Note that this does *not* check that the
315 * directory doesn't contain other unexpected stuff, just that it *does* 344 * directory doesn't contain other unexpected stuff, just that it *does*
316 * contain the stuff we do expect. 345 * contain the stuff we do expect.
317 */ 346 */
318 Future<String> validate(String path) { 347 Future validate(String path) {
319 // Validate each of the items in this directory. 348 // Validate each of the items in this directory.
320 final entryFutures = contents.map( 349 final entryFutures = contents.map(
321 (entry) => entry.validate(join(path, name))); 350 (entry) => entry.validate(join(path, name)));
322 351
323 // If they are all valid, the directory is valid. 352 // If they are all valid, the directory is valid.
324 return Futures.wait(entryFutures).transform((entries) { 353 return Futures.wait(entryFutures).transform((entries) => null);
325 for (final entry in entries) {
326 if (entry != null) return entry;
327 }
328
329 // If we got here, all of the sub-entries were valid.
330 return null;
331 });
332 } 354 }
333 } 355 }
356
357 /**
358 * Schedules a callback to be called before Pub is run with [runPub].
359 */
360 void _scheduleBeforePub(_ScheduledEvent event) {
361 if (_scheduledBeforePub == null) _scheduledBeforePub = [];
362 _scheduledBeforePub.add(event);
363 }
364
365 /**
366 * Schedules a callback to be called after Pub is run with [runPub].
367 */
368 void _scheduleAfterPub(_ScheduledEvent event) {
369 if (_scheduledAfterPub == null) _scheduledAfterPub = [];
370 _scheduledAfterPub.add(event);
371 }
OLDNEW
« lib/unittest/unittest.dart ('K') | « utils/tests/pub/pub_test.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698