| Index: mojo/public/dart/third_party/test/lib/src/util/io.dart
|
| diff --git a/mojo/public/dart/third_party/test/lib/src/util/io.dart b/mojo/public/dart/third_party/test/lib/src/util/io.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..ceb8da6c9992ecc06e79cb933e99eab5d0b4805a
|
| --- /dev/null
|
| +++ b/mojo/public/dart/third_party/test/lib/src/util/io.dart
|
| @@ -0,0 +1,201 @@
|
| +// 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.
|
| +
|
| +library test.util.io;
|
| +
|
| +import 'dart:async';
|
| +import 'dart:convert';
|
| +import 'dart:io';
|
| +import 'dart:mirrors';
|
| +
|
| +import 'package:path/path.dart' as p;
|
| +import 'package:pub_semver/pub_semver.dart';
|
| +
|
| +import '../backend/operating_system.dart';
|
| +import '../runner/application_exception.dart';
|
| +import '../util/stream_queue.dart';
|
| +import '../utils.dart';
|
| +
|
| +/// The ASCII code for a newline character.
|
| +const _newline = 0xA;
|
| +
|
| +/// The ASCII code for a carriage return character.
|
| +const _carriageReturn = 0xD;
|
| +
|
| +/// The root directory of the Dart SDK.
|
| +final String sdkDir = p.dirname(p.dirname(Platform.resolvedExecutable));
|
| +
|
| +/// The version of the Dart SDK currently in use.
|
| +final Version _sdkVersion = new Version.parse(
|
| + new File(p.join(sdkDir, 'version'))
|
| + .readAsStringSync().trim());
|
| +
|
| +/// Returns the current operating system.
|
| +final OperatingSystem currentOS = (() {
|
| + var name = Platform.operatingSystem;
|
| + var os = OperatingSystem.findByIoName(name);
|
| + if (os != null) return os;
|
| +
|
| + throw new UnsupportedError('Unsupported operating system "$name".');
|
| +})();
|
| +
|
| +/// A queue of lines of standard input.
|
| +final stdinLines = new StreamQueue(
|
| + UTF8.decoder.fuse(const LineSplitter()).bind(stdin));
|
| +
|
| +/// The root directory below which to nest temporary directories created by the
|
| +/// test runner.
|
| +///
|
| +/// This is configurable so that the test code can validate that the runner
|
| +/// cleans up after itself fully.
|
| +final _tempDir = Platform.environment.containsKey("_UNITTEST_TEMP_DIR")
|
| + ? Platform.environment["_UNITTEST_TEMP_DIR"]
|
| + : Directory.systemTemp.path;
|
| +
|
| +/// The path to the `lib` directory of the `test` package.
|
| +String libDir({String packageRoot}) {
|
| + var pathToIo = libraryPath(#test.util.io, packageRoot: packageRoot);
|
| + return p.dirname(p.dirname(p.dirname(pathToIo)));
|
| +}
|
| +
|
| +// TODO(nweiz): Make this check [stdioType] once that works within "pub run".
|
| +/// Whether "special" strings such as Unicode characters or color escapes are
|
| +/// safe to use.
|
| +///
|
| +/// On Windows or when not printing to a terminal, only printable ASCII
|
| +/// characters should be used.
|
| +bool get canUseSpecialChars =>
|
| + Platform.operatingSystem != 'windows' &&
|
| + Platform.environment["_UNITTEST_USE_COLOR"] != "false";
|
| +
|
| +/// Creates a temporary directory and returns its path.
|
| +String createTempDir() =>
|
| + new Directory(_tempDir).createTempSync('dart_test_').path;
|
| +
|
| +/// Creates a temporary directory and passes its path to [fn].
|
| +///
|
| +/// Once the [Future] returned by [fn] completes, the temporary directory and
|
| +/// all its contents are deleted. [fn] can also return `null`, in which case
|
| +/// the temporary directory is deleted immediately afterwards.
|
| +///
|
| +/// Returns a future that completes to the value that the future returned from
|
| +/// [fn] completes to.
|
| +Future withTempDir(Future fn(String path)) {
|
| + return new Future.sync(() {
|
| + var tempDir = createTempDir();
|
| + return new Future.sync(() => fn(tempDir))
|
| + .whenComplete(() => new Directory(tempDir).deleteSync(recursive: true));
|
| + });
|
| +}
|
| +
|
| +/// Return a transformation of [input] with all null bytes removed.
|
| +///
|
| +/// This works around the combination of issue 23295 and 22667 by removing null
|
| +/// bytes. This workaround can be removed when either of those are fixed in the
|
| +/// oldest supported SDK.
|
| +///
|
| +/// It also somewhat works around issue 23303 by removing any carriage returns
|
| +/// that are followed by newlines, to ensure that carriage returns aren't
|
| +/// doubled up in the output. This can be removed when the issue is fixed in the
|
| +/// oldest supported SDk.
|
| +Stream<List<int>> sanitizeForWindows(Stream<List<int>> input) {
|
| + if (!Platform.isWindows) return input;
|
| +
|
| + return input.map((list) {
|
| + var previous;
|
| + return list.reversed.where((byte) {
|
| + if (byte == 0) return false;
|
| + if (byte == _carriageReturn && previous == _newline) return false;
|
| + previous = byte;
|
| + return true;
|
| + }).toList().reversed.toList();
|
| + });
|
| +}
|
| +
|
| +/// Print a warning containing [message].
|
| +///
|
| +/// This automatically wraps lines if they get too long. If [color] is passed,
|
| +/// it controls whether the warning header is color; otherwise, it defaults to
|
| +/// [canUseSpecialChars].
|
| +void warn(String message, {bool color}) {
|
| + if (color == null) color = canUseSpecialChars;
|
| + var header = color
|
| + ? "\u001b[33mWarning:\u001b[0m"
|
| + : "Warning:";
|
| + stderr.writeln(wordWrap("$header $message\n"));
|
| +}
|
| +
|
| +/// Creates a URL string for [address]:[port].
|
| +///
|
| +/// Handles properly formatting IPv6 addresses.
|
| +Uri baseUrlForAddress(InternetAddress address, int port) {
|
| + if (address.isLoopback) {
|
| + return new Uri(scheme: "http", host: "localhost", port: port);
|
| + }
|
| +
|
| + // IPv6 addresses in URLs need to be enclosed in square brackets to avoid
|
| + // URL ambiguity with the ":" in the address.
|
| + if (address.type == InternetAddressType.IP_V6) {
|
| + return new Uri(scheme: "http", host: "[${address.address}]", port: port);
|
| + }
|
| +
|
| + return new Uri(scheme: "http", host: address.address, port: port);
|
| +}
|
| +
|
| +/// Returns the package root at [root].
|
| +///
|
| +/// If [override] is passed, that's used. If the package root doesn't exist, an
|
| +/// [ApplicationException] is thrown.
|
| +String packageRootFor(String root, [String override]) {
|
| + if (root == null) root = p.current;
|
| + var packageRoot = override == null ? p.join(root, 'packages') : override;
|
| +
|
| + if (!new Directory(packageRoot).existsSync()) {
|
| + throw new ApplicationException(
|
| + "Directory ${p.prettyUri(p.toUri(packageRoot))} does not exist.");
|
| + }
|
| +
|
| + return packageRoot;
|
| +}
|
| +
|
| +/// The library name must be globally unique, or the wrong library path may be
|
| +/// returned.
|
| +String libraryPath(Symbol libraryName, {String packageRoot}) {
|
| + var lib = currentMirrorSystem().findLibrary(libraryName);
|
| + if (lib.uri.scheme != 'package') return p.fromUri(lib.uri);
|
| +
|
| + // TODO(nweiz): is there a way to avoid assuming this is being run next to a
|
| + // packages directory?.
|
| + if (packageRoot == null) packageRoot = p.absolute('packages');
|
| + return p.join(packageRoot, p.fromUri(lib.uri.path));
|
| +}
|
| +
|
| +/// Repeatedly finds a probably-unused port on localhost and passes it to
|
| +/// [tryPort] until it binds successfully.
|
| +///
|
| +/// [tryPort] should return a non-`null` value or a Future completing to a
|
| +/// non-`null` value once it binds successfully. This value will be returned
|
| +/// by [getUnusedPort] in turn.
|
| +///
|
| +/// This is necessary for ensuring that our port binding isn't flaky for
|
| +/// applications that don't print out the bound port.
|
| +Future getUnusedPort(tryPort(int port)) {
|
| + var value;
|
| + return Future.doWhile(() async {
|
| + value = await tryPort(await getUnsafeUnusedPort());
|
| + return value == null;
|
| + }).then((_) => value);
|
| +}
|
| +
|
| +/// Returns a port that is probably, but not definitely, not in use.
|
| +///
|
| +/// This has a built-in race condition: another process may bind this port at
|
| +/// any time after this call has returned. If at all possible, callers should
|
| +/// use [getUnusedPort] instead.
|
| +Future<int> getUnsafeUnusedPort() async {
|
| + var socket = await RawServerSocket.bind(InternetAddress.LOOPBACK_IP_V4, 0);
|
| + var port = socket.port;
|
| + await socket.close();
|
| + return port;
|
| +}
|
|
|