| Index: test/io.dart
|
| diff --git a/test/io.dart b/test/io.dart
|
| index 7bb87336f7be32c8280a03b7e97e8a738ad9f195..93f5c5834990aeeb301b16c50aeabb61b04e32c4 100644
|
| --- a/test/io.dart
|
| +++ b/test/io.dart
|
| @@ -8,8 +8,11 @@ import 'dart:async';
|
| import 'dart:io';
|
|
|
| import 'package:path/path.dart' as p;
|
| +import 'package:scheduled_test/descriptor.dart' as d;
|
| +import 'package:scheduled_test/scheduled_process.dart';
|
| +import 'package:scheduled_test/scheduled_stream.dart';
|
| +import 'package:scheduled_test/scheduled_test.dart';
|
| import 'package:test/src/util/io.dart';
|
| -import 'package:test/src/utils.dart';
|
|
|
| /// The path to the root directory of the `test` package.
|
| final String packageDir = p.dirname(p.dirname(libraryPath(#test.test.io)));
|
| @@ -28,25 +31,93 @@ final String noSuchFileMessage = Platform.isWindows
|
| final _servingRegExp =
|
| new RegExp(r'^Serving myapp [a-z]+ on http://localhost:(\d+)$');
|
|
|
| +/// A future that will return the port of a pub serve instance run via
|
| +/// [runPubServe].
|
| +///
|
| +/// This should only be called after [runPubServe].
|
| +Future<int> get pubServePort => _pubServePortCompleter.future;
|
| +Completer<int> _pubServePortCompleter;
|
| +
|
| +/// The path to the sandbox directory.
|
| +///
|
| +/// This is only set in tests for which [useSandbox] is active.
|
| +String get sandbox => _sandbox;
|
| +String _sandbox;
|
| +
|
| +/// Declares a [setUp] function that creates a sandbox diretory and sets it as
|
| +/// the default for scheduled_test's directory descriptors.
|
| +///
|
| +/// This should be called outside of any tests. If [additionalSetup] is passed,
|
| +/// it's run after the sandbox creation has been scheduled.
|
| +void useSandbox([void additionalSetup()]) {
|
| + setUp(() {
|
| + _sandbox = createTempDir();
|
| + d.defaultRoot = _sandbox;
|
| +
|
| + currentSchedule.onComplete.schedule(() {
|
| + try {
|
| + new Directory(_sandbox).deleteSync(recursive: true);
|
| + } on IOException catch (_) {
|
| + // Silently swallow exceptions on Windows. If the test failed, there may
|
| + // still be lingering processes that have files in the sandbox open,
|
| + // which will cause this to fail on Windows.
|
| + if (!Platform.isWindows) rethrow;
|
| + }
|
| + }, 'deleting the sandbox directory');
|
| +
|
| + if (additionalSetup != null) additionalSetup();
|
| + });
|
| +}
|
| +
|
| +/// Expects that the entire stdout stream of [test] equals [expected].
|
| +void expectStdoutEquals(ScheduledProcess test, String expected) =>
|
| + _expectStreamEquals(test.stdoutStream(), expected);
|
| +
|
| +/// Expects that the entire stderr stream of [test] equals [expected].
|
| +void expectStderrEquals(ScheduledProcess test, String expected) =>
|
| + _expectStreamEquals(test.stderrStream(), expected);
|
| +
|
| +/// Expects that the entirety of the line stream [stream] equals [expected].
|
| +void _expectStreamEquals(Stream<String> stream, String expected) {
|
| + expect((() async {
|
| + var lines = await stream.toList();
|
| + expect(lines.join("\n").trim(), equals(expected.trim()));
|
| + })(), completes);
|
| +}
|
| +
|
| +/// Returns a [StreamMatcher] that asserts that the stream emits strings
|
| +/// containing each string in [strings] in order.
|
| +///
|
| +/// This expects each string in [strings] to match a different string in the
|
| +/// stream.
|
| +StreamMatcher containsInOrder(Iterable<String> strings) =>
|
| + inOrder(strings.map((string) => consumeThrough(contains(string))));
|
| +
|
| /// Runs the test executable with the package root set properly.
|
| -ProcessResult runTest(List<String> args, {String workingDirectory,
|
| - Map<String, String> environment}) {
|
| +ScheduledProcess runTest(List args, {bool compact: false,
|
| + int concurrency, Map<String, String> environment}) {
|
| + if (concurrency == null) concurrency = 1;
|
| +
|
| var allArgs = [
|
| p.absolute(p.join(packageDir, 'bin/test.dart')),
|
| - "--package-root=${p.join(packageDir, 'packages')}"
|
| - ]..addAll(args);
|
| + "--package-root=${p.join(packageDir, 'packages')}",
|
| + "--concurrency=$concurrency"
|
| + ];
|
| +
|
| + if (!compact) allArgs.addAll(["-r", "expanded"]);
|
| + allArgs.addAll(args);
|
|
|
| if (environment == null) environment = {};
|
| environment.putIfAbsent("_UNITTEST_USE_COLOR", () => "false");
|
|
|
| - // TODO(nweiz): Use ScheduledProcess once it's compatible.
|
| - return runDart(allArgs, workingDirectory: workingDirectory,
|
| - environment: environment);
|
| + return runDart(allArgs,
|
| + environment: environment,
|
| + description: "dart bin/test.dart");
|
| }
|
|
|
| /// Runs Dart.
|
| -ProcessResult runDart(List<String> args, {String workingDirectory,
|
| - Map<String, String> environment}) {
|
| +ScheduledProcess runDart(List args, {Map<String, String> environment,
|
| + String description}) {
|
| var allArgs = Platform.executableArguments.map((arg) {
|
| // The package root might be relative, so we need to make it absolute if
|
| // we're going to run in a different working directory.
|
| @@ -55,88 +126,44 @@ ProcessResult runDart(List<String> args, {String workingDirectory,
|
| p.absolute(p.fromUri(arg.substring("--package-root=".length)));
|
| }).toList()..addAll(args);
|
|
|
| - // TODO(nweiz): Use ScheduledProcess once it's compatible.
|
| - return new _NormalizedProcessResult(Process.runSync(
|
| + return new ScheduledProcess.start(
|
| p.absolute(Platform.executable), allArgs,
|
| - workingDirectory: workingDirectory, environment: environment));
|
| + workingDirectory: _sandbox,
|
| + environment: environment,
|
| + description: description);
|
| }
|
|
|
| /// Runs Pub.
|
| -ProcessResult runPub(List<String> args, {String workingDirectory,
|
| - Map<String, String> environment}) {
|
| - // TODO(nweiz): Use ScheduledProcess once it's compatible.
|
| - return new _NormalizedProcessResult(Process.runSync(
|
| +ScheduledProcess runPub(List args, {Map<String, String> environment}) {
|
| + return new ScheduledProcess.start(
|
| _pubPath, args,
|
| - workingDirectory: workingDirectory, environment: environment));
|
| + workingDirectory: _sandbox,
|
| + environment: environment,
|
| + description: "pub ${args.first}");
|
| }
|
|
|
| -/// Starts the test executable with the package root set properly.
|
| -Future<Process> startTest(List<String> args, {String workingDirectory,
|
| - Map<String, String> environment}) {
|
| - var allArgs = [
|
| - p.absolute(p.join(packageDir, 'bin/test.dart')),
|
| - "--package-root=${p.join(packageDir, 'packages')}"
|
| - ]..addAll(args);
|
| -
|
| - if (environment == null) environment = {};
|
| - environment.putIfAbsent("_UNITTEST_USE_COLOR", () => "false");
|
| -
|
| - return startDart(allArgs, workingDirectory: workingDirectory,
|
| - environment: environment);
|
| -}
|
| -
|
| -/// Starts Dart.
|
| -Future<Process> startDart(List<String> args, {String workingDirectory,
|
| - Map<String, String> environment}) {
|
| - var allArgs = Platform.executableArguments.toList()..addAll(args);
|
| -
|
| - // TODO(nweiz): Use ScheduledProcess once it's compatible.
|
| - return Process.start(Platform.executable, allArgs,
|
| - workingDirectory: workingDirectory, environment: environment);
|
| -}
|
| -
|
| -/// Starts Pub.
|
| -Future<Process> startPub(List<String> args, {String workingDirectory,
|
| +/// Runs "pub serve".
|
| +///
|
| +/// This returns assigns [_pubServePort] to a future that will complete to the
|
| +/// port of the "pub serve" instance.
|
| +ScheduledProcess runPubServe({List args, String workingDirectory,
|
| Map<String, String> environment}) {
|
| - // TODO(nweiz): Use ScheduledProcess once it's compatible.
|
| - return Process.start(_pubPath, args,
|
| - workingDirectory: workingDirectory, environment: environment);
|
| -}
|
| + _pubServePortCompleter = new Completer();
|
| + currentSchedule.onComplete.schedule(() => _pubServePortCompleter = null);
|
|
|
| -/// Starts "pub serve".
|
| -///
|
| -/// This returns a pair of the pub serve process and the port it's serving on.
|
| -Future<Pair<Process, int>> startPubServe({List<String> args,
|
| - String workingDirectory, Map<String, String> environment}) async {
|
| var allArgs = ['serve', '--port', '0'];
|
| if (args != null) allArgs.addAll(args);
|
|
|
| - var process = await startPub(allArgs,
|
| - workingDirectory: workingDirectory, environment: environment);
|
| - var line = await lineSplitter.bind(process.stdout)
|
| - .firstWhere(_servingRegExp.hasMatch);
|
| - var match = _servingRegExp.firstMatch(line);
|
| + var pub = runPub(allArgs, environment: environment);
|
|
|
| - return new Pair(process, int.parse(match[1]));
|
| -}
|
| + schedule(() async {
|
| + var match;
|
| + while (match == null) {
|
| + var line = await pub.stdout.next();
|
| + match = _servingRegExp.firstMatch(line);
|
| + }
|
| + _pubServePortCompleter.complete(int.parse(match[1]));
|
| + }, "waiting for pub serve to emit its port number");
|
|
|
| -/// A wrapper around [ProcessResult] that normalizes the newline format across
|
| -/// operating systems.
|
| -class _NormalizedProcessResult implements ProcessResult {
|
| - final ProcessResult _inner;
|
| -
|
| - int get exitCode => _inner.exitCode;
|
| - int get pid => _inner.pid;
|
| -
|
| - final String stdout;
|
| - final String stderr;
|
| -
|
| - _NormalizedProcessResult(ProcessResult inner)
|
| - : _inner = inner,
|
| - stdout = Platform.isWindows
|
| - ? inner.stdout.replaceAll("\r\n", "\n")
|
| - : inner.stdout,
|
| - stderr = Platform.isWindows
|
| - ? inner.stderr.replaceAll("\r\n", "\n")
|
| - : inner.stderr;
|
| + return pub;
|
| }
|
|
|