Fix ProcessManager, align better with dart:io (#19)

* Fix a bug, add named constructors.

* Some refactors.

* Dartfmt.

* Fix example.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b369f92..a0e6bf1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,22 @@
-## 0.1.0
+## 0.3.0
+
+- **BREAKING CHANGE**: The `arguments` argument to `ProcessManager.spawn` is
+  now positional (not named) and required. This makes it more similar to the
+  built-in `Process.start`, and easier to use as a drop in replacement:
+
+```dart
+processManager.spawn('dart', ['--version']);
+```
+
+- Fixed a bug where processes created from `ProcessManager.spawn` could not
+  have their `stdout`/`stderr` read through their respective getters (a runtime
+  error was always thrown).
+
+- Added `ProcessMangaer#spawnBackground`, which does not forward `stdin`.
+
+- Added `ProcessManager#spawnDetached`, which does not forward any I/O.
+
+## 0.2.0
 
 - Initial commit of...
    - `FutureOr<bool> String isExecutable(path)`.
diff --git a/README.md b/README.md
index bbb3a1f..3d541a7 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,15 @@
 
 [![Build Status](https://travis-ci.org/dart-lang/io.svg?branch=master)](https://travis-ci.org/dart-lang/io)
 
+## Contributing
+
+> **NOTE**: Due to the changing nature of the Dart SDK (towards 2.0.0), running
+> `dartfmt` requires the local executable:
+
+```sh
+$ pub run dart_style:format
+```
+
 ## Usage - `io.dart`
 
 ### Files
diff --git a/example/spawn_process.dart b/example/spawn_process.dart
index d865138..7654110 100644
--- a/example/spawn_process.dart
+++ b/example/spawn_process.dart
@@ -12,19 +12,19 @@
 
   // Runs dartfmt --version and outputs the result via stdout.
   print('Running dartfmt --version');
-  var spawn = await manager.spawn('dartfmt', arguments: ['--version']);
+  var spawn = await manager.spawn('dartfmt', ['--version']);
   await spawn.exitCode;
 
   // Runs dartfmt -n . and outputs the result via stdout.
   print('Running dartfmt -n .');
-  spawn = await manager.spawn('dartfmt', arguments: ['-n', '.']);
+  spawn = await manager.spawn('dartfmt', ['-n', '.']);
   await spawn.exitCode;
 
   // Runs pub publish. Upon hitting a blocking stdin state, you may directly
   // output to the processes's stdin via your own, similar to how a bash or
   // shell script would spawn a process.
   print('Running pub publish');
-  spawn = await manager.spawn('pub', arguments: ['publish']);
+  spawn = await manager.spawn('pub', ['publish']);
   await spawn.exitCode;
 
   // Closes stdin for the entire program.
diff --git a/lib/src/process_manager.dart b/lib/src/process_manager.dart
index 496c17d..5a796d8 100644
--- a/lib/src/process_manager.dart
+++ b/lib/src/process_manager.dart
@@ -52,12 +52,44 @@
   ///
   /// Returns a future that completes with a handle to the spawned process.
   Future<io.Process> spawn(
-    String executable, {
-    Iterable<String> arguments: const [],
-  }) async {
+    String executable,
+    Iterable<String> arguments,
+  ) async {
     final process = io.Process.start(executable, arguments.toList());
     return new _ForwardingSpawn(await process, _stdin, _stdout, _stderr);
   }
+
+  /// Spawns a process by invoking [executable] with [arguments].
+  ///
+  /// This is _similar_ to [io.Process.start], but `stdout` and `stderr` is
+  /// forwarded/routed between the process and host, similar to how a shell
+  /// script works.
+  ///
+  /// Returns a future that completes with a handle to the spawned process.
+  Future<io.Process> spawnBackground(
+    String executable,
+    Iterable<String> arguments,
+  ) async {
+    final process = io.Process.start(executable, arguments.toList());
+    return new _ForwardingSpawn(
+      await process,
+      const Stream.empty(),
+      _stdout,
+      _stderr,
+    );
+  }
+
+  /// Spawns a process by invoking [executable] with [arguments].
+  ///
+  /// This is _identical to [io.Process.start] (no forwarding of I/O).
+  ///
+  /// Returns a future that completes with a handle to the spawned process.
+  Future<io.Process> spawnDetached(
+    String executable,
+    Iterable<String> arguments,
+  ) async {
+    return io.Process.start(executable, arguments.toList());
+  }
 }
 
 /// A process instance created and managed through [ProcessManager].
@@ -99,6 +131,8 @@
   final StreamSubscription _stdInSub;
   final StreamSubscription _stdOutSub;
   final StreamSubscription _stdErrSub;
+  final StreamController<List<int>> _stdOut;
+  final StreamController<List<int>> _stdErr;
 
   factory _ForwardingSpawn(
     io.Process delegate,
@@ -106,14 +140,24 @@
     io.IOSink stdout,
     io.IOSink stderr,
   ) {
+    final stdoutSelf = new StreamController<List<int>>();
+    final stderrSelf = new StreamController<List<int>>();
     final stdInSub = stdin.listen(delegate.stdin.add);
-    final stdOutSub = delegate.stdout.listen(stdout.add);
-    final stdErrSub = delegate.stderr.listen(stderr.add);
+    final stdOutSub = delegate.stdout.listen((event) {
+      stdout.add(event);
+      stdoutSelf.add(event);
+    });
+    final stdErrSub = delegate.stderr.listen((event) {
+      stderr.add(event);
+      stderrSelf.add(event);
+    });
     return new _ForwardingSpawn._delegate(
       delegate,
       stdInSub,
       stdOutSub,
       stdErrSub,
+      stdoutSelf,
+      stderrSelf,
     );
   }
 
@@ -122,6 +166,8 @@
     this._stdInSub,
     this._stdOutSub,
     this._stdErrSub,
+    this._stdOut,
+    this._stdErr,
   )
       : super._(delegate);
 
@@ -132,6 +178,12 @@
     _stdErrSub.cancel();
     super._onClosed();
   }
+
+  @override
+  Stream<List<int>> get stdout => _stdOut.stream;
+
+  @override
+  Stream<List<int>> get stderr => _stdErr.stream;
 }
 
 class _UnixProcessManager extends ProcessManager {
diff --git a/pubspec.yaml b/pubspec.yaml
index 2175d5d..5b8d7d7 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,7 +1,7 @@
 name: io
 description: >
   Utilities for the Dart VM Runtime.
-version: 0.2.0
+version: 0.2.1
 author: Dart Team <misc@dartlang.org>
 homepage: https://github.com/dart-lang/io
 
@@ -12,5 +12,6 @@
   meta: ^1.0.2
 
 dev_dependencies:
+  dart_style: ^1.0.7
   path: ^1.0.0
   test: ^0.12.0
diff --git a/test/process_manager_test.dart b/test/process_manager_test.dart
index 4ad49d7..88e63af 100644
--- a/test/process_manager_test.dart
+++ b/test/process_manager_test.dart
@@ -42,7 +42,7 @@
     test('should output Hello from another process [via stdout]', () async {
       final spawn = await processManager.spawn(
         'dart',
-        arguments: [p.join('test', '_files', 'stdout_hello.dart')],
+        [p.join('test', '_files', 'stdout_hello.dart')],
       );
       await spawn.exitCode;
       expect(stdoutLog, ['Hello']);
@@ -51,7 +51,7 @@
     test('should output Hello from another process [via stderr]', () async {
       final spawn = await processManager.spawn(
         'dart',
-        arguments: [p.join('test', '_files', 'stderr_hello.dart')],
+        [p.join('test', '_files', 'stderr_hello.dart')],
       );
       await spawn.exitCode;
       expect(stderrLog, ['Hello']);
@@ -60,13 +60,29 @@
     test('should forward stdin to another process', () async {
       final spawn = await processManager.spawn(
         'dart',
-        arguments: [p.join('test', '_files', 'stdin_echo.dart')],
+        [p.join('test', '_files', 'stdin_echo.dart')],
       );
       spawn.stdin.writeln('Ping');
       await spawn.exitCode;
-      // TODO: https://github.com/dart-lang/sdk/issues/30119.
-      // expect(stdoutLog, ['You said: Ping', '\n']);
       expect(stdoutLog.join(''), contains('You said: Ping'));
     });
+
+    group('should return a Process where', () {
+      test('.stdout is readable', () async {
+        final spawn = await processManager.spawn(
+          'dart',
+          [p.join('test', '_files', 'stdout_hello.dart')],
+        );
+        expect(await spawn.stdout.transform(UTF8.decoder).first, 'Hello');
+      });
+
+      test('.stderr is readable', () async {
+        final spawn = await processManager.spawn(
+          'dart',
+          [p.join('test', '_files', 'stderr_hello.dart')],
+        );
+        expect(await spawn.stderr.transform(UTF8.decoder).first, 'Hello');
+      });
+    });
   });
 }