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() {} |
+} |