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. Unlike typical unit tests, most pub | 5 /// Test infrastructure for testing pub. Unlike typical unit tests, most pub |
6 /// tests are integration tests that stage some stuff on the file system, run | 6 /// tests are integration tests that stage some stuff on the file system, run |
7 /// pub, and then validate the results. This library provides an API to build | 7 /// pub, and then validate the results. This library provides an API to build |
8 /// tests like that. | 8 /// tests like that. |
9 library test_pub; | 9 library test_pub; |
10 | 10 |
(...skipping 15 matching lines...) Expand all Loading... |
26 | 26 |
27 import '../lib/src/entrypoint.dart'; | 27 import '../lib/src/entrypoint.dart'; |
28 // TODO(rnystrom): Using "gitlib" as the prefix here is ugly, but "git" collides | 28 // TODO(rnystrom): Using "gitlib" as the prefix here is ugly, but "git" collides |
29 // with the git descriptor method. Maybe we should try to clean up the top level | 29 // with the git descriptor method. Maybe we should try to clean up the top level |
30 // scope a bit? | 30 // scope a bit? |
31 import '../lib/src/git.dart' as gitlib; | 31 import '../lib/src/git.dart' as gitlib; |
32 import '../lib/src/git_source.dart'; | 32 import '../lib/src/git_source.dart'; |
33 import '../lib/src/hosted_source.dart'; | 33 import '../lib/src/hosted_source.dart'; |
34 import '../lib/src/http.dart'; | 34 import '../lib/src/http.dart'; |
35 import '../lib/src/io.dart'; | 35 import '../lib/src/io.dart'; |
| 36 import '../lib/src/log.dart' as log; |
36 import '../lib/src/path_source.dart'; | 37 import '../lib/src/path_source.dart'; |
37 import '../lib/src/safe_http_server.dart'; | 38 import '../lib/src/safe_http_server.dart'; |
38 import '../lib/src/system_cache.dart'; | 39 import '../lib/src/system_cache.dart'; |
39 import '../lib/src/utils.dart'; | 40 import '../lib/src/utils.dart'; |
40 import '../lib/src/validator.dart'; | 41 import '../lib/src/validator.dart'; |
41 import 'command_line_config.dart'; | 42 import 'command_line_config.dart'; |
42 import 'descriptor.dart' as d; | 43 import 'descriptor.dart' as d; |
43 | 44 |
44 /// This should be called at the top of a test file to set up an appropriate | 45 /// This should be called at the top of a test file to set up an appropriate |
45 /// test configuration for the machine running the tests. | 46 /// test configuration for the machine running the tests. |
(...skipping 280 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
326 /// upload. | 327 /// upload. |
327 void confirmPublish(ScheduledProcess pub) { | 328 void confirmPublish(ScheduledProcess pub) { |
328 // TODO(rnystrom): This is overly specific and inflexible regarding different | 329 // TODO(rnystrom): This is overly specific and inflexible regarding different |
329 // test packages. Should validate this a little more loosely. | 330 // test packages. Should validate this a little more loosely. |
330 expect(pub.nextLine(), completion(equals('Publishing "test_pkg" 1.0.0:'))); | 331 expect(pub.nextLine(), completion(equals('Publishing "test_pkg" 1.0.0:'))); |
331 expect(pub.nextLine(), completion(equals("|-- LICENSE"))); | 332 expect(pub.nextLine(), completion(equals("|-- LICENSE"))); |
332 expect(pub.nextLine(), completion(equals("|-- lib"))); | 333 expect(pub.nextLine(), completion(equals("|-- lib"))); |
333 expect(pub.nextLine(), completion(equals("| '-- test_pkg.dart"))); | 334 expect(pub.nextLine(), completion(equals("| '-- test_pkg.dart"))); |
334 expect(pub.nextLine(), completion(equals("'-- pubspec.yaml"))); | 335 expect(pub.nextLine(), completion(equals("'-- pubspec.yaml"))); |
335 expect(pub.nextLine(), completion(equals(""))); | 336 expect(pub.nextLine(), completion(equals(""))); |
| 337 expect(pub.nextLine(), completion(equals('Looks great! Are you ready to ' |
| 338 'upload your package (y/n)?'))); |
336 | 339 |
337 pub.writeLine("y"); | 340 pub.writeLine("y"); |
338 } | 341 } |
339 | 342 |
340 /// Starts a Pub process and returns a [ScheduledProcess] that supports | 343 /// Starts a Pub process and returns a [ScheduledProcess] that supports |
341 /// interaction with that process. | 344 /// interaction with that process. |
342 /// | 345 /// |
343 /// Any futures in [args] will be resolved before the process is started. | 346 /// Any futures in [args] will be resolved before the process is started. |
344 ScheduledProcess startPub({List args, Future<Uri> tokenEndpoint}) { | 347 ScheduledProcess startPub({List args, Future<Uri> tokenEndpoint}) { |
345 String pathInSandbox(String relPath) { | 348 String pathInSandbox(String relPath) { |
346 return path.join(path.absolute(sandboxDir), relPath); | 349 return path.join(path.absolute(sandboxDir), relPath); |
347 } | 350 } |
348 | 351 |
349 ensureDir(pathInSandbox(appPath)); | 352 ensureDir(pathInSandbox(appPath)); |
350 | 353 |
351 // Find a Dart executable we can use to spawn. Use the same one that was | 354 // Find a Dart executable we can use to spawn. Use the same one that was |
352 // used to run this script itself. | 355 // used to run this script itself. |
353 var dartBin = new Options().executable; | 356 var dartBin = new Options().executable; |
354 | 357 |
355 // If the executable looks like a path, get its full path. That way we | 358 // If the executable looks like a path, get its full path. That way we |
356 // can still find it when we spawn it with a different working directory. | 359 // can still find it when we spawn it with a different working directory. |
357 if (dartBin.contains(Platform.pathSeparator)) { | 360 if (dartBin.contains(Platform.pathSeparator)) { |
358 dartBin = path.absolute(dartBin); | 361 dartBin = path.absolute(dartBin); |
359 } | 362 } |
360 | 363 |
361 // Find the main pub entrypoint. | 364 // Find the main pub entrypoint. |
362 var pubPath = path.join(testDirectory, '..', 'bin', 'pub.dart'); | 365 var pubPath = path.join(testDirectory, '..', 'bin', 'pub.dart'); |
363 | 366 |
364 var dartArgs = ['--package-root=$_packageRoot/', '--checked', pubPath, | 367 var dartArgs = ['--package-root=$_packageRoot/', '--checked', pubPath, |
365 '--trace']; | 368 '--verbose']; |
366 dartArgs.addAll(args); | 369 dartArgs.addAll(args); |
367 | 370 |
368 if (tokenEndpoint == null) tokenEndpoint = new Future.value(); | 371 if (tokenEndpoint == null) tokenEndpoint = new Future.value(); |
369 var optionsFuture = tokenEndpoint.then((tokenEndpoint) { | 372 var optionsFuture = tokenEndpoint.then((tokenEndpoint) { |
370 var options = new ProcessOptions(); | 373 var options = new ProcessOptions(); |
371 options.workingDirectory = pathInSandbox(appPath); | 374 options.workingDirectory = pathInSandbox(appPath); |
372 // TODO(nweiz): remove this when issue 9294 is fixed. | 375 // TODO(nweiz): remove this when issue 9294 is fixed. |
373 options.environment = new Map.from(Platform.environment); | 376 options.environment = new Map.from(Platform.environment); |
374 options.environment['_PUB_TESTING'] = 'true'; | 377 options.environment['_PUB_TESTING'] = 'true'; |
375 options.environment['PUB_CACHE'] = pathInSandbox(cachePath); | 378 options.environment['PUB_CACHE'] = pathInSandbox(cachePath); |
376 options.environment['DART_SDK'] = pathInSandbox(sdkPath); | 379 options.environment['DART_SDK'] = pathInSandbox(sdkPath); |
377 if (tokenEndpoint != null) { | 380 if (tokenEndpoint != null) { |
378 options.environment['_PUB_TEST_TOKEN_ENDPOINT'] = | 381 options.environment['_PUB_TEST_TOKEN_ENDPOINT'] = |
379 tokenEndpoint.toString(); | 382 tokenEndpoint.toString(); |
380 } | 383 } |
381 return options; | 384 return options; |
382 }); | 385 }); |
383 | 386 |
384 return new ScheduledProcess.start(dartBin, dartArgs, options: optionsFuture, | 387 return new PubProcess.start(dartBin, dartArgs, options: optionsFuture, |
385 description: args.isEmpty ? 'pub' : 'pub ${args.first}'); | 388 description: args.isEmpty ? 'pub' : 'pub ${args.first}'); |
386 } | 389 } |
387 | 390 |
| 391 /// A subclass of [ScheduledProcess] that parses pub's verbose logging output |
| 392 /// and makes [nextLine], [nextErrLine], [remainingStdout], and |
| 393 /// [remainingStderr] work as though pub weren't running in verbose mode. |
| 394 class PubProcess extends ScheduledProcess { |
| 395 Stream<Pair<log.Level, String>> _log; |
| 396 Stream<String> _stdout; |
| 397 Stream<String> _stderr; |
| 398 |
| 399 PubProcess.start(executable, arguments, |
| 400 {options, String description, Encoding encoding: Encoding.UTF_8}) |
| 401 : super.start(executable, arguments, |
| 402 options: options, |
| 403 description: description, |
| 404 encoding: encoding); |
| 405 |
| 406 Stream<Pair<log.Level, String>> _logStream() { |
| 407 if (_log == null) { |
| 408 _log = mergeStreams( |
| 409 _outputToLog(super.stdoutStream(), log.Level.MESSAGE), |
| 410 _outputToLog(super.stderrStream(), log.Level.ERROR)); |
| 411 } |
| 412 |
| 413 var pair = tee(_log); |
| 414 _log = pair.first; |
| 415 return pair.last; |
| 416 } |
| 417 |
| 418 final _logLineRegExp = new RegExp(r"^([A-Z ]{4})[:|] (.*)$"); |
| 419 final _logLevels = [ |
| 420 log.Level.ERROR, log.Level.WARNING, log.Level.MESSAGE, log.Level.IO, |
| 421 log.Level.SOLVER, log.Level.FINE |
| 422 ].fold(<String, log.Level>{}, (levels, level) { |
| 423 levels[level.name] = level; |
| 424 return levels; |
| 425 }); |
| 426 |
| 427 Stream<Pair<log.Level, String>> _outputToLog(Stream<String> stream, |
| 428 log.Level defaultLevel) { |
| 429 var lastLevel; |
| 430 return stream.map((line) { |
| 431 var match = _logLineRegExp.firstMatch(line); |
| 432 if (match == null) return new Pair<log.Level, String>(defaultLevel, line); |
| 433 |
| 434 var level = _logLevels[match[1]]; |
| 435 if (level == null) level = lastLevel; |
| 436 lastLevel = level; |
| 437 return new Pair<log.Level, String>(level, match[2]); |
| 438 }); |
| 439 } |
| 440 |
| 441 Stream<String> stdoutStream() { |
| 442 if (_stdout == null) { |
| 443 _stdout = _logStream().expand((entry) { |
| 444 if (entry.first != log.Level.MESSAGE) return []; |
| 445 return [entry.last]; |
| 446 }); |
| 447 } |
| 448 |
| 449 var pair = tee(_stdout); |
| 450 _stdout = pair.first; |
| 451 return pair.last; |
| 452 } |
| 453 |
| 454 Stream<String> stderrStream() { |
| 455 if (_stderr == null) { |
| 456 _stderr = _logStream().expand((entry) { |
| 457 if (entry.first != log.Level.ERROR && entry.first != log.Level.WARNING)
{ |
| 458 return []; |
| 459 } |
| 460 return [entry.last]; |
| 461 }); |
| 462 } |
| 463 |
| 464 var pair = tee(_stderr); |
| 465 _stderr = pair.first; |
| 466 return pair.last; |
| 467 } |
| 468 } |
| 469 |
388 // TODO(nweiz): use the built-in mechanism for accessing this once it exists | 470 // TODO(nweiz): use the built-in mechanism for accessing this once it exists |
389 // (issue 9119). | 471 // (issue 9119). |
390 /// The path to the `packages` directory from which pub loads its dependencies. | 472 /// The path to the `packages` directory from which pub loads its dependencies. |
391 String get _packageRoot { | 473 String get _packageRoot { |
392 return path.absolute(path.join( | 474 return path.absolute(path.join( |
393 path.dirname(new Options().executable), '..', '..', 'packages')); | 475 path.dirname(new Options().executable), '..', '..', 'packages')); |
394 } | 476 } |
395 | 477 |
396 /// Skips the current test if Git is not installed. This validates that the | 478 /// Skips the current test if Git is not installed. This validates that the |
397 /// current test is running on a buildbot in which case we expect git to be | 479 /// current test is running on a buildbot in which case we expect git to be |
(...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
612 bool matches(item, MatchState matchState) { | 694 bool matches(item, MatchState matchState) { |
613 if (item is! Pair) return false; | 695 if (item is! Pair) return false; |
614 return _firstMatcher.matches(item.first, matchState) && | 696 return _firstMatcher.matches(item.first, matchState) && |
615 _lastMatcher.matches(item.last, matchState); | 697 _lastMatcher.matches(item.last, matchState); |
616 } | 698 } |
617 | 699 |
618 Description describe(Description description) { | 700 Description describe(Description description) { |
619 description.addAll("(", ", ", ")", [_firstMatcher, _lastMatcher]); | 701 description.addAll("(", ", ", ")", [_firstMatcher, _lastMatcher]); |
620 } | 702 } |
621 } | 703 } |
OLD | NEW |