| // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| // TODO(nweiz): Remove this tag when we can get [packageDir] working without it |
| // (dart-lang/sdk#24022). |
| library test.test.io; |
| |
| import 'dart:async'; |
| import 'dart:io'; |
| |
| import 'package:package_resolver/package_resolver.dart'; |
| 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'; |
| |
| /// The path to the root directory of the `test` package. |
| final Future<String> packageDir = PackageResolver.current.packagePath('test'); |
| |
| /// The path to the `pub` executable in the current Dart SDK. |
| final _pubPath = p.absolute(p.join( |
| p.dirname(Platform.resolvedExecutable), |
| Platform.isWindows ? 'pub.bat' : 'pub')); |
| |
| /// The platform-specific message emitted when a nonexistent file is loaded. |
| final String noSuchFileMessage = Platform.isWindows |
| ? "The system cannot find the file specified." |
| : "No such file or directory"; |
| |
| /// A regular expression that matches the output of "pub serve". |
| final _servingRegExp = |
| new RegExp(r'^Serving myapp [a-z]+ on http://localhost:(\d+)$'); |
| |
| /// An operating system name that's different than the current operating system. |
| final otherOS = Platform.isWindows ? "mac-os" : "windows"; |
| |
| /// 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. |
| /// |
| /// If [forwardStdio] is true, the standard output and error from the process |
| /// will be printed as part of the parent test. This is used for debugging. |
| ScheduledProcess runTest(List args, {String reporter, |
| int concurrency, Map<String, String> environment, |
| bool forwardStdio: false}) { |
| concurrency ??= 1; |
| |
| var allArgs = [ |
| packageDir.then((dir) => p.absolute(p.join(dir, 'bin/test.dart'))), |
| "--concurrency=$concurrency" |
| ]; |
| if (reporter != null) allArgs.add("--reporter=$reporter"); |
| allArgs.addAll(args); |
| |
| if (environment == null) environment = {}; |
| environment.putIfAbsent("_DART_TEST_TESTING", () => "true"); |
| |
| var process = runDart(allArgs, |
| environment: environment, |
| description: "dart bin/test.dart"); |
| |
| if (forwardStdio) { |
| process.stdoutStream().listen(print); |
| process.stderrStream().listen(print); |
| } |
| |
| return process; |
| } |
| |
| /// Runs Dart. |
| ScheduledProcess runDart(List<String> args, {Map<String, String> environment, |
| String description}) { |
| var allArgs = <Object>[] |
| ..addAll(Platform.executableArguments.where((arg) => |
| !arg.startsWith("--package-root=") && !arg.startsWith("--packages="))) |
| ..add(PackageResolver.current.processArgument) |
| ..addAll(args); |
| |
| return new ScheduledProcess.start( |
| p.absolute(Platform.resolvedExecutable), allArgs, |
| workingDirectory: _sandbox, |
| environment: environment, |
| description: description); |
| } |
| |
| /// Runs Pub. |
| ScheduledProcess runPub(List args, {Map<String, String> environment}) { |
| return new ScheduledProcess.start( |
| _pubPath, args, |
| workingDirectory: _sandbox, |
| environment: environment, |
| description: "pub ${args.first}"); |
| } |
| |
| /// Runs "pub serve". |
| /// |
| /// This returns assigns [_pubServePort] to a future that will complete to the |
| /// port of the "pub serve" instance. |
| ScheduledProcess runPubServe({List<String> args, String workingDirectory, |
| Map<String, String> environment}) { |
| _pubServePortCompleter = new Completer(); |
| currentSchedule.onComplete.schedule(() => _pubServePortCompleter = null); |
| |
| var allArgs = ['serve', '--port', '0']; |
| if (args != null) allArgs.addAll(args); |
| |
| var pub = runPub(allArgs, environment: environment); |
| |
| 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"); |
| |
| return pub; |
| } |