| Index: test/child_server_process.dart
|
| diff --git a/test/child_server_process.dart b/test/child_server_process.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..8a6f85ef7a3ba37762b0e8b8202b880488e0280c
|
| --- /dev/null
|
| +++ b/test/child_server_process.dart
|
| @@ -0,0 +1,83 @@
|
| +// Copyright (c) 2016, 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.
|
| +
|
| +import 'dart:io';
|
| +import 'dart:async';
|
| +
|
| +class ChildServerProcess {
|
| + /// [IOSink]s like [stdout] don't like to be piped more than one [Stream], but
|
| + /// we want to pipe many of them (basically, every standard output and error
|
| + /// of every child process we open), so we pipe to an accommodating consumer.
|
| + static final _consoleOut = new _MultipleStreamConsumer(stdout);
|
| +
|
| + final Process process;
|
| + final String host;
|
| + final int port;
|
| + ChildServerProcess._(this.process, this.host, this.port);
|
| +
|
| + get httpUri => Uri.parse('http://$host:$port');
|
| +
|
| + static build(Future<Process> builder(String host, int port),
|
| + {int defaultPort: 1024,
|
| + int maxPort: 65535,
|
| + String host: '0.0.0.0'}) async {
|
| + var port = await _findUnusedPort(defaultPort, maxPort);
|
| + var p = (await builder(host, port))
|
| + ..stdout.pipe(_consoleOut)
|
| + ..stderr.pipe(_consoleOut);
|
| + await _waitForServer(host, port);
|
| + return new ChildServerProcess._(p, host, port);
|
| + }
|
| +
|
| + static _waitForServer(String host, int port,
|
| + {int attempts: 10,
|
| + Duration retryDelay: const Duration(seconds: 1)}) async {
|
| + var lastError;
|
| + for (int i = 0; i < attempts; i++) {
|
| + try {
|
| + await (await Socket.connect(host, port)).close();
|
| + return;
|
| + } catch (e) {
|
| + lastError = e;
|
| + await new Future.delayed(retryDelay);
|
| + }
|
| + }
|
| + throw new StateError(
|
| + 'Failed to connect to $host:$port after $attempts attempts; '
|
| + 'Last error:\n$lastError');
|
| + }
|
| +
|
| + static Future<int> _findUnusedPort(int fromPort, int toPort) async {
|
| + var lastError;
|
| + for (int port = fromPort; port <= toPort; port++) {
|
| + try {
|
| + await (await ServerSocket.bind(InternetAddress.ANY_IP_V4, port))
|
| + .close();
|
| + return port;
|
| + } catch (e) {
|
| + lastError = e;
|
| + }
|
| + }
|
| + throw new StateError(
|
| + 'Failed to find an unused port between $fromPort and $toPort; '
|
| + 'Last error:\n$lastError');
|
| + }
|
| +}
|
| +
|
| +/// A consumer into which we can pipe as many streams as we want, that forwards
|
| +/// everything to an [IOSink] (such as [stdout]).
|
| +class _MultipleStreamConsumer extends StreamConsumer<List<int>> {
|
| + final IOSink _sink;
|
| + _MultipleStreamConsumer(this._sink);
|
| +
|
| + @override
|
| + Future addStream(Stream<List<int>> stream) async {
|
| + await for (var data in stream) {
|
| + _sink.add(data);
|
| + }
|
| + }
|
| +
|
| + @override
|
| + close() {}
|
| +}
|
|
|