OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 /// Test infrastructure for testing pub. | 5 /// Test infrastructure for testing pub. |
6 /// | 6 /// |
7 /// Unlike typical unit tests, most pub tests are integration tests that stage | 7 /// Unlike typical unit tests, most pub tests are integration tests that stage |
8 /// some stuff on the file system, run pub, and then validate the results. This | 8 /// some stuff on the file system, run pub, and then validate the results. This |
9 /// library provides an API to build tests like that. | 9 /// library provides an API to build tests like that. |
10 import 'dart:async'; | 10 import 'dart:async'; |
(...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
212 schedule( | 212 schedule( |
213 () => createSymlink( | 213 () => createSymlink( |
214 p.join(sandboxDir, target), | 214 p.join(sandboxDir, target), |
215 p.join(sandboxDir, symlink)), | 215 p.join(sandboxDir, symlink)), |
216 'symlinking $target to $symlink'); | 216 'symlinking $target to $symlink'); |
217 } | 217 } |
218 | 218 |
219 /// Schedules a call to the Pub command-line utility. | 219 /// Schedules a call to the Pub command-line utility. |
220 /// | 220 /// |
221 /// Runs Pub with [args] and validates that its results match [output] (or | 221 /// Runs Pub with [args] and validates that its results match [output] (or |
222 /// [outputJson]), [error], and [exitCode]. | 222 /// [outputJson]), [error], [silent] (for logs that are silent by default), and |
| 223 /// [exitCode]. |
223 /// | 224 /// |
224 /// [output] and [error] can be [String]s, [RegExp]s, or [Matcher]s. | 225 /// [output], [error], and [silent] can be [String]s, [RegExp]s, or [Matcher]s. |
225 /// | 226 /// |
226 /// If [outputJson] is given, validates that pub outputs stringified JSON | 227 /// If [outputJson] is given, validates that pub outputs stringified JSON |
227 /// matching that object, which can be a literal JSON object or any other | 228 /// matching that object, which can be a literal JSON object or any other |
228 /// [Matcher]. | 229 /// [Matcher]. |
229 /// | 230 /// |
230 /// If [environment] is given, any keys in it will override the environment | 231 /// If [environment] is given, any keys in it will override the environment |
231 /// variables passed to the spawned process. | 232 /// variables passed to the spawned process. |
232 void schedulePub({List args, output, error, outputJson, | 233 void schedulePub({List args, output, error, outputJson, silent, |
233 int exitCode: exit_codes.SUCCESS, Map<String, String> environment}) { | 234 int exitCode: exit_codes.SUCCESS, Map<String, String> environment}) { |
234 // Cannot pass both output and outputJson. | 235 // Cannot pass both output and outputJson. |
235 assert(output == null || outputJson == null); | 236 assert(output == null || outputJson == null); |
236 | 237 |
237 var pub = startPub(args: args, environment: environment); | 238 var pub = startPub(args: args, environment: environment); |
238 pub.shouldExit(exitCode); | 239 pub.shouldExit(exitCode); |
239 | 240 |
240 var failures = []; | 241 expect(() async { |
241 var stderr; | 242 var actualOutput = (await pub.stdoutStream().toList()).join("\n"); |
| 243 var actualError = (await pub.stderrStream().toList()).join("\n"); |
| 244 var actualSilent = (await pub.silentStream().toList()).join("\n"); |
242 | 245 |
243 expect(Future.wait([ | 246 var failures = []; |
244 pub.stdoutStream().toList(), | |
245 pub.stderrStream().toList() | |
246 ]).then((results) { | |
247 var stdout = results[0].join("\n"); | |
248 stderr = results[1].join("\n"); | |
249 | |
250 if (outputJson == null) { | 247 if (outputJson == null) { |
251 _validateOutput(failures, 'stdout', output, stdout); | 248 _validateOutput(failures, 'stdout', output, actualOutput); |
252 return null; | 249 } else { |
| 250 _validateOutputJson( |
| 251 failures, 'stdout', await awaitObject(outputJson), actualOutput); |
253 } | 252 } |
254 | 253 |
255 // Allow the expected JSON to contain futures. | 254 _validateOutput(failures, 'stderr', error, actualError); |
256 return awaitObject(outputJson).then((resolved) { | 255 _validateOutput(failures, 'silent', silent, actualSilent); |
257 _validateOutputJson(failures, 'stdout', resolved, stdout); | |
258 }); | |
259 }).then((_) { | |
260 _validateOutput(failures, 'stderr', error, stderr); | |
261 | 256 |
262 if (!failures.isEmpty) throw new TestFailure(failures.join('\n')); | 257 if (!failures.isEmpty) throw new TestFailure(failures.join('\n')); |
263 }), completes); | 258 }(), completes); |
264 } | 259 } |
265 | 260 |
266 /// Like [startPub], but runs `pub lish` in particular with [server] used both | 261 /// Like [startPub], but runs `pub lish` in particular with [server] used both |
267 /// as the OAuth2 server (with "/token" as the token endpoint) and as the | 262 /// as the OAuth2 server (with "/token" as the token endpoint) and as the |
268 /// package server. | 263 /// package server. |
269 /// | 264 /// |
270 /// Any futures in [args] will be resolved before the process is started. | 265 /// Any futures in [args] will be resolved before the process is started. |
271 ScheduledProcess startPublish(ScheduledServer server, {List args}) { | 266 ScheduledProcess startPublish(ScheduledServer server, {List args}) { |
272 var tokenEndpoint = server.url.then((url) => | 267 var tokenEndpoint = server.url.then((url) => |
273 url.resolve('/token').toString()); | 268 url.resolve('/token').toString()); |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
366 description: args.isEmpty ? 'pub' : 'pub ${args.first}'); | 361 description: args.isEmpty ? 'pub' : 'pub ${args.first}'); |
367 } | 362 } |
368 | 363 |
369 /// A subclass of [ScheduledProcess] that parses pub's verbose logging output | 364 /// A subclass of [ScheduledProcess] that parses pub's verbose logging output |
370 /// and makes [stdout] and [stderr] work as though pub weren't running in | 365 /// and makes [stdout] and [stderr] work as though pub weren't running in |
371 /// verbose mode. | 366 /// verbose mode. |
372 class PubProcess extends ScheduledProcess { | 367 class PubProcess extends ScheduledProcess { |
373 Stream<Pair<log.Level, String>> _log; | 368 Stream<Pair<log.Level, String>> _log; |
374 Stream<String> _stdout; | 369 Stream<String> _stdout; |
375 Stream<String> _stderr; | 370 Stream<String> _stderr; |
| 371 Stream<String> _silent; |
376 | 372 |
377 PubProcess.start(executable, arguments, | 373 PubProcess.start(executable, arguments, |
378 {workingDirectory, environment, String description, | 374 {workingDirectory, environment, String description, |
379 Encoding encoding: UTF8}) | 375 Encoding encoding: UTF8}) |
380 : super.start(executable, arguments, | 376 : super.start(executable, arguments, |
381 workingDirectory: workingDirectory, | 377 workingDirectory: workingDirectory, |
382 environment: environment, | 378 environment: environment, |
383 description: description, | 379 description: description, |
384 encoding: encoding); | 380 encoding: encoding); |
385 | 381 |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
439 return []; | 435 return []; |
440 } | 436 } |
441 return [entry.last]; | 437 return [entry.last]; |
442 }); | 438 }); |
443 } | 439 } |
444 | 440 |
445 var pair = tee(_stderr); | 441 var pair = tee(_stderr); |
446 _stderr = pair.first; | 442 _stderr = pair.first; |
447 return pair.last; | 443 return pair.last; |
448 } | 444 } |
| 445 |
| 446 /// A stream of log messages that are silent by default. |
| 447 Stream<String> silentStream() { |
| 448 if (_silent == null) { |
| 449 _silent = _logStream().expand((entry) { |
| 450 if (entry.first == log.Level.MESSAGE) return []; |
| 451 if (entry.first == log.Level.ERROR) return []; |
| 452 if (entry.first == log.Level.WARNING) return []; |
| 453 return [entry.last]; |
| 454 }); |
| 455 } |
| 456 |
| 457 var pair = tee(_silent); |
| 458 _silent = pair.first; |
| 459 return pair.last; |
| 460 } |
449 } | 461 } |
450 | 462 |
451 /// Fails the current test if Git is not installed. | 463 /// Fails the current test if Git is not installed. |
452 /// | 464 /// |
453 /// We require machines running these tests to have git installed. This | 465 /// We require machines running these tests to have git installed. This |
454 /// validation gives an easier-to-understand error when that requirement isn't | 466 /// validation gives an easier-to-understand error when that requirement isn't |
455 /// met than just failing in the middle of a test when pub invokes git. | 467 /// met than just failing in the middle of a test when pub invokes git. |
456 /// | 468 /// |
457 /// This also increases the [Schedule] timeout to 30 seconds on Windows, | 469 /// This also increases the [Schedule] timeout to 30 seconds on Windows, |
458 /// where Git runs really slowly. | 470 /// where Git runs really slowly. |
(...skipping 284 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
743 _lastMatcher.matches(item.last, matchState); | 755 _lastMatcher.matches(item.last, matchState); |
744 } | 756 } |
745 | 757 |
746 Description describe(Description description) { | 758 Description describe(Description description) { |
747 return description.addAll("(", ", ", ")", [_firstMatcher, _lastMatcher]); | 759 return description.addAll("(", ", ", ")", [_firstMatcher, _lastMatcher]); |
748 } | 760 } |
749 } | 761 } |
750 | 762 |
751 /// A [StreamMatcher] that matches multiple lines of output. | 763 /// A [StreamMatcher] that matches multiple lines of output. |
752 StreamMatcher emitsLines(String output) => inOrder(output.split("\n")); | 764 StreamMatcher emitsLines(String output) => inOrder(output.split("\n")); |
OLD | NEW |