Add Process wrapper (#34)

Initially contains `Future<int> get done`
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b5b5bfa..dc8760c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,8 @@
 
+#### 3.0.6
+
+* Added class `Process`, a simple wrapper around dart:io's `Process` class.
+
 #### 3.0.5
 
 * Fixes for missing_return analysis errors with 2.10.0-dev.1.0.
diff --git a/lib/process.dart b/lib/process.dart
index 2fddabb..f9de50f 100644
--- a/lib/process.dart
+++ b/lib/process.dart
@@ -3,4 +3,5 @@
 // BSD-style license that can be found in the LICENSE file.
 
 export 'src/interface/local_process_manager.dart';
+export 'src/interface/process.dart';
 export 'src/interface/process_manager.dart';
diff --git a/lib/src/interface/process.dart b/lib/src/interface/process.dart
new file mode 100644
index 0000000..cd6d4e5
--- /dev/null
+++ b/lib/src/interface/process.dart
@@ -0,0 +1,55 @@
+// Copyright (c) 2017, 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' as io;
+
+/// A wrapper around an [io.Process] class that adds some convenience methods.
+class Process implements io.Process {
+  /// Constructs a [Process] object that delegates to the specified underlying
+  /// object.
+  const Process(this.delegate);
+
+  final io.Process delegate;
+
+  @override
+  Future<int> get exitCode => delegate.exitCode;
+
+  /// A [Future] that completes when the process has exited and its standard
+  /// output and error streams have closed.
+  ///
+  /// This exists as an alternative to [exitCode], which does not guarantee
+  /// that the stdio streams have closed (it is possible for the exit code to
+  /// be available before stdout and stderr have closed).
+  ///
+  /// The future returned here will complete with the exit code of the process.
+  Future<int> get done async {
+    int result;
+    await Future.wait<void>(<Future<void>>[
+      delegate.stdout.length,
+      delegate.stderr.length,
+      delegate.exitCode.then((int value) {
+        result = value;
+      }),
+    ]);
+    assert(result != null);
+    return result;
+  }
+
+  @override
+  bool kill([io.ProcessSignal signal = io.ProcessSignal.sigterm]) {
+    return delegate.kill(signal);
+  }
+
+  @override
+  int get pid => delegate.pid;
+
+  @override
+  Stream<List<int>> get stderr => delegate.stderr;
+
+  @override
+  io.IOSink get stdin => delegate.stdin;
+
+  @override
+  Stream<List<int>> get stdout => delegate.stdout;
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index 9a312dd..7c34541 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: process
-version: 3.0.5
+version: 3.0.6
 authors:
 - Todd Volkert <tvolkert@google.com>
 - Michael Goderbauer <goderbauer@google.com>
diff --git a/test/src/interface/process_test.dart b/test/src/interface/process_test.dart
new file mode 100644
index 0000000..d205b97
--- /dev/null
+++ b/test/src/interface/process_test.dart
@@ -0,0 +1,65 @@
+// Copyright (c) 2017, 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:async';
+import 'dart:io' as io;
+
+import 'package:process/process.dart';
+import 'package:test/test.dart';
+
+void main() {
+  group('done', () {
+    test('completes only when all done', () async {
+      TestProcess delegate = new TestProcess();
+      Process process = new Process(delegate);
+      bool done = false;
+      // ignore: unawaited_futures
+      process.done.then((int result) {
+        done = true;
+      });
+      expect(done, isFalse);
+      delegate.exitCodeCompleter.complete(0);
+      await Future<void>.value();
+      expect(done, isFalse);
+      await delegate.stdoutController.close();
+      await Future<void>.value();
+      expect(done, isFalse);
+      await delegate.stderrController.close();
+      await Future<void>.value();
+      expect(done, isTrue);
+      expect(await process.exitCode, 0);
+    });
+  });
+}
+
+class TestProcess implements io.Process {
+  TestProcess([this.pid = 123])
+      : exitCodeCompleter = new Completer<int>(),
+        stdoutController = new StreamController<List<int>>(),
+        stderrController = new StreamController<List<int>>();
+
+  @override
+  final int pid;
+  final Completer<int> exitCodeCompleter;
+  final StreamController<List<int>> stdoutController;
+  final StreamController<List<int>> stderrController;
+
+  @override
+  Future<int> get exitCode => exitCodeCompleter.future;
+
+  @override
+  bool kill([io.ProcessSignal signal = io.ProcessSignal.sigterm]) {
+    exitCodeCompleter.complete(-1);
+    return true;
+  }
+
+  @override
+  Stream<List<int>> get stderr => stderrController.stream;
+
+  @override
+  io.IOSink get stdin => throw UnsupportedError('Not supported');
+
+  @override
+  Stream<List<int>> get stdout => stdoutController.stream;
+}