OLD | NEW |
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 | 14 |
15 #import('../../../lib/unittest/unittest.dart'); | 15 #import('../../../lib/unittest/unittest.dart'); |
16 #import('../../lib/file_system.dart'); | 16 #import('../../lib/file_system.dart', prefix: 'fs'); |
| 17 #import('../../pub/io.dart'); |
17 | 18 |
18 void testOutput(String description, List<String> pubArgs, String expected, | 19 /** |
19 [int exitCode = 0]) { | 20 * Creates a new [FileDescriptor] with [name] and [contents]. |
| 21 */ |
| 22 FileDescriptor file(String name, String contents) => |
| 23 new FileDescriptor(name, contents); |
| 24 |
| 25 /** |
| 26 * Creates a new [DirectoryDescriptor] with [name] and [contents]. |
| 27 */ |
| 28 DirectoryDescriptor dir(String name, [List<Descriptor> contents]) => |
| 29 new DirectoryDescriptor(name, contents); |
| 30 |
| 31 void testPub(String description, [List<Descriptor> cache, List<String> args, |
| 32 String output, int exitCode = 0]) { |
20 asyncTest(description, 1, () { | 33 asyncTest(description, 1, () { |
21 // Find a dart executable we can use to run pub. Uses the one that the | 34 var createdSandboxDir; |
22 // test infrastructure uses. | |
23 final scriptDir = new File(new Options().script).directorySync().path; | |
24 final platform = Platform.operatingSystem(); | |
25 final dartBin = joinPaths(scriptDir, | |
26 '../../../tools/testing/bin/$platform/dart'); | |
27 | 35 |
28 // Find the main pub entrypoint. | 36 deleteSandboxIfCreated() { |
29 final pubPath = joinPaths(scriptDir, '../../pub/pub.dart'); | 37 if (createdSandboxDir != null) { |
30 | 38 deleteDir(createdSandboxDir).then((_) { |
31 final args = [pubPath]; | 39 callbackDone(); |
32 args.addAll(pubArgs); | 40 }); |
33 | 41 } else { |
34 final process = new Process.start(dartBin, args); | 42 callbackDone(); |
35 final outStream = new StringInputStream(process.stdout); | 43 } |
36 final output = <String>[]; | |
37 bool processDone = false; | |
38 | |
39 checkComplete() { | |
40 if (!outStream.closed) return; | |
41 if (!processDone) return; | |
42 | |
43 _validateOutput(expected, output); | |
44 callbackDone(); | |
45 } | 44 } |
46 | 45 |
47 process.stderr.pipe(stderr, close: false); | 46 final future = _setUpSandbox().chain((sandboxDir) { |
| 47 createdSandboxDir = sandboxDir; |
| 48 return _setUpCache(sandboxDir, cache); |
| 49 }).chain((cacheDir) { |
| 50 if (cacheDir != null) { |
| 51 // TODO(rnystrom): Hack in the cache directory path. Should pass this |
| 52 // in using environment var once #752 is done. |
| 53 args.add('--cachedir=${cacheDir.path}'); |
| 54 } |
48 | 55 |
49 outStream.onLine = () { | 56 return _runPub(args); |
50 output.add(outStream.readLine()); | 57 }); |
51 }; | |
52 | 58 |
53 outStream.onClosed = checkComplete; | 59 future.then((result) { |
| 60 _validateOutput(output, result.stdout); |
54 | 61 |
55 process.onError = (error) { | 62 Expect.equals(result.stderr.length, 0, |
56 Expect.fail('Failed to run pub: $error'); | 63 'Did not expect any output on stderr, and got:\n' + |
57 processDone = true; | 64 Strings.join(result.stderr, '\n')); |
58 }; | |
59 | 65 |
60 process.onExit = (actualExitCode) { | 66 Expect.equals(result.exitCode, exitCode, |
61 Expect.equals(actualExitCode, exitCode, | 67 'Pub returned exit code ${result.exitCode}, expected $exitCode.'); |
62 'Pub returned exit code $actualExitCode, expected $exitCode.'); | 68 |
63 processDone = true; | 69 deleteSandboxIfCreated(); |
64 checkComplete(); | 70 }); |
65 }; | 71 |
| 72 future.handleException((error) { |
| 73 deleteSandboxIfCreated(); |
| 74 }); |
66 }); | 75 }); |
67 } | 76 } |
68 | 77 |
| 78 Future<Directory> _setUpSandbox() { |
| 79 return createTempDir('pub-test-sandbox-'); |
| 80 } |
| 81 |
| 82 Future _setUpCache(Directory sandboxDir, List<Descriptor> cache) { |
| 83 // No cache. |
| 84 if (cache == null) return new Future.immediate(null); |
| 85 |
| 86 return dir('pub-cache', cache).create(sandboxDir); |
| 87 } |
| 88 |
| 89 Future<ProcessResult> _runPub(List<String> pubArgs) { |
| 90 // Find a dart executable we can use to run pub. Uses the one that the |
| 91 // test infrastructure uses. |
| 92 final scriptDir = new File(new Options().script).directorySync().path; |
| 93 final platform = Platform.operatingSystem(); |
| 94 final dartBin = join(scriptDir, '../../../tools/testing/bin/$platform/dart'); |
| 95 |
| 96 // Find the main pub entrypoint. |
| 97 final pubPath = fs.joinPaths(scriptDir, '../../pub/pub.dart'); |
| 98 |
| 99 final args = [pubPath]; |
| 100 args.addAll(pubArgs); |
| 101 |
| 102 return runProcess(dartBin, args); |
| 103 } |
| 104 |
69 /** | 105 /** |
70 * Compares the [actual] output from running pub with [expectedText]. Ignores | 106 * Compares the [actual] output from running pub with [expectedText]. Ignores |
71 * leading and trailing whitespace differences and tries to report the | 107 * leading and trailing whitespace differences and tries to report the |
72 * offending difference in a nice way. | 108 * offending difference in a nice way. |
73 */ | 109 */ |
74 void _validateOutput(String expectedText, List<String> actual) { | 110 void _validateOutput(String expectedText, List<String> actual) { |
75 final expected = expectedText.split('\n'); | 111 final expected = expectedText.split('\n'); |
76 | 112 |
77 final length = Math.min(expected.length, actual.length); | 113 final length = Math.min(expected.length, actual.length); |
78 for (var i = 0; i < length; i++) { | 114 for (var i = 0; i < length; i++) { |
(...skipping 18 matching lines...) Expand all Loading... |
97 final message = new StringBuffer(); | 133 final message = new StringBuffer(); |
98 message.add('Unexpected output:\n'); | 134 message.add('Unexpected output:\n'); |
99 for (var i = expected.length; i < actual.length; i++) { | 135 for (var i = expected.length; i < actual.length; i++) { |
100 message.add(actual[i]); | 136 message.add(actual[i]); |
101 message.add('\n'); | 137 message.add('\n'); |
102 } | 138 } |
103 | 139 |
104 Expect.fail(message.toString()); | 140 Expect.fail(message.toString()); |
105 } | 141 } |
106 } | 142 } |
| 143 |
| 144 /** |
| 145 * Base class for [FileDescriptor] and [DirectoryDescriptor] so that a |
| 146 * directory can contain a heterogeneous collection of files and |
| 147 * subdirectories. |
| 148 */ |
| 149 class Descriptor { |
| 150 /** |
| 151 * The short name of this file or directory. |
| 152 */ |
| 153 final String name; |
| 154 |
| 155 Descriptor(this.name); |
| 156 |
| 157 /** |
| 158 * Creates the file or directory within [dir]. Returns a [Future] that is |
| 159 * completed after the creation is done. |
| 160 */ |
| 161 abstract Future create(String dir); |
| 162 } |
| 163 |
| 164 /** |
| 165 * Describes a file. These are used both for setting up an expected directory |
| 166 * tree before running a test, and for validating that the file system matches |
| 167 * some expectations after running it. |
| 168 */ |
| 169 class FileDescriptor extends Descriptor { |
| 170 /** |
| 171 * The text contents of the file. |
| 172 */ |
| 173 final String contents; |
| 174 |
| 175 FileDescriptor(String name, this.contents) : super(name); |
| 176 |
| 177 /** |
| 178 * Creates the file within [dir]. Returns a [Future] that is completed after |
| 179 * the creation is done. |
| 180 */ |
| 181 Future<File> create(String dir) { |
| 182 return writeTextFile(join(dir, name), contents); |
| 183 } |
| 184 } |
| 185 |
| 186 /** |
| 187 * Describes a directory and its contents. These are used both for setting up |
| 188 * an expected directory tree before running a test, and for validating that |
| 189 * the file system matches some expectations after running it. |
| 190 */ |
| 191 class DirectoryDescriptor extends Descriptor { |
| 192 /** |
| 193 * The files and directories contained in this directory. |
| 194 */ |
| 195 final List<Descriptor> contents; |
| 196 |
| 197 DirectoryDescriptor(String name, this.contents) : super(name); |
| 198 |
| 199 /** |
| 200 * Creates the file within [dir]. Returns a [Future] that is completed after |
| 201 * the creation is done. |
| 202 */ |
| 203 Future<Directory> create(String parentDir) { |
| 204 final completer = new Completer<Directory>(); |
| 205 |
| 206 // Create the directory. |
| 207 createDir(join(parentDir, name)).then((dir) { |
| 208 if (contents == null) { |
| 209 completer.complete(dir); |
| 210 } else { |
| 211 // Recursively create all of its children. |
| 212 final childFutures = contents.map((child) => child.create(dir.path)); |
| 213 Futures.wait(childFutures).then((_) { |
| 214 // Only complete once all of the children have been created too. |
| 215 completer.complete(dir); |
| 216 }); |
| 217 } |
| 218 }); |
| 219 |
| 220 return completer.future; |
| 221 } |
| 222 } |
OLD | NEW |