| 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 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 92 /// A future that will complete to the port used for the current server. | 92 /// A future that will complete to the port used for the current server. |
| 93 Future<int> get port => _portCompleter.future; | 93 Future<int> get port => _portCompleter.future; |
| 94 | 94 |
| 95 /// Creates an HTTP server to serve [contents] as static files. This server will | 95 /// Creates an HTTP server to serve [contents] as static files. This server will |
| 96 /// exist only for the duration of the pub run. | 96 /// exist only for the duration of the pub run. |
| 97 /// | 97 /// |
| 98 /// Subsequent calls to [serve] will replace the previous server. | 98 /// Subsequent calls to [serve] will replace the previous server. |
| 99 void serve([List<Descriptor> contents]) { | 99 void serve([List<Descriptor> contents]) { |
| 100 var baseDir = dir("serve-dir", contents); | 100 var baseDir = dir("serve-dir", contents); |
| 101 | 101 |
| 102 _schedule((_) { | 102 schedule((_) { |
| 103 return _closeServer().then((_) { | 103 return _closeServer().then((_) { |
| 104 _server = new HttpServer(); | 104 _server = new HttpServer(); |
| 105 _server.defaultRequestHandler = (request, response) { | 105 _server.defaultRequestHandler = (request, response) { |
| 106 var path = request.uri.replaceFirst("/", "").split("/"); | 106 var path = request.uri.replaceFirst("/", "").split("/"); |
| 107 response.persistentConnection = false; | 107 response.persistentConnection = false; |
| 108 var stream; | 108 var stream; |
| 109 try { | 109 try { |
| 110 stream = baseDir.load(path); | 110 stream = baseDir.load(path); |
| 111 } catch (e) { | 111 } catch (e) { |
| 112 response.statusCode = 404; | 112 response.statusCode = 404; |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 170 _servedPackages = <String, Map<String, String>>{}; | 170 _servedPackages = <String, Map<String, String>>{}; |
| 171 _servedPackageDir = dir('packages', []); | 171 _servedPackageDir = dir('packages', []); |
| 172 serve([_servedPackageDir]); | 172 serve([_servedPackageDir]); |
| 173 | 173 |
| 174 _scheduleCleanup((_) { | 174 _scheduleCleanup((_) { |
| 175 _servedPackages = null; | 175 _servedPackages = null; |
| 176 _servedPackageDir = null; | 176 _servedPackageDir = null; |
| 177 }); | 177 }); |
| 178 } | 178 } |
| 179 | 179 |
| 180 _schedule((_) { | 180 schedule((_) { |
| 181 return _awaitObject(pubspecs).then((resolvedPubspecs) { | 181 return _awaitObject(pubspecs).then((resolvedPubspecs) { |
| 182 for (var spec in resolvedPubspecs) { | 182 for (var spec in resolvedPubspecs) { |
| 183 var name = spec['name']; | 183 var name = spec['name']; |
| 184 var version = spec['version']; | 184 var version = spec['version']; |
| 185 var versions = _servedPackages.putIfAbsent( | 185 var versions = _servedPackages.putIfAbsent( |
| 186 name, () => <String, String>{}); | 186 name, () => <String, String>{}); |
| 187 versions[version] = yaml(spec); | 187 versions[version] = yaml(spec); |
| 188 } | 188 } |
| 189 | 189 |
| 190 _servedPackageDir.contents.clear(); | 190 _servedPackageDir.contents.clear(); |
| (...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 356 contents.add(packageCacheDir(name, version)); | 356 contents.add(packageCacheDir(name, version)); |
| 357 } | 357 } |
| 358 }); | 358 }); |
| 359 return dir(cachePath, [ | 359 return dir(cachePath, [ |
| 360 dir('hosted', [ | 360 dir('hosted', [ |
| 361 async(port.then((p) => dir('localhost%58$p', contents))) | 361 async(port.then((p) => dir('localhost%58$p', contents))) |
| 362 ]) | 362 ]) |
| 363 ]); | 363 ]); |
| 364 } | 364 } |
| 365 | 365 |
| 366 /// Describes the directory in the system cache where the package [name] at |
| 367 /// [version] is stored when installed from the mock package server. |
| 368 Future<String> hostedCacheDir(String name, String version) { |
| 369 return port.then((p) { |
| 370 return path.join(cachePath, "hosted", "localhost%58$p", "$name-$version"); |
| 371 }); |
| 372 } |
| 373 |
| 366 /// Describes the file in the system cache that contains the client's OAuth2 | 374 /// Describes the file in the system cache that contains the client's OAuth2 |
| 367 /// credentials. The URL "/token" on [server] will be used as the token | 375 /// credentials. The URL "/token" on [server] will be used as the token |
| 368 /// endpoint for refreshing the access token. | 376 /// endpoint for refreshing the access token. |
| 369 Descriptor credentialsFile( | 377 Descriptor credentialsFile( |
| 370 ScheduledServer server, | 378 ScheduledServer server, |
| 371 String accessToken, | 379 String accessToken, |
| 372 {String refreshToken, | 380 {String refreshToken, |
| 373 DateTime expiration}) { | 381 DateTime expiration}) { |
| 374 return async(server.url.then((url) { | 382 return async(server.url.then((url) { |
| 375 return dir(cachePath, [ | 383 return dir(cachePath, [ |
| (...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 529 var dir = new Options().script; | 537 var dir = new Options().script; |
| 530 while (basename(dir) != 'pub') dir = dirname(dir); | 538 while (basename(dir) != 'pub') dir = dirname(dir); |
| 531 | 539 |
| 532 return getFullPath(dir); | 540 return getFullPath(dir); |
| 533 } | 541 } |
| 534 | 542 |
| 535 /// Schedules a call to the Pub command-line utility. Runs Pub with [args] and | 543 /// Schedules a call to the Pub command-line utility. Runs Pub with [args] and |
| 536 /// validates that its results match [output], [error], and [exitCode]. | 544 /// validates that its results match [output], [error], and [exitCode]. |
| 537 void schedulePub({List args, Pattern output, Pattern error, | 545 void schedulePub({List args, Pattern output, Pattern error, |
| 538 Future<Uri> tokenEndpoint, int exitCode: 0}) { | 546 Future<Uri> tokenEndpoint, int exitCode: 0}) { |
| 539 _schedule((sandboxDir) { | 547 schedule((sandboxDir) { |
| 540 return _doPub(runProcess, sandboxDir, args, tokenEndpoint).then((result) { | 548 return _doPub(runProcess, sandboxDir, args, tokenEndpoint).then((result) { |
| 541 var failures = []; | 549 var failures = []; |
| 542 | 550 |
| 543 _validateOutput(failures, 'stdout', output, result.stdout); | 551 _validateOutput(failures, 'stdout', output, result.stdout); |
| 544 _validateOutput(failures, 'stderr', error, result.stderr); | 552 _validateOutput(failures, 'stderr', error, result.stderr); |
| 545 | 553 |
| 546 if (result.exitCode != exitCode) { | 554 if (result.exitCode != exitCode) { |
| 547 failures.add( | 555 failures.add( |
| 548 'Pub returned exit code ${result.exitCode}, expected $exitCode.'); | 556 'Pub returned exit code ${result.exitCode}, expected $exitCode.'); |
| 549 } | 557 } |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 647 }); | 655 }); |
| 648 } | 656 } |
| 649 | 657 |
| 650 /// Skips the current test if Git is not installed. This validates that the | 658 /// Skips the current test if Git is not installed. This validates that the |
| 651 /// current test is running on a buildbot in which case we expect git to be | 659 /// current test is running on a buildbot in which case we expect git to be |
| 652 /// installed. If we are not running on the buildbot, we will instead see if | 660 /// installed. If we are not running on the buildbot, we will instead see if |
| 653 /// git is installed and skip the test if not. This way, users don't need to | 661 /// git is installed and skip the test if not. This way, users don't need to |
| 654 /// have git installed to run the tests locally (unless they actually care | 662 /// have git installed to run the tests locally (unless they actually care |
| 655 /// about the pub git tests). | 663 /// about the pub git tests). |
| 656 void ensureGit() { | 664 void ensureGit() { |
| 657 _schedule((_) { | 665 schedule((_) { |
| 658 return gitlib.isInstalled.then((installed) { | 666 return gitlib.isInstalled.then((installed) { |
| 659 if (!installed && | 667 if (!installed && |
| 660 !Platform.environment.containsKey('BUILDBOT_BUILDERNAME')) { | 668 !Platform.environment.containsKey('BUILDBOT_BUILDERNAME')) { |
| 661 _abortScheduled = true; | 669 _abortScheduled = true; |
| 662 } | 670 } |
| 663 return null; | 671 return null; |
| 664 }); | 672 }); |
| 665 }); | 673 }); |
| 666 } | 674 } |
| 667 | 675 |
| (...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 798 Future delete(String dir); | 806 Future delete(String dir); |
| 799 | 807 |
| 800 /// Loads the file at [path] from within this descriptor. If [path] is empty, | 808 /// Loads the file at [path] from within this descriptor. If [path] is empty, |
| 801 /// loads the contents of the descriptor itself. | 809 /// loads the contents of the descriptor itself. |
| 802 ByteStream load(List<String> path); | 810 ByteStream load(List<String> path); |
| 803 | 811 |
| 804 /// Schedules the directory to be created before Pub is run with | 812 /// Schedules the directory to be created before Pub is run with |
| 805 /// [schedulePub]. The directory will be created relative to the sandbox | 813 /// [schedulePub]. The directory will be created relative to the sandbox |
| 806 /// directory. | 814 /// directory. |
| 807 // TODO(nweiz): Use implicit closurization once issue 2984 is fixed. | 815 // TODO(nweiz): Use implicit closurization once issue 2984 is fixed. |
| 808 void scheduleCreate() => _schedule((dir) => this.create(dir)); | 816 void scheduleCreate() => schedule((dir) => this.create(dir)); |
| 809 | 817 |
| 810 /// Schedules the file or directory to be deleted recursively. | 818 /// Schedules the file or directory to be deleted recursively. |
| 811 void scheduleDelete() => _schedule((dir) => this.delete(dir)); | 819 void scheduleDelete() => schedule((dir) => this.delete(dir)); |
| 812 | 820 |
| 813 /// Schedules the directory to be validated after Pub is run with | 821 /// Schedules the directory to be validated after Pub is run with |
| 814 /// [schedulePub]. The directory will be validated relative to the sandbox | 822 /// [schedulePub]. The directory will be validated relative to the sandbox |
| 815 /// directory. | 823 /// directory. |
| 816 void scheduleValidate() => _schedule((parentDir) => validate(parentDir.path)); | 824 void scheduleValidate() => schedule((parentDir) => validate(parentDir.path)); |
| 817 | 825 |
| 818 /// Asserts that the name of the descriptor is a [String] and returns it. | 826 /// Asserts that the name of the descriptor is a [String] and returns it. |
| 819 String get _stringName { | 827 String get _stringName { |
| 820 if (name is String) return name; | 828 if (name is String) return name; |
| 821 throw 'Pattern $name must be a string.'; | 829 throw 'Pattern $name must be a string.'; |
| 822 } | 830 } |
| 823 | 831 |
| 824 /// Validates that at least one file in [dir] matching [name] is valid | 832 /// Validates that at least one file in [dir] matching [name] is valid |
| 825 /// according to [validate]. [validate] should throw or complete to an | 833 /// according to [validate]. [validate] should throw or complete to an |
| 826 /// exception if the input path is invalid. | 834 /// exception if the input path is invalid. |
| (...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1032 | 1040 |
| 1033 /// Commits any changes to the Git repository. | 1041 /// Commits any changes to the Git repository. |
| 1034 Future commit(parentDir) { | 1042 Future commit(parentDir) { |
| 1035 return _runGitCommands(parentDir, [ | 1043 return _runGitCommands(parentDir, [ |
| 1036 ['add', '.'], | 1044 ['add', '.'], |
| 1037 ['commit', '-m', 'update'] | 1045 ['commit', '-m', 'update'] |
| 1038 ]); | 1046 ]); |
| 1039 } | 1047 } |
| 1040 | 1048 |
| 1041 /// Schedules changes to be committed to the Git repository. | 1049 /// Schedules changes to be committed to the Git repository. |
| 1042 void scheduleCommit() => _schedule((dir) => this.commit(dir)); | 1050 void scheduleCommit() => schedule((dir) => this.commit(dir)); |
| 1043 | 1051 |
| 1044 /// Return a Future that completes to the commit in the git repository | 1052 /// Return a Future that completes to the commit in the git repository |
| 1045 /// referred to by [ref] at the current point in the scheduled test run. | 1053 /// referred to by [ref] at the current point in the scheduled test run. |
| 1046 Future<String> revParse(String ref) { | 1054 Future<String> revParse(String ref) { |
| 1047 return _scheduleValue((parentDir) { | 1055 return _scheduleValue((parentDir) { |
| 1048 return super.create(parentDir).then((rootDir) { | 1056 return super.create(parentDir).then((rootDir) { |
| 1049 return _runGit(['rev-parse', ref], rootDir); | 1057 return _runGit(['rev-parse', ref], rootDir); |
| 1050 }).then((output) => output[0]); | 1058 }).then((output) => output[0]); |
| 1051 }); | 1059 }); |
| 1052 } | 1060 } |
| 1053 | 1061 |
| 1054 /// Schedule a Git command to run in this repository. | 1062 /// Schedule a Git command to run in this repository. |
| 1055 void scheduleGit(List<String> args) { | 1063 void scheduleGit(List<String> args) { |
| 1056 _schedule((parentDir) { | 1064 schedule((parentDir) { |
| 1057 var gitDir = new Directory(join(parentDir, name)); | 1065 var gitDir = new Directory(join(parentDir, name)); |
| 1058 return _runGit(args, gitDir); | 1066 return _runGit(args, gitDir); |
| 1059 }); | 1067 }); |
| 1060 } | 1068 } |
| 1061 | 1069 |
| 1062 Future _runGitCommands(parentDir, List<List<String>> commands) { | 1070 Future _runGitCommands(parentDir, List<List<String>> commands) { |
| 1063 var workingDir; | 1071 var workingDir; |
| 1064 | 1072 |
| 1065 Future runGitStep(_) { | 1073 Future runGitStep(_) { |
| 1066 if (commands.isEmpty) return new Future.immediate(workingDir); | 1074 if (commands.isEmpty) return new Future.immediate(workingDir); |
| (...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1295 var stderrPair = streamWithSubscription(stderrTee.last); | 1303 var stderrPair = streamWithSubscription(stderrTee.last); |
| 1296 _stderr = stderrPair.first; | 1304 _stderr = stderrPair.first; |
| 1297 _stderrSubscription = stderrPair.last; | 1305 _stderrSubscription = stderrPair.last; |
| 1298 | 1306 |
| 1299 return new Pair(stdoutTee.first, stderrTee.first); | 1307 return new Pair(stdoutTee.first, stderrTee.first); |
| 1300 }); | 1308 }); |
| 1301 | 1309 |
| 1302 _stdoutLines = pairFuture.then((pair) => pair.first.toList()); | 1310 _stdoutLines = pairFuture.then((pair) => pair.first.toList()); |
| 1303 _stderrLines = pairFuture.then((pair) => pair.last.toList()); | 1311 _stderrLines = pairFuture.then((pair) => pair.last.toList()); |
| 1304 | 1312 |
| 1305 _schedule((_) { | 1313 schedule((_) { |
| 1306 if (!_endScheduled) { | 1314 if (!_endScheduled) { |
| 1307 throw new StateError("Scheduled process $name must have shouldExit() " | 1315 throw new StateError("Scheduled process $name must have shouldExit() " |
| 1308 "or kill() called before the test is run."); | 1316 "or kill() called before the test is run."); |
| 1309 } | 1317 } |
| 1310 | 1318 |
| 1311 process.then((p) => p.exitCode).then((exitCode) { | 1319 process.then((p) => p.exitCode).then((exitCode) { |
| 1312 if (_endExpected) { | 1320 if (_endExpected) { |
| 1313 _exitCode = exitCode; | 1321 _exitCode = exitCode; |
| 1314 _exitCodeCompleter.complete(exitCode); | 1322 _exitCodeCompleter.complete(exitCode); |
| 1315 return; | 1323 return; |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1397 return _scheduleValue((_) { | 1405 return _scheduleValue((_) { |
| 1398 return timeout(_stderrFuture.then((stream) => stream.toList()) | 1406 return timeout(_stderrFuture.then((stream) => stream.toList()) |
| 1399 .then((lines) => lines.join("\n")), | 1407 .then((lines) => lines.join("\n")), |
| 1400 _SCHEDULE_TIMEOUT, | 1408 _SCHEDULE_TIMEOUT, |
| 1401 "waiting for the last stderr line from process $name"); | 1409 "waiting for the last stderr line from process $name"); |
| 1402 }); | 1410 }); |
| 1403 } | 1411 } |
| 1404 | 1412 |
| 1405 /// Writes [line] to the process as stdin. | 1413 /// Writes [line] to the process as stdin. |
| 1406 void writeLine(String line) { | 1414 void writeLine(String line) { |
| 1407 _schedule((_) => _processFuture.then( | 1415 schedule((_) => _processFuture.then( |
| 1408 (p) => p.stdin.add('$line\n'.charCodes))); | 1416 (p) => p.stdin.add('$line\n'.charCodes))); |
| 1409 } | 1417 } |
| 1410 | 1418 |
| 1411 /// Kills the process, and waits until it's dead. | 1419 /// Kills the process, and waits until it's dead. |
| 1412 void kill() { | 1420 void kill() { |
| 1413 _endScheduled = true; | 1421 _endScheduled = true; |
| 1414 _schedule((_) { | 1422 schedule((_) { |
| 1415 _endExpected = true; | 1423 _endExpected = true; |
| 1416 _process.kill(); | 1424 _process.kill(); |
| 1417 timeout(_exitCodeFuture, _SCHEDULE_TIMEOUT, | 1425 timeout(_exitCodeFuture, _SCHEDULE_TIMEOUT, |
| 1418 "waiting for process $name to die"); | 1426 "waiting for process $name to die"); |
| 1419 }); | 1427 }); |
| 1420 } | 1428 } |
| 1421 | 1429 |
| 1422 /// Waits for the process to exit, and verifies that the exit code matches | 1430 /// Waits for the process to exit, and verifies that the exit code matches |
| 1423 /// [expectedExitCode] (if given). | 1431 /// [expectedExitCode] (if given). |
| 1424 void shouldExit([int expectedExitCode]) { | 1432 void shouldExit([int expectedExitCode]) { |
| 1425 _endScheduled = true; | 1433 _endScheduled = true; |
| 1426 _schedule((_) { | 1434 schedule((_) { |
| 1427 _endExpected = true; | 1435 _endExpected = true; |
| 1428 return timeout(_exitCodeFuture, _SCHEDULE_TIMEOUT, | 1436 return timeout(_exitCodeFuture, _SCHEDULE_TIMEOUT, |
| 1429 "waiting for process $name to exit").then((exitCode) { | 1437 "waiting for process $name to exit").then((exitCode) { |
| 1430 if (expectedExitCode != null) { | 1438 if (expectedExitCode != null) { |
| 1431 expect(exitCode, equals(expectedExitCode)); | 1439 expect(exitCode, equals(expectedExitCode)); |
| 1432 } | 1440 } |
| 1433 }); | 1441 }); |
| 1434 }); | 1442 }); |
| 1435 } | 1443 } |
| 1436 | 1444 |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1554 return Future.wait(pairs).then((resolvedPairs) { | 1562 return Future.wait(pairs).then((resolvedPairs) { |
| 1555 var map = {}; | 1563 var map = {}; |
| 1556 for (var pair in resolvedPairs) { | 1564 for (var pair in resolvedPairs) { |
| 1557 map[pair.first] = pair.last; | 1565 map[pair.first] = pair.last; |
| 1558 } | 1566 } |
| 1559 return map; | 1567 return map; |
| 1560 }); | 1568 }); |
| 1561 } | 1569 } |
| 1562 | 1570 |
| 1563 /// Schedules a callback to be called as part of the test case. | 1571 /// Schedules a callback to be called as part of the test case. |
| 1564 void _schedule(_ScheduledEvent event) { | 1572 void schedule(_ScheduledEvent event) { |
| 1565 if (_scheduled == null) _scheduled = []; | 1573 if (_scheduled == null) _scheduled = []; |
| 1566 _scheduled.add(event); | 1574 _scheduled.add(event); |
| 1567 } | 1575 } |
| 1568 | 1576 |
| 1569 /// Like [_schedule], but pipes the return value of [event] to a returned | 1577 /// Like [_schedule], but pipes the return value of [event] to a returned |
| 1570 /// [Future]. | 1578 /// [Future]. |
| 1571 Future _scheduleValue(_ScheduledEvent event) { | 1579 Future _scheduleValue(_ScheduledEvent event) { |
| 1572 var completer = new Completer(); | 1580 var completer = new Completer(); |
| 1573 _schedule((parentDir) { | 1581 schedule((parentDir) { |
| 1574 chainToCompleter(event(parentDir), completer); | 1582 chainToCompleter(event(parentDir), completer); |
| 1575 return completer.future; | 1583 return completer.future; |
| 1576 }); | 1584 }); |
| 1577 return completer.future; | 1585 return completer.future; |
| 1578 } | 1586 } |
| 1579 | 1587 |
| 1580 /// Schedules a callback to be called after the test case has completed, even | 1588 /// Schedules a callback to be called after the test case has completed, even |
| 1581 /// if it failed. | 1589 /// if it failed. |
| 1582 void _scheduleCleanup(_ScheduledEvent event) { | 1590 void _scheduleCleanup(_ScheduledEvent event) { |
| 1583 if (_scheduledCleanup == null) _scheduledCleanup = []; | 1591 if (_scheduledCleanup == null) _scheduledCleanup = []; |
| 1584 _scheduledCleanup.add(event); | 1592 _scheduledCleanup.add(event); |
| 1585 } | 1593 } |
| 1586 | 1594 |
| 1587 /// Schedules a callback to be called after the test case has completed, but | 1595 /// Schedules a callback to be called after the test case has completed, but |
| 1588 /// only if it failed. | 1596 /// only if it failed. |
| 1589 void _scheduleOnException(_ScheduledEvent event) { | 1597 void _scheduleOnException(_ScheduledEvent event) { |
| 1590 if (_scheduledOnException == null) _scheduledOnException = []; | 1598 if (_scheduledOnException == null) _scheduledOnException = []; |
| 1591 _scheduledOnException.add(event); | 1599 _scheduledOnException.add(event); |
| 1592 } | 1600 } |
| 1593 | 1601 |
| 1594 /// Like [expect], but for [Future]s that complete as part of the scheduled | 1602 /// Like [expect], but for [Future]s that complete as part of the scheduled |
| 1595 /// test. This is necessary to ensure that the exception thrown by the | 1603 /// test. This is necessary to ensure that the exception thrown by the |
| 1596 /// expectation failing is handled by the scheduler. | 1604 /// expectation failing is handled by the scheduler. |
| 1597 /// | 1605 /// |
| 1598 /// Note that [matcher] matches against the completed value of [actual], so | 1606 /// Note that [matcher] matches against the completed value of [actual], so |
| 1599 /// calling [completion] is unnecessary. | 1607 /// calling [completion] is unnecessary. |
| 1600 void expectLater(Future actual, matcher, {String reason, | 1608 void expectLater(Future actual, matcher, {String reason, |
| 1601 FailureHandler failureHandler, bool verbose: false}) { | 1609 FailureHandler failureHandler, bool verbose: false}) { |
| 1602 _schedule((_) { | 1610 schedule((_) { |
| 1603 return actual.then((value) { | 1611 return actual.then((value) { |
| 1604 expect(value, matcher, reason: reason, failureHandler: failureHandler, | 1612 expect(value, matcher, reason: reason, failureHandler: failureHandler, |
| 1605 verbose: false); | 1613 verbose: false); |
| 1606 }); | 1614 }); |
| 1607 }); | 1615 }); |
| 1608 } | 1616 } |
| OLD | NEW |