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');
+ });
+ });
});
}