| 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 /// 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 13 matching lines...) Expand all Loading... |
| 24 import '../../lib/file_system.dart' as fs; | 24 import '../../lib/file_system.dart' as fs; |
| 25 import '../../pub/entrypoint.dart'; | 25 import '../../pub/entrypoint.dart'; |
| 26 // TODO(rnystrom): Using "gitlib" as the prefix here is ugly, but "git" collides | 26 // TODO(rnystrom): Using "gitlib" as the prefix here is ugly, but "git" collides |
| 27 // with the git descriptor method. Maybe we should try to clean up the top level | 27 // with the git descriptor method. Maybe we should try to clean up the top level |
| 28 // scope a bit? | 28 // scope a bit? |
| 29 import '../../pub/git.dart' as gitlib; | 29 import '../../pub/git.dart' as gitlib; |
| 30 import '../../pub/git_source.dart'; | 30 import '../../pub/git_source.dart'; |
| 31 import '../../pub/hosted_source.dart'; | 31 import '../../pub/hosted_source.dart'; |
| 32 import '../../pub/http.dart'; | 32 import '../../pub/http.dart'; |
| 33 import '../../pub/io.dart'; | 33 import '../../pub/io.dart'; |
| 34 import '../../pub/path_source.dart'; |
| 34 import '../../pub/sdk_source.dart'; | 35 import '../../pub/sdk_source.dart'; |
| 35 import '../../pub/system_cache.dart'; | 36 import '../../pub/system_cache.dart'; |
| 36 import '../../pub/utils.dart'; | 37 import '../../pub/utils.dart'; |
| 37 import '../../pub/validator.dart'; | 38 import '../../pub/validator.dart'; |
| 38 import 'command_line_config.dart'; | 39 import 'command_line_config.dart'; |
| 39 | 40 |
| 40 /// This should be called at the top of a test file to set up an appropriate | 41 /// This should be called at the top of a test file to set up an appropriate |
| 41 /// test configuration for the machine running the tests. | 42 /// test configuration for the machine running the tests. |
| 42 initConfig() { | 43 initConfig() { |
| 43 // If we aren't running on the bots, use the human-friendly config. | 44 // If we aren't running on the bots, use the human-friendly config. |
| (...skipping 353 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 397 var keys = dependency.keys.where((key) => key != "version"); | 398 var keys = dependency.keys.where((key) => key != "version"); |
| 398 var sourceName = only(keys); | 399 var sourceName = only(keys); |
| 399 var source; | 400 var source; |
| 400 switch (sourceName) { | 401 switch (sourceName) { |
| 401 case "git": | 402 case "git": |
| 402 source = new GitSource(); | 403 source = new GitSource(); |
| 403 break; | 404 break; |
| 404 case "hosted": | 405 case "hosted": |
| 405 source = new HostedSource(); | 406 source = new HostedSource(); |
| 406 break; | 407 break; |
| 408 case "path": |
| 409 source = new PathSource(); |
| 410 break; |
| 407 case "sdk": | 411 case "sdk": |
| 408 source = new SdkSource(); | 412 source = new SdkSource(); |
| 409 break; | 413 break; |
| 410 default: | 414 default: |
| 411 throw 'Unknown source "$sourceName"'; | 415 throw 'Unknown source "$sourceName"'; |
| 412 } | 416 } |
| 413 | 417 |
| 414 result[_packageName(sourceName, dependency[sourceName])] = dependency; | 418 result[_packageName(sourceName, dependency[sourceName])] = dependency; |
| 415 } | 419 } |
| 416 return result; | 420 return result; |
| 417 }); | 421 }); |
| 418 } | 422 } |
| 419 | 423 |
| 420 /// Return the name for the package described by [description] and from | 424 /// Return the name for the package described by [description] and from |
| 421 /// [sourceName]. | 425 /// [sourceName]. |
| 422 String _packageName(String sourceName, description) { | 426 String _packageName(String sourceName, description) { |
| 423 switch (sourceName) { | 427 switch (sourceName) { |
| 424 case "git": | 428 case "git": |
| 425 var url = description is String ? description : description['url']; | 429 var url = description is String ? description : description['url']; |
| 426 return basename(url.replaceFirst(new RegExp(r"(\.git)?/?$"), "")); | 430 return basename(url.replaceFirst(new RegExp(r"(\.git)?/?$"), "")); |
| 427 case "hosted": | 431 case "hosted": |
| 428 if (description is String) return description; | 432 if (description is String) return description; |
| 429 return description['name']; | 433 return description['name']; |
| 434 case "path": |
| 435 return basename(description); |
| 430 case "sdk": | 436 case "sdk": |
| 431 return description; | 437 return description; |
| 432 default: | 438 default: |
| 433 return description; | 439 return description; |
| 434 } | 440 } |
| 435 } | 441 } |
| 436 | 442 |
| 443 /// The full path to the created sandbox directory for an integration test. |
| 444 String get sandboxDir => _sandboxDir.path; |
| 445 Directory _sandboxDir; |
| 446 |
| 437 /// The path of the package cache directory used for tests. Relative to the | 447 /// The path of the package cache directory used for tests. Relative to the |
| 438 /// sandbox directory. | 448 /// sandbox directory. |
| 439 final String cachePath = "cache"; | 449 final String cachePath = "cache"; |
| 440 | 450 |
| 441 /// The path of the mock SDK directory used for tests. Relative to the sandbox | 451 /// The path of the mock SDK directory used for tests. Relative to the sandbox |
| 442 /// directory. | 452 /// directory. |
| 443 final String sdkPath = "sdk"; | 453 final String sdkPath = "sdk"; |
| 444 | 454 |
| 445 /// The path of the mock app directory used for tests. Relative to the sandbox | 455 /// The path of the mock app directory used for tests. Relative to the sandbox |
| 446 /// directory. | 456 /// directory. |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 481 void solo_integration(String description, void body()) => | 491 void solo_integration(String description, void body()) => |
| 482 _integration(description, body, solo_test); | 492 _integration(description, body, solo_test); |
| 483 | 493 |
| 484 void _integration(String description, void body(), [Function testFn]) { | 494 void _integration(String description, void body(), [Function testFn]) { |
| 485 testFn(description, () { | 495 testFn(description, () { |
| 486 // Ensure the SDK version is always available. | 496 // Ensure the SDK version is always available. |
| 487 dir(sdkPath, [ | 497 dir(sdkPath, [ |
| 488 file('version', '0.1.2.3') | 498 file('version', '0.1.2.3') |
| 489 ]).scheduleCreate(); | 499 ]).scheduleCreate(); |
| 490 | 500 |
| 501 _sandboxDir = createTempDir(); |
| 502 |
| 491 // Schedule the test. | 503 // Schedule the test. |
| 492 body(); | 504 body(); |
| 493 | 505 |
| 494 // Run all of the scheduled tasks. If an error occurs, it will propagate | 506 // Run all of the scheduled tasks. If an error occurs, it will propagate |
| 495 // through the futures back up to here where we can hand it off to unittest. | 507 // through the futures back up to here where we can hand it off to unittest. |
| 496 var asyncDone = expectAsync0(() {}); | 508 var asyncDone = expectAsync0(() {}); |
| 497 var sandboxDir = createTempDir(); | 509 return timeout(_runScheduled(_scheduled), |
| 498 return timeout(_runScheduled(sandboxDir, _scheduled), | |
| 499 _TIMEOUT, 'waiting for a test to complete').catchError((e) { | 510 _TIMEOUT, 'waiting for a test to complete').catchError((e) { |
| 500 return _runScheduled(sandboxDir, _scheduledOnException).then((_) { | 511 return _runScheduled(_scheduledOnException).then((_) { |
| 501 // Rethrow the original error so it keeps propagating. | 512 // Rethrow the original error so it keeps propagating. |
| 502 throw e; | 513 throw e; |
| 503 }); | 514 }); |
| 504 }).whenComplete(() { | 515 }).whenComplete(() { |
| 505 // Clean up after ourselves. Do this first before reporting back to | 516 // Clean up after ourselves. Do this first before reporting back to |
| 506 // unittest because it will advance to the next test immediately. | 517 // unittest because it will advance to the next test immediately. |
| 507 return _runScheduled(sandboxDir, _scheduledCleanup).then((_) { | 518 return _runScheduled(_scheduledCleanup).then((_) { |
| 508 _scheduled = null; | 519 _scheduled = null; |
| 509 _scheduledCleanup = null; | 520 _scheduledCleanup = null; |
| 510 _scheduledOnException = null; | 521 _scheduledOnException = null; |
| 511 if (sandboxDir != null) return deleteDir(sandboxDir); | 522 if (_sandboxDir != null) { |
| 523 var dir = _sandboxDir; |
| 524 _sandboxDir = null; |
| 525 return deleteDir(dir); |
| 526 } |
| 512 }); | 527 }); |
| 513 }).then((_) { | 528 }).then((_) { |
| 514 // If we got here, the test completed successfully so tell unittest so. | 529 // If we got here, the test completed successfully so tell unittest so. |
| 515 asyncDone(); | 530 asyncDone(); |
| 516 }).catchError((e) { | 531 }).catchError((e) { |
| 517 // If we got here, an error occurred. We will register it with unittest | 532 // If we got here, an error occurred. We will register it with unittest |
| 518 // directly so that the error message isn't wrapped in any matcher stuff. | 533 // directly so that the error message isn't wrapped in any matcher stuff. |
| 519 // We do this call last because it will cause unittest to *synchronously* | 534 // We do this call last because it will cause unittest to *synchronously* |
| 520 // advance to the next test and run it. | 535 // advance to the next test and run it. |
| 521 registerException(e.error, e.stackTrace); | 536 registerException(e.error, e.stackTrace); |
| 522 }); | 537 }); |
| 523 }); | 538 }); |
| 524 } | 539 } |
| 525 | 540 |
| 526 /// Get the path to the root "util/test/pub" directory containing the pub | 541 /// Get the path to the root "util/test/pub" directory containing the pub |
| 527 /// tests. | 542 /// tests. |
| 528 String get testDirectory { | 543 String get testDirectory { |
| 529 var dir = new Options().script; | 544 var dir = new Options().script; |
| 530 while (basename(dir) != 'pub') dir = dirname(dir); | 545 while (basename(dir) != 'pub') dir = dirname(dir); |
| 531 | 546 |
| 532 return getFullPath(dir); | 547 return getFullPath(dir); |
| 533 } | 548 } |
| 534 | 549 |
| 550 /// Schedules renaming (moving) the directory at [from] to [to], both of which |
| 551 /// are assumed to be relative to [sandboxDir]. |
| 552 void scheduleRename(String from, String to) { |
| 553 _schedule((sandboxDir) { |
| 554 return renameDir(join(sandboxDir, from), join(sandboxDir, to)); |
| 555 }); |
| 556 } |
| 557 |
| 535 /// Schedules a call to the Pub command-line utility. Runs Pub with [args] and | 558 /// Schedules a call to the Pub command-line utility. Runs Pub with [args] and |
| 536 /// validates that its results match [output], [error], and [exitCode]. | 559 /// validates that its results match [output], [error], and [exitCode]. |
| 537 void schedulePub({List args, Pattern output, Pattern error, | 560 void schedulePub({List args, Pattern output, Pattern error, |
| 538 Future<Uri> tokenEndpoint, int exitCode: 0}) { | 561 Future<Uri> tokenEndpoint, int exitCode: 0}) { |
| 539 _schedule((sandboxDir) { | 562 _schedule((sandboxDir) { |
| 540 return _doPub(runProcess, sandboxDir, args, tokenEndpoint).then((result) { | 563 return _doPub(runProcess, sandboxDir, args, tokenEndpoint).then((result) { |
| 541 var failures = []; | 564 var failures = []; |
| 542 | 565 |
| 543 _validateOutput(failures, 'stdout', output, result.stdout); | 566 _validateOutput(failures, 'stdout', output, result.stdout); |
| 544 _validateOutput(failures, 'stderr', error, result.stderr); | 567 _validateOutput(failures, 'stderr', error, result.stderr); |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 595 expectLater(pub.nextLine(), equals('Publishing "test_pkg" 1.0.0:')); | 618 expectLater(pub.nextLine(), equals('Publishing "test_pkg" 1.0.0:')); |
| 596 expectLater(pub.nextLine(), equals("|-- LICENSE")); | 619 expectLater(pub.nextLine(), equals("|-- LICENSE")); |
| 597 expectLater(pub.nextLine(), equals("|-- lib")); | 620 expectLater(pub.nextLine(), equals("|-- lib")); |
| 598 expectLater(pub.nextLine(), equals("| '-- test_pkg.dart")); | 621 expectLater(pub.nextLine(), equals("| '-- test_pkg.dart")); |
| 599 expectLater(pub.nextLine(), equals("'-- pubspec.yaml")); | 622 expectLater(pub.nextLine(), equals("'-- pubspec.yaml")); |
| 600 expectLater(pub.nextLine(), equals("")); | 623 expectLater(pub.nextLine(), equals("")); |
| 601 | 624 |
| 602 pub.writeLine("y"); | 625 pub.writeLine("y"); |
| 603 } | 626 } |
| 604 | 627 |
| 605 | |
| 606 /// Calls [fn] with appropriately modified arguments to run a pub process. [fn] | 628 /// Calls [fn] with appropriately modified arguments to run a pub process. [fn] |
| 607 /// should have the same signature as [startProcess], except that the returned | 629 /// should have the same signature as [startProcess], except that the returned |
| 608 /// [Future] may have a type other than [Process]. | 630 /// [Future] may have a type other than [Process]. |
| 609 Future _doPub(Function fn, sandboxDir, List args, Future<Uri> tokenEndpoint) { | 631 Future _doPub(Function fn, sandboxDir, List args, Future<Uri> tokenEndpoint) { |
| 610 String pathInSandbox(path) => join(getFullPath(sandboxDir), path); | 632 String pathInSandbox(path) => join(getFullPath(sandboxDir), path); |
| 611 return defer(() { | 633 return defer(() { |
| 612 ensureDir(pathInSandbox(appPath)); | 634 ensureDir(pathInSandbox(appPath)); |
| 613 return Future.wait([ | 635 return Future.wait([ |
| 614 _awaitObject(args), | 636 _awaitObject(args), |
| 615 tokenEndpoint == null ? new Future.immediate(null) : tokenEndpoint | 637 tokenEndpoint == null ? new Future.immediate(null) : tokenEndpoint |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 670 /// Note that this will only affect HTTP requests made via http.dart in the | 692 /// Note that this will only affect HTTP requests made via http.dart in the |
| 671 /// parent process. | 693 /// parent process. |
| 672 void useMockClient(MockClient client) { | 694 void useMockClient(MockClient client) { |
| 673 var oldInnerClient = httpClient.inner; | 695 var oldInnerClient = httpClient.inner; |
| 674 httpClient.inner = client; | 696 httpClient.inner = client; |
| 675 _scheduleCleanup((_) { | 697 _scheduleCleanup((_) { |
| 676 httpClient.inner = oldInnerClient; | 698 httpClient.inner = oldInnerClient; |
| 677 }); | 699 }); |
| 678 } | 700 } |
| 679 | 701 |
| 680 Future _runScheduled(Directory parentDir, List<_ScheduledEvent> scheduled) { | 702 Future _runScheduled(List<_ScheduledEvent> scheduled) { |
| 681 if (scheduled == null) return new Future.immediate(null); | 703 if (scheduled == null) return new Future.immediate(null); |
| 682 var iterator = scheduled.iterator; | 704 var iterator = scheduled.iterator; |
| 683 | 705 |
| 684 Future runNextEvent(_) { | 706 Future runNextEvent(_) { |
| 685 if (_abortScheduled || !iterator.moveNext()) { | 707 if (_abortScheduled || !iterator.moveNext()) { |
| 686 _abortScheduled = false; | 708 _abortScheduled = false; |
| 687 scheduled.clear(); | 709 scheduled.clear(); |
| 688 return new Future.immediate(null); | 710 return new Future.immediate(null); |
| 689 } | 711 } |
| 690 | 712 |
| 691 var future = iterator.current(parentDir); | 713 var future = iterator.current(_sandboxDir); |
| 692 if (future != null) { | 714 if (future != null) { |
| 693 return future.then(runNextEvent); | 715 return future.then(runNextEvent); |
| 694 } else { | 716 } else { |
| 695 return runNextEvent(null); | 717 return runNextEvent(null); |
| 696 } | 718 } |
| 697 } | 719 } |
| 698 | 720 |
| 699 return runNextEvent(null); | 721 return runNextEvent(null); |
| 700 } | 722 } |
| 701 | 723 |
| (...skipping 458 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1160 throw "Can't load ${Strings.join(path, '/')} from within $name: $name " | 1182 throw "Can't load ${Strings.join(path, '/')} from within $name: $name " |
| 1161 "doesn't exist."; | 1183 "doesn't exist."; |
| 1162 } | 1184 } |
| 1163 } | 1185 } |
| 1164 } | 1186 } |
| 1165 | 1187 |
| 1166 /// A function that creates a [Validator] subclass. | 1188 /// A function that creates a [Validator] subclass. |
| 1167 typedef Validator ValidatorCreator(Entrypoint entrypoint); | 1189 typedef Validator ValidatorCreator(Entrypoint entrypoint); |
| 1168 | 1190 |
| 1169 /// Schedules a single [Validator] to run on the [appPath]. Returns a scheduled | 1191 /// Schedules a single [Validator] to run on the [appPath]. Returns a scheduled |
| 1170 /// Future that contains the erros and warnings produced by that validator. | 1192 /// Future that contains the errors and warnings produced by that validator. |
| 1171 Future<Pair<List<String>, List<String>>> schedulePackageValidation( | 1193 Future<Pair<List<String>, List<String>>> schedulePackageValidation( |
| 1172 ValidatorCreator fn) { | 1194 ValidatorCreator fn) { |
| 1173 return _scheduleValue((sandboxDir) { | 1195 return _scheduleValue((sandboxDir) { |
| 1174 var cache = new SystemCache.withSources(join(sandboxDir, cachePath)); | 1196 var cache = new SystemCache.withSources(join(sandboxDir, cachePath)); |
| 1175 | 1197 |
| 1176 return defer(() { | 1198 return defer(() { |
| 1177 var validator = fn(new Entrypoint(join(sandboxDir, appPath), cache)); | 1199 var validator = fn(new Entrypoint(join(sandboxDir, appPath), cache)); |
| 1178 return validator.validate().then((_) { | 1200 return validator.validate().then((_) { |
| 1179 return new Pair(validator.errors, validator.warnings); | 1201 return new Pair(validator.errors, validator.warnings); |
| 1180 }); | 1202 }); |
| (...skipping 418 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1599 /// calling [completion] is unnecessary. | 1621 /// calling [completion] is unnecessary. |
| 1600 void expectLater(Future actual, matcher, {String reason, | 1622 void expectLater(Future actual, matcher, {String reason, |
| 1601 FailureHandler failureHandler, bool verbose: false}) { | 1623 FailureHandler failureHandler, bool verbose: false}) { |
| 1602 _schedule((_) { | 1624 _schedule((_) { |
| 1603 return actual.then((value) { | 1625 return actual.then((value) { |
| 1604 expect(value, matcher, reason: reason, failureHandler: failureHandler, | 1626 expect(value, matcher, reason: reason, failureHandler: failureHandler, |
| 1605 verbose: false); | 1627 verbose: false); |
| 1606 }); | 1628 }); |
| 1607 }); | 1629 }); |
| 1608 } | 1630 } |
| OLD | NEW |