[null-safety] migrate package:process to _almost_ null safety (#47)

Migrate to null-safety, except for the path/file/platform dependencies which are not yet migrated. Removes record/replay functionality.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c4e881b..59dbf53 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,9 @@
 #### 4.0.0-dev
 
-* Remove implicit casts in preparation for null-safety
-* Remove dependency on `package:intl`
+* Migrate to null-safety.
+* Remove record/replay functionality.
+* Remove implicit casts in preparation for null-safety.
+* Remove dependency on `package:intl` and `package:meta`.
 
 #### 3.0.13
 
diff --git a/analysis_options.yaml b/analysis_options.yaml
index 2b88268..2c72e4b 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -1,4 +1,6 @@
 analyzer:
+  enable-experiment:
+    - non-nullable
   strong-mode:
     implicit-dynamic: false
     implicit-casts: false
diff --git a/dev/travis.sh b/dev/travis.sh
index 866b361..192b2e8 100755
--- a/dev/travis.sh
+++ b/dev/travis.sh
@@ -27,4 +27,4 @@
 set -e
 
 # Run the tests.
-pub run test
+pub run --enable-experiment=non-nullable test
diff --git a/lib/process.dart b/lib/process.dart
index dfeb1d0..a407c48 100644
--- a/lib/process.dart
+++ b/lib/process.dart
@@ -2,6 +2,7 @@
 // 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.
 
+// @dart=2.10
 export 'src/interface/local_process_manager.dart';
 export 'src/interface/process_manager.dart';
 export 'src/interface/process_wrapper.dart';
diff --git a/lib/record_replay.dart b/lib/record_replay.dart
deleted file mode 100644
index e9211bb..0000000
--- a/lib/record_replay.dart
+++ /dev/null
@@ -1,7 +0,0 @@
-// 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.
-
-export 'src/record_replay/command_element.dart';
-export 'src/record_replay/recording_process_manager.dart';
-export 'src/record_replay/replay_process_manager.dart';
diff --git a/lib/src/interface/common.dart b/lib/src/interface/common.dart
index 89c8036..f2db1c3 100644
--- a/lib/src/interface/common.dart
+++ b/lib/src/interface/common.dart
@@ -2,6 +2,7 @@
 // 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.
 
+// @dart=2.10
 import 'package:file/file.dart';
 import 'package:file/local.dart';
 import 'package:path/path.dart' show Context;
@@ -48,9 +49,9 @@
 /// could not be found.
 ///
 /// If [platform] is not specified, it will default to the current platform.
-String getExecutablePath(
+String? getExecutablePath(
   String command,
-  String workingDirectory, {
+  String? workingDirectory, {
   Platform platform = const LocalPlatform(),
   FileSystem fs = const LocalFileSystem(),
 }) {
@@ -72,7 +73,7 @@
 
   List<String> extensions = <String>[];
   if (platform.isWindows && context.extension(command).isEmpty) {
-    extensions = platform.environment['PATHEXT'].split(pathSeparator);
+    extensions = platform.environment['PATHEXT']!.split(pathSeparator);
   }
 
   List<String> candidates = <String>[];
@@ -80,11 +81,16 @@
     candidates = _getCandidatePaths(
         command, <String>[workingDirectory], extensions, context);
   } else {
-    List<String> searchPath = platform.environment['PATH'].split(pathSeparator);
+    List<String> searchPath =
+        platform.environment['PATH']!.split(pathSeparator);
     candidates = _getCandidatePaths(command, searchPath, extensions, context);
   }
-  return candidates.firstWhere((String path) => fs.file(path).existsSync(),
-      orElse: () => null);
+  for (String path in candidates) {
+    if (fs.file(path).existsSync()) {
+      return path;
+    }
+  }
+  return null;
 }
 
 /// Returns all possible combinations of `$searchPath\$command.$ext` for
diff --git a/lib/src/interface/local_process_manager.dart b/lib/src/interface/local_process_manager.dart
index 268bcf7..648540a 100644
--- a/lib/src/interface/local_process_manager.dart
+++ b/lib/src/interface/local_process_manager.dart
@@ -2,6 +2,7 @@
 // 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.
 
+// @dart=2.10
 import 'dart:async';
 import 'dart:convert';
 import 'dart:io'
@@ -31,8 +32,8 @@
   @override
   Future<Process> start(
     covariant List<Object> command, {
-    String workingDirectory,
-    Map<String, String> environment,
+    String? workingDirectory,
+    Map<String, String>? environment,
     bool includeParentEnvironment = true,
     bool runInShell = false,
     ProcessStartMode mode = ProcessStartMode.normal,
@@ -55,8 +56,8 @@
   @override
   Future<ProcessResult> run(
     covariant List<Object> command, {
-    String workingDirectory,
-    Map<String, String> environment,
+    String? workingDirectory,
+    Map<String, String>? environment,
     bool includeParentEnvironment = true,
     bool runInShell = false,
     Encoding stdoutEncoding = systemEncoding,
@@ -81,8 +82,8 @@
   @override
   ProcessResult runSync(
     covariant List<Object> command, {
-    String workingDirectory,
-    Map<String, String> environment,
+    String? workingDirectory,
+    Map<String, String>? environment,
     bool includeParentEnvironment = true,
     bool runInShell = false,
     Encoding stdoutEncoding = systemEncoding,
@@ -105,7 +106,7 @@
   }
 
   @override
-  bool canRun(covariant String executable, {String workingDirectory}) =>
+  bool canRun(covariant String executable, {String? workingDirectory}) =>
       getExecutablePath(executable, workingDirectory) != null;
 
   @override
@@ -115,12 +116,12 @@
 }
 
 String _getExecutable(
-    List<dynamic> command, String workingDirectory, bool runInShell) {
+    List<dynamic> command, String? workingDirectory, bool runInShell) {
   String commandName = command.first.toString();
   if (runInShell) {
     return commandName;
   }
-  String exe = getExecutablePath(commandName, workingDirectory);
+  String? exe = getExecutablePath(commandName, workingDirectory);
   if (exe == null) {
     throw ArgumentError('Cannot find executable for $commandName.');
   }
diff --git a/lib/src/interface/process_manager.dart b/lib/src/interface/process_manager.dart
index 67fd079..8c0e5b7 100644
--- a/lib/src/interface/process_manager.dart
+++ b/lib/src/interface/process_manager.dart
@@ -2,6 +2,7 @@
 // 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.
 
+// @dart=2.10
 import 'dart:async';
 import 'dart:convert';
 import 'dart:io'
diff --git a/lib/src/interface/process_wrapper.dart b/lib/src/interface/process_wrapper.dart
index e2ebee4..9c275f5 100644
--- a/lib/src/interface/process_wrapper.dart
+++ b/lib/src/interface/process_wrapper.dart
@@ -2,6 +2,7 @@
 // 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.
 
+// @dart=2.10
 import 'dart:async';
 import 'dart:io' as io;
 
@@ -53,7 +54,7 @@
   ///
   /// The future returned here will complete with the exit code of the process.
   Future<int> get done async {
-    int result;
+    late int result;
     await Future.wait<void>(<Future<void>>[
       _stdoutDone.future,
       _stderrDone.future,
@@ -61,7 +62,6 @@
         result = value;
       }),
     ]);
-    assert(result != null);
     return result;
   }
 
diff --git a/lib/src/record_replay/can_run_manifest_entry.dart b/lib/src/record_replay/can_run_manifest_entry.dart
deleted file mode 100644
index ec9a64a..0000000
--- a/lib/src/record_replay/can_run_manifest_entry.dart
+++ /dev/null
@@ -1,34 +0,0 @@
-import 'manifest_entry.dart';
-
-/// An entry in the process invocation manifest for `canRun`.
-class CanRunManifestEntry extends ManifestEntry {
-  /// Creates a new manifest entry with the given properties.
-  CanRunManifestEntry({this.executable, this.result});
-
-  /// Creates a new manifest entry populated with the specified JSON [data].
-  ///
-  /// If any required fields are missing from the JSON data, this will throw
-  /// a [FormatException].
-  factory CanRunManifestEntry.fromJson(Map<String, dynamic> data) {
-    checkRequiredField(data, 'executable');
-    checkRequiredField(data, 'result');
-    CanRunManifestEntry entry = CanRunManifestEntry(
-      executable: data['executable'] as String,
-      result: data['result'] as bool,
-    );
-    return entry;
-  }
-
-  @override
-  final String type = 'can_run';
-
-  /// The name of the executable for which the run-ability is checked.
-  final String executable;
-
-  /// The result of the check.
-  final bool result;
-
-  @override
-  Map<String, dynamic> toJson() =>
-      JsonBuilder().add('executable', executable).add('result', result).entry;
-}
diff --git a/lib/src/record_replay/command_element.dart b/lib/src/record_replay/command_element.dart
deleted file mode 100644
index c38a948..0000000
--- a/lib/src/record_replay/command_element.dart
+++ /dev/null
@@ -1,60 +0,0 @@
-// 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 'package:process/process.dart';
-
-/// Callback used to [sanitize](CommandElement.sanitized) a [CommandElement]
-/// for the purpose of recording.
-typedef CommandSanitizer = String Function(String rawValue);
-
-/// A command element capable of holding both a raw and sanitized value.
-///
-/// Instances of this type can be used in the `command` list of the
-/// [ProcessManager.start], [ProcessManager.run], and [ProcessManager.runSync]
-/// methods.
-///
-/// Each command element has:
-///   - A raw value, which is the value that should passed to the underlying
-///     operating system to invoke the process.
-///   - A sanitized value, which is the value that's serialized when used with
-///     [RecordingProcessManager] and looked up in the replay log when used
-///     with [ReplayProcessManager]. Sanitized values typically will remove
-///     user-specific segments (such as the user's home directory) or random
-///     segments (such as temporary file names). Sanitizing values allows you
-///     to guarantee determinism in your process invocation lookups, thus
-///     removing flakiness in tests.
-///
-/// This class implements [toString] to return the element's raw value, meaning
-/// instances of this class can be passed directly to [LocalProcessManager]
-/// and will work as intended.
-class CommandElement {
-  /// Creates a new command element with the specified [raw] value.
-  ///
-  /// If a [sanitizer] is specified, it will be used to generate this command
-  /// element's [sanitized] value. If it is unspecified, the raw value will be
-  /// used as the sanitized value.
-  CommandElement(this.raw, {CommandSanitizer sanitizer})
-      : _sanitizer = sanitizer;
-
-  final CommandSanitizer _sanitizer;
-
-  /// This command element's raw, unsanitized, value.
-  ///
-  /// This value is liable to contain non-deterministic segments, such as
-  /// OS-generated temporary file names. It is suitable for passing to the
-  /// operating system to invoke a process, but it is not suitable for
-  /// record/replay.
-  final String raw;
-
-  /// This command element's sanitized value.
-  ///
-  /// This value has been stripped of any non-deterministic segments, such as
-  /// OS-generated temporary file names or user-specific values. It is suitable
-  /// for record/replay, but it is not suitable for passing to the operating
-  /// system to invoke a process.
-  String get sanitized => _sanitizer == null ? raw : _sanitizer(raw);
-
-  @override
-  String toString() => raw;
-}
diff --git a/lib/src/record_replay/common.dart b/lib/src/record_replay/common.dart
deleted file mode 100644
index fae84a4..0000000
--- a/lib/src/record_replay/common.dart
+++ /dev/null
@@ -1,19 +0,0 @@
-// 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 'command_element.dart';
-
-/// Sanitizes the specified [command] by running any non-deterministic
-/// segments through a [sanitizer](CommandSanitizer) if possible.
-List<String> sanitize(List<dynamic> command) {
-  return command
-      .map((dynamic element) {
-        if (element is CommandElement) {
-          return element.sanitized;
-        }
-        return element.toString();
-      })
-      .toList()
-      .cast<String>();
-}
diff --git a/lib/src/record_replay/constants.dart b/lib/src/record_replay/constants.dart
deleted file mode 100644
index 732d607..0000000
--- a/lib/src/record_replay/constants.dart
+++ /dev/null
@@ -1,6 +0,0 @@
-// 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.
-
-/// The name of the invocation manifest file.
-const String kManifestName = 'MANIFEST.txt';
diff --git a/lib/src/record_replay/manifest.dart b/lib/src/record_replay/manifest.dart
deleted file mode 100644
index 9aae5fd..0000000
--- a/lib/src/record_replay/manifest.dart
+++ /dev/null
@@ -1,128 +0,0 @@
-// 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:convert';
-import 'dart:io' show ProcessStartMode;
-
-import 'can_run_manifest_entry.dart';
-import 'manifest_entry.dart';
-import 'run_manifest_entry.dart';
-
-/// Tests if two lists contain pairwise equal elements.
-bool _areListsEqual<T>(List<T> list1, List<T> list2) {
-  int i = 0;
-  return list1 != null &&
-      list2 != null &&
-      list1.length == list2.length &&
-      list1.every((dynamic element) => element == list2[i++]);
-}
-
-/// Tells whether [testValue] is non-null and equal to [entryValue]. If
-/// [isEqual] is specified, it will be used to determine said equality.
-bool _isHit<T>(
-  T entryValue,
-  T testValue, [
-  bool isEqual(T value1, T value2),
-]) {
-  if (testValue == null) {
-    return true;
-  } else if (isEqual != null) {
-    return isEqual(entryValue, testValue);
-  }
-  return entryValue == testValue;
-}
-
-/// The process invocation manifest, holding metadata about every recorded
-/// process invocation.
-class Manifest {
-  /// Creates a new manifest.
-  Manifest();
-
-  /// Creates a new manifest populated with the specified [json] data.
-  ///
-  /// If [json] does not represent a valid JSON string (matching the format of
-  /// [toJson]), a [FormatException] will be thrown.
-  factory Manifest.fromJson(String json) {
-    List<Map<String, dynamic>> decoded =
-        (jsonDecode(json) as List<dynamic>).cast<Map<String, dynamic>>();
-    Manifest manifest = Manifest();
-    decoded.forEach((Map<String, dynamic> entry) {
-      switch (entry['type'] as String) {
-        case 'run':
-          manifest._entries.add(
-              RunManifestEntry.fromJson(entry['body'] as Map<String, dynamic>));
-          break;
-        case 'can_run':
-          manifest._entries.add(CanRunManifestEntry.fromJson(
-              entry['body'] as Map<String, dynamic>));
-          break;
-        default:
-          throw UnsupportedError('Manifest type ${entry['type']} is unkown.');
-      }
-    });
-    return manifest;
-  }
-
-  final List<ManifestEntry> _entries = <ManifestEntry>[];
-
-  /// Adds the specified [entry] to this manifest.
-  void add(ManifestEntry entry) => _entries.add(entry);
-
-  /// The number of entries currently in the manifest.
-  int get length => _entries.length;
-
-  /// Gets the entry whose [RunManifestEntry.pid] matches the specified [pid].
-  ManifestEntry getRunEntry(int pid) {
-    return _entries.firstWhere(
-        (ManifestEntry entry) => entry is RunManifestEntry && entry.pid == pid);
-  }
-
-  /// Finds the first manifest entry that has not been invoked and whose
-  /// metadata matches the specified criteria. If no arguments are specified,
-  /// this will simply return the first entry that has not yet been invoked.
-  ManifestEntry findPendingRunEntry({
-    List<String> command,
-    ProcessStartMode mode,
-    Encoding stdoutEncoding,
-    Encoding stderrEncoding,
-  }) {
-    return _entries.firstWhere(
-      (ManifestEntry entry) {
-        return entry is RunManifestEntry &&
-            !entry.invoked &&
-            _isHit(entry.command, command, _areListsEqual) &&
-            _isHit(entry.mode, mode) &&
-            _isHit(entry.stdoutEncoding, stdoutEncoding) &&
-            _isHit(entry.stderrEncoding, stderrEncoding);
-      },
-      orElse: () => null,
-    );
-  }
-
-  /// Finds the first manifest entry that has not been invoked and whose
-  /// metadata matches the specified criteria. If no arguments are specified,
-  /// this will simply return the first entry that has not yet been invoked.
-  ManifestEntry findPendingCanRunEntry({
-    String executable,
-  }) {
-    return _entries.firstWhere(
-      (ManifestEntry entry) {
-        return entry is CanRunManifestEntry &&
-            !entry.invoked &&
-            _isHit(entry.executable, executable);
-      },
-      orElse: () => null,
-    );
-  }
-
-  /// Returns a JSON-encoded representation of this manifest.
-  String toJson() {
-    List<Map<String, dynamic>> list = <Map<String, dynamic>>[];
-    _entries.forEach((ManifestEntry entry) => list.add(JsonBuilder()
-        .add('type', entry.type)
-        .add('body', entry.toJson())
-        .entry));
-    return const JsonEncoder.withIndent('  ').convert(list);
-  }
-}
diff --git a/lib/src/record_replay/manifest_entry.dart b/lib/src/record_replay/manifest_entry.dart
deleted file mode 100644
index 78d0314..0000000
--- a/lib/src/record_replay/manifest_entry.dart
+++ /dev/null
@@ -1,46 +0,0 @@
-import 'manifest.dart';
-import 'replay_process_manager.dart';
-
-/// An entry in the process invocation manifest.
-///
-/// Each entry in the [Manifest] represents a single recorded process
-/// invocation.
-abstract class ManifestEntry {
-  /// Whether this entry has been "invoked" by [ReplayProcessManager].
-  bool get invoked => _invoked;
-  bool _invoked = false;
-
-  /// Marks this entry as having been "invoked" by [ReplayProcessManager].
-  void setInvoked() {
-    _invoked = true;
-  }
-
-  /// The type of this [ManifestEntry].
-  String get type;
-
-  /// Returns a JSON-encodable representation of this manifest entry.
-  Map<String, dynamic> toJson();
-}
-
-/// A lightweight class that provides a means of building a manifest entry
-/// JSON object.
-class JsonBuilder {
-  /// The JSON-encodable object.
-  final Map<String, dynamic> entry = <String, dynamic>{};
-
-  /// Adds the specified key/value pair to the manifest entry iff the value
-  /// is non-null. If [jsonValue] is specified, its value will be used instead
-  /// of the raw value.
-  JsonBuilder add(String name, dynamic value, [dynamic jsonValue()]) {
-    if (value != null) {
-      entry[name] = jsonValue == null ? value : jsonValue();
-    }
-    return this;
-  }
-}
-
-/// Throws a [FormatException] if [data] does not contain [key].
-void checkRequiredField(Map<String, dynamic> data, String key) {
-  if (!data.containsKey(key))
-    throw FormatException('Required field missing: $key');
-}
diff --git a/lib/src/record_replay/recording_process_manager.dart b/lib/src/record_replay/recording_process_manager.dart
deleted file mode 100644
index a2e1c41..0000000
--- a/lib/src/record_replay/recording_process_manager.dart
+++ /dev/null
@@ -1,416 +0,0 @@
-// 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:convert';
-import 'dart:io' as io
-    show
-        IOSink,
-        Process,
-        ProcessResult,
-        ProcessSignal,
-        ProcessStartMode,
-        systemEncoding;
-
-import 'package:file/file.dart';
-import 'package:path/path.dart' as path;
-
-import '../interface/process_manager.dart';
-import 'can_run_manifest_entry.dart';
-import 'common.dart';
-import 'constants.dart';
-import 'manifest.dart';
-import 'replay_process_manager.dart';
-import 'run_manifest_entry.dart';
-
-/// Records process invocation activity and serializes it to disk.
-///
-/// A `RecordingProcessManager` decorates another `ProcessManager` instance by
-/// recording all process invocation activity (including the stdout and stderr
-/// of the associated processes) before delegating to the underlying manager.
-///
-/// This class enables "record / replay" tests, where you record the process
-/// invocation activity during a real program run, serialize the activity to
-/// disk, then fake all invocation activity during tests by replaying the
-/// serialized recording.
-///
-/// See also:
-///
-/// * [ReplayProcessManager].
-class RecordingProcessManager implements ProcessManager {
-  /// Constructs a new `RecordingProcessManager`.
-  ///
-  /// This manager will record all process invocations and serialize them to
-  /// the specified [destination]. The underlying `ProcessManager` functionality
-  /// will be delegated to [delegate].
-  ///
-  /// If [destination] does not already exist, or if it exists and is not empty,
-  /// a [StateError] will be thrown.
-  ///
-  /// [destination] should be treated as opaque. Its contents are intended to
-  /// be consumed only by [ReplayProcessManager] and are subject to change
-  /// between versions of `package:process`.
-  RecordingProcessManager(this.delegate, this.destination) {
-    if (!destination.existsSync() || destination.listSync().isNotEmpty) {
-      throw StateError('Cannot record to ${destination.path}');
-    }
-  }
-
-  static const List<String> _kSkippableExecutables = <String>[
-    'env',
-    'xcrun',
-  ];
-
-  /// The manager to which this manager delegates.
-  final ProcessManager delegate;
-
-  /// The directory to which serialized invocation metadata will be written.
-  final Directory destination;
-
-  /// List of invocation metadata. Will be serialized as [kManifestName].
-  final Manifest _manifest = Manifest();
-
-  /// Maps process IDs of running processes to exit code futures.
-  final Map<int, Future<int>> _runningProcesses = <int, Future<int>>{};
-
-  /// The file system in which this manager will create recording files.
-  FileSystem get fs => destination.fileSystem;
-
-  @override
-  Future<io.Process> start(
-    List<dynamic> command, {
-    String workingDirectory,
-    Map<String, String> environment,
-    bool includeParentEnvironment = true,
-    bool runInShell = false,
-    io.ProcessStartMode mode = io.ProcessStartMode.normal,
-  }) async {
-    io.Process process = await delegate.start(
-      command,
-      workingDirectory: workingDirectory,
-      environment: environment,
-      includeParentEnvironment: includeParentEnvironment,
-      runInShell: runInShell,
-      mode: mode,
-    );
-
-    List<String> sanitizedCommand = sanitize(command);
-    String basename = _getBasename(process.pid, sanitizedCommand);
-    RunManifestEntry entry = RunManifestEntry(
-      pid: process.pid,
-      basename: basename,
-      command: sanitizedCommand,
-      workingDirectory: workingDirectory,
-      environment: environment,
-      includeParentEnvironment: includeParentEnvironment,
-      runInShell: runInShell,
-      mode: mode,
-    );
-    _manifest.add(entry);
-
-    _RecordingProcess result = _RecordingProcess(
-      manager: this,
-      basename: basename,
-      delegate: process,
-    );
-    await result.startRecording();
-    _runningProcesses[process.pid] = result.exitCode.then((int exitCode) {
-      _runningProcesses.remove(process.pid);
-      entry.exitCode = exitCode;
-      return exitCode;
-    });
-
-    return result;
-  }
-
-  @override
-  Future<io.ProcessResult> run(
-    List<dynamic> command, {
-    String workingDirectory,
-    Map<String, String> environment,
-    bool includeParentEnvironment = true,
-    bool runInShell = false,
-    Encoding stdoutEncoding = io.systemEncoding,
-    Encoding stderrEncoding = io.systemEncoding,
-  }) async {
-    io.ProcessResult result = await delegate.run(
-      command,
-      workingDirectory: workingDirectory,
-      environment: environment,
-      includeParentEnvironment: includeParentEnvironment,
-      runInShell: runInShell,
-      stdoutEncoding: stdoutEncoding,
-      stderrEncoding: stderrEncoding,
-    );
-
-    List<String> sanitizedCommand = sanitize(command);
-    String basename = _getBasename(result.pid, sanitizedCommand);
-    _manifest.add(RunManifestEntry(
-      pid: result.pid,
-      basename: basename,
-      command: sanitizedCommand,
-      workingDirectory: workingDirectory,
-      environment: environment,
-      includeParentEnvironment: includeParentEnvironment,
-      runInShell: runInShell,
-      stdoutEncoding: stdoutEncoding,
-      stderrEncoding: stderrEncoding,
-      exitCode: result.exitCode,
-    ));
-
-    await _recordData(result.stdout, stdoutEncoding, '$basename.stdout');
-    await _recordData(result.stderr, stderrEncoding, '$basename.stderr');
-
-    return result;
-  }
-
-  Future<Null> _recordData(
-      dynamic data, Encoding encoding, String basename) async {
-    File file = fs.file('${destination.path}/$basename');
-    IOSink recording = file.openWrite(encoding: encoding);
-    try {
-      if (encoding == null)
-        recording.add(data as List<int>);
-      else
-        recording.write(data);
-      await recording.flush();
-    } finally {
-      await recording.close();
-    }
-  }
-
-  @override
-  io.ProcessResult runSync(
-    List<dynamic> command, {
-    String workingDirectory,
-    Map<String, String> environment,
-    bool includeParentEnvironment = true,
-    bool runInShell = false,
-    Encoding stdoutEncoding = io.systemEncoding,
-    Encoding stderrEncoding = io.systemEncoding,
-  }) {
-    io.ProcessResult result = delegate.runSync(
-      command,
-      workingDirectory: workingDirectory,
-      environment: environment,
-      includeParentEnvironment: includeParentEnvironment,
-      runInShell: runInShell,
-      stdoutEncoding: stdoutEncoding,
-      stderrEncoding: stderrEncoding,
-    );
-
-    List<String> sanitizedCommand = sanitize(command);
-    String basename = _getBasename(result.pid, sanitizedCommand);
-    _manifest.add(RunManifestEntry(
-      pid: result.pid,
-      basename: basename,
-      command: sanitizedCommand,
-      workingDirectory: workingDirectory,
-      environment: environment,
-      includeParentEnvironment: includeParentEnvironment,
-      runInShell: runInShell,
-      stdoutEncoding: stdoutEncoding,
-      stderrEncoding: stderrEncoding,
-      exitCode: result.exitCode,
-    ));
-
-    _recordDataSync(result.stdout, stdoutEncoding, '$basename.stdout');
-    _recordDataSync(result.stderr, stderrEncoding, '$basename.stderr');
-
-    return result;
-  }
-
-  void _recordDataSync(dynamic data, Encoding encoding, String basename) {
-    File file = fs.file('${destination.path}/$basename');
-    if (encoding == null)
-      file.writeAsBytesSync(data as List<int>, flush: true);
-    else
-      file.writeAsStringSync(data as String, encoding: encoding, flush: true);
-  }
-
-  @override
-  bool canRun(dynamic executable, {String workingDirectory}) {
-    bool result =
-        delegate.canRun(executable, workingDirectory: workingDirectory);
-    _manifest.add(
-        CanRunManifestEntry(executable: executable.toString(), result: result));
-    return result;
-  }
-
-  @override
-  bool killPid(int pid, [io.ProcessSignal signal = io.ProcessSignal.sigterm]) {
-    return delegate.killPid(pid, signal);
-  }
-
-  /// Returns a human-readable identifier for the specified executable.
-  String _getBasename(int pid, List<String> sanitizedCommand) {
-    String index = _manifest.length.toString();
-    String identifier = 'executable';
-    for (String element in sanitizedCommand) {
-      if (element.startsWith('-')) {
-        // Ignore flags.
-        continue;
-      }
-      identifier = path.basename(element);
-      if (!_kSkippableExecutables.contains(identifier)) {
-        break;
-      }
-    }
-    return '$index.$identifier.$pid';
-  }
-
-  /// Flushes pending data to [destination].
-  ///
-  /// This manager may buffer invocation metadata in memory as it sees fit.
-  /// Calling `flush` will force the manager to write any pending data to disk.
-  /// This returns a future that completes when all pending data has been
-  /// written to disk.
-  ///
-  /// Failure to call this method before the current process exits will likely
-  /// cause invocation data to be lost.
-  ///
-  /// If [finishRunningProcesses] is true, the returned future will only
-  /// complete after all running processes have exited, thus guaranteeing that
-  /// no new invocation data will be generated until new processes are invoked.
-  /// Any processes that don't exit on their own within the specified [timeout]
-  /// will be marked as daemon processes in the serialized metadata and will be
-  /// signalled with `SIGTERM`. If such processes *still* don't exit within the
-  /// specified [timeout] after being signalled, they'll be marked as not
-  /// responding in the serialized metadata.
-  ///
-  /// If [finishRunningProcesses] is false (the default), then [timeout] is
-  /// ignored.
-  Future<Null> flush({
-    bool finishRunningProcesses = false,
-    Duration timeout = const Duration(milliseconds: 20),
-  }) async {
-    if (finishRunningProcesses) {
-      await _waitForRunningProcessesToExit(timeout);
-    }
-    await _writeManifestToDisk();
-  }
-
-  /// Waits for all running processes to exit, and records their exit codes in
-  /// the process manifest. Any process that doesn't exit within [timeout]
-  /// will be marked as a [RunManifestEntry.daemon] and be signalled with
-  /// `SIGTERM`. If such processes *still* don't exit within [timeout] after
-  /// being signalled, they'll be marked as [RunManifestEntry.notResponding].
-  Future<Null> _waitForRunningProcessesToExit(Duration timeout) async {
-    await _waitForRunningProcessesToExitWithTimeout(
-        timeout: timeout,
-        onTimeout: (RunManifestEntry entry) {
-          entry.daemon = true;
-          delegate.killPid(entry.pid);
-        });
-    // Now that we explicitly signalled the processes that timed out asking
-    // them to shutdown, wait one more time for those processes to exit.
-    await _waitForRunningProcessesToExitWithTimeout(
-        timeout: timeout,
-        onTimeout: (RunManifestEntry entry) {
-          entry.notResponding = true;
-        });
-  }
-
-  Future<Null> _waitForRunningProcessesToExitWithTimeout({
-    Duration timeout,
-    void onTimeout(RunManifestEntry entry),
-  }) async {
-    void callOnTimeout(int pid) =>
-        onTimeout(_manifest.getRunEntry(pid) as RunManifestEntry);
-    await Future.wait(List<Future<int>>.from(_runningProcesses.values))
-        .timeout(timeout, onTimeout: () {
-      _runningProcesses.keys.forEach(callOnTimeout);
-      return null;
-    });
-  }
-
-  /// Writes our process invocation manifest to disk in the destination folder.
-  Future<Null> _writeManifestToDisk() async {
-    File manifestFile = fs.file('${destination.path}/$kManifestName');
-    await manifestFile.writeAsString(_manifest.toJson(), flush: true);
-  }
-}
-
-/// A [io.Process] implementation that records `stdout` and `stderr` stream events
-/// to disk before forwarding them on to the underlying stream listener.
-class _RecordingProcess implements io.Process {
-  _RecordingProcess({this.manager, this.basename, this.delegate});
-
-  final io.Process delegate;
-  final String basename;
-  final RecordingProcessManager manager;
-
-  // ignore: close_sinks
-  final StreamController<List<int>> _stdout = StreamController<List<int>>();
-  // ignore: close_sinks
-  final StreamController<List<int>> _stderr = StreamController<List<int>>();
-
-  bool _started = false;
-
-  Future<Null> startRecording() async {
-    assert(!_started);
-    _started = true;
-    await Future.wait(<Future<Null>>[
-      _recordStream(delegate.stdout, _stdout, 'stdout'),
-      _recordStream(delegate.stderr, _stderr, 'stderr'),
-    ]);
-  }
-
-  Future<Null> _recordStream(
-    Stream<List<int>> stream,
-    StreamController<List<int>> controller,
-    String suffix,
-  ) async {
-    String path = '${manager.destination.path}/$basename.$suffix';
-    File file = await manager.fs.file(path).create();
-    RandomAccessFile recording = await file.open(mode: FileMode.write);
-    stream.listen(
-      (List<int> data) {
-        // Write synchronously to guarantee that the order of data
-        // within our recording is preserved across stream notifications.
-        recording.writeFromSync(data);
-        // Flush immediately so that if the program crashes, forensic
-        // data from the recording won't be lost.
-        recording.flushSync();
-        controller.add(data);
-      },
-      onError: (dynamic error, StackTrace stackTrace) {
-        recording.closeSync();
-        controller.addError(error, stackTrace);
-      },
-      onDone: () {
-        recording.closeSync();
-        controller.close();
-      },
-    );
-  }
-
-  @override
-  Future<int> get exitCode => delegate.exitCode;
-
-  @override
-  Stream<List<int>> get stdout {
-    assert(_started);
-    return _stdout.stream;
-  }
-
-  @override
-  Stream<List<int>> get stderr {
-    assert(_started);
-    return _stderr.stream;
-  }
-
-  @override
-  io.IOSink get stdin {
-    // We don't currently support recording `stdin`.
-    return delegate.stdin;
-  }
-
-  @override
-  int get pid => delegate.pid;
-
-  @override
-  bool kill([io.ProcessSignal signal = io.ProcessSignal.sigterm]) =>
-      delegate.kill(signal);
-}
diff --git a/lib/src/record_replay/replay_process_manager.dart b/lib/src/record_replay/replay_process_manager.dart
deleted file mode 100644
index 53fdfeb..0000000
--- a/lib/src/record_replay/replay_process_manager.dart
+++ /dev/null
@@ -1,323 +0,0 @@
-// 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:convert';
-import 'dart:io' as io
-    show
-        IOSink,
-        Process,
-        ProcessException,
-        ProcessResult,
-        ProcessSignal,
-        ProcessStartMode,
-        systemEncoding;
-
-import 'package:file/file.dart';
-import 'package:path/path.dart' as path;
-
-import '../interface/process_manager.dart';
-import 'can_run_manifest_entry.dart';
-import 'common.dart';
-import 'constants.dart';
-import 'manifest.dart';
-import 'run_manifest_entry.dart';
-import 'recording_process_manager.dart';
-
-/// Fakes all process invocations by replaying a previously-recorded series
-/// of invocations.
-///
-/// Recordings exist as opaque directories that are produced by
-/// [RecordingProcessManager].
-class ReplayProcessManager implements ProcessManager {
-  ReplayProcessManager._(this._manifest, this.location, this.streamDelay);
-
-  final Manifest _manifest;
-
-  /// The location of the serialized recording that's driving this manager.
-  final Directory location;
-
-  /// If non-null, processes spawned by this manager will delay their
-  /// `stdout` and `stderr` stream production by the this amount. See
-  /// description of the associated parameter in [create].
-  final Duration streamDelay;
-
-  /// Creates a new `ReplayProcessManager` capable of replaying a recording that
-  /// was serialized to the specified [location] by [RecordingProcessManager].
-  ///
-  /// If [location] does not exist, or if it does not represent a valid
-  /// recording (as determined by [RecordingProcessManager]), an [ArgumentError]
-  /// will be thrown.
-  ///
-  /// If [streamDelay] is specified, processes spawned by this manager will
-  /// delay their `stdout` and `stderr` stream production by the specified
-  /// amount. This is useful in cases where the real process invocation had
-  /// a necessary delay in stream production, and you need to mirror that
-  /// behavior. e.g. you spawn a `tail` process to tail a log file, then in a
-  /// follow-on event loop, you invoke a `startServer` process, which starts
-  /// producing log output. In this case, you may need to delay the `tail`
-  /// output to prevent its stream from flushing all its content before you
-  /// start listening.
-  static Future<ReplayProcessManager> create(
-    Directory location, {
-    Duration streamDelay = Duration.zero,
-  }) async {
-    assert(streamDelay != null);
-
-    if (!location.existsSync()) {
-      throw ArgumentError.value(location.path, 'location', "Doesn't exist");
-    }
-
-    FileSystem fs = location.fileSystem;
-    File manifestFile = fs.file(path.join(location.path, kManifestName));
-    if (!manifestFile.existsSync()) {
-      throw ArgumentError.value(
-          location, 'location', 'Does not represent a valid recording');
-    }
-
-    String content = await manifestFile.readAsString();
-    try {
-      // We don't validate the existence of all stdout and stderr files
-      // referenced in the manifest.
-      Manifest manifest = Manifest.fromJson(content);
-      return ReplayProcessManager._(manifest, location, streamDelay);
-    } on FormatException catch (e) {
-      throw ArgumentError('$kManifestName is not a valid JSON file: $e');
-    }
-  }
-
-  @override
-  Future<io.Process> start(
-    List<dynamic> command, {
-    String workingDirectory,
-    Map<String, String> environment,
-    bool includeParentEnvironment = true,
-    bool runInShell = false,
-    io.ProcessStartMode mode = io.ProcessStartMode.normal,
-  }) async {
-    RunManifestEntry entry = _popRunEntry(command, mode: mode);
-    _ReplayResult result = await _ReplayResult.create(this, entry);
-    return result.asProcess(entry.daemon);
-  }
-
-  @override
-  Future<io.ProcessResult> run(
-    List<dynamic> command, {
-    String workingDirectory,
-    Map<String, String> environment,
-    bool includeParentEnvironment = true,
-    bool runInShell = false,
-    Encoding stdoutEncoding = io.systemEncoding,
-    Encoding stderrEncoding = io.systemEncoding,
-  }) async {
-    RunManifestEntry entry = _popRunEntry(command,
-        stdoutEncoding: stdoutEncoding, stderrEncoding: stderrEncoding);
-    return await _ReplayResult.create(this, entry);
-  }
-
-  @override
-  io.ProcessResult runSync(
-    List<dynamic> command, {
-    String workingDirectory,
-    Map<String, String> environment,
-    bool includeParentEnvironment = true,
-    bool runInShell = false,
-    Encoding stdoutEncoding = io.systemEncoding,
-    Encoding stderrEncoding = io.systemEncoding,
-  }) {
-    RunManifestEntry entry = _popRunEntry(command,
-        stdoutEncoding: stdoutEncoding, stderrEncoding: stderrEncoding);
-    return _ReplayResult.createSync(this, entry);
-  }
-
-  /// Finds and returns the next entry in the process manifest that matches
-  /// the specified process arguments. Once found, it marks the manifest entry
-  /// as having been invoked and thus not eligible for invocation again.
-  RunManifestEntry _popRunEntry(
-    List<dynamic> command, {
-    io.ProcessStartMode mode,
-    Encoding stdoutEncoding,
-    Encoding stderrEncoding,
-  }) {
-    List<String> sanitizedCommand = sanitize(command);
-    RunManifestEntry entry = _manifest.findPendingRunEntry(
-      command: sanitizedCommand,
-      mode: mode,
-      stdoutEncoding: stdoutEncoding,
-      stderrEncoding: stderrEncoding,
-    ) as RunManifestEntry;
-
-    if (entry == null) {
-      throw io.ProcessException(sanitizedCommand.first,
-          sanitizedCommand.skip(1).toList(), 'No matching invocation found');
-    }
-
-    entry.setInvoked();
-    return entry;
-  }
-
-  @override
-  bool canRun(dynamic executable, {String workingDirectory}) {
-    CanRunManifestEntry entry = _manifest.findPendingCanRunEntry(
-      executable: executable.toString(),
-    ) as CanRunManifestEntry;
-    if (entry == null) {
-      throw ArgumentError('No matching invocation found for $executable');
-    }
-    entry.setInvoked();
-    return entry.result;
-  }
-
-  @override
-  bool killPid(int pid, [io.ProcessSignal signal = io.ProcessSignal.sigterm]) {
-    throw UnsupportedError(
-        "$runtimeType.killPid() has not been implemented because at the time "
-        "of its writing, it wasn't needed. If you're hitting this error, you "
-        "should implement it.");
-  }
-}
-
-/// A [io.ProcessResult] implementation that derives its data from a recording
-/// fragment.
-class _ReplayResult implements io.ProcessResult {
-  _ReplayResult._({
-    this.manager,
-    this.pid,
-    this.exitCode,
-    this.stdout,
-    this.stderr,
-  });
-
-  final ReplayProcessManager manager;
-
-  @override
-  final int pid;
-
-  @override
-  final int exitCode;
-
-  @override
-  final dynamic stdout;
-
-  @override
-  final dynamic stderr;
-
-  static Future<_ReplayResult> create(
-    ReplayProcessManager manager,
-    RunManifestEntry entry,
-  ) async {
-    FileSystem fs = manager.location.fileSystem;
-    String basePath = path.join(manager.location.path, entry.basename);
-    try {
-      return _ReplayResult._(
-        manager: manager,
-        pid: entry.pid,
-        exitCode: entry.exitCode,
-        stdout: await _getData(fs, '$basePath.stdout', entry.stdoutEncoding),
-        stderr: await _getData(fs, '$basePath.stderr', entry.stderrEncoding),
-      );
-    } catch (e) {
-      throw io.ProcessException(
-          entry.executable, entry.arguments, e.toString());
-    }
-  }
-
-  static Future<dynamic> _getData(
-      FileSystem fs, String path, Encoding encoding) async {
-    File file = fs.file(path);
-    return encoding == null
-        ? await file.readAsBytes()
-        : await file.readAsString(encoding: encoding);
-  }
-
-  static _ReplayResult createSync(
-    ReplayProcessManager manager,
-    RunManifestEntry entry,
-  ) {
-    FileSystem fs = manager.location.fileSystem;
-    String basePath = path.join(manager.location.path, entry.basename);
-    try {
-      return _ReplayResult._(
-        manager: manager,
-        pid: entry.pid,
-        exitCode: entry.exitCode,
-        stdout: _getDataSync(fs, '$basePath.stdout', entry.stdoutEncoding),
-        stderr: _getDataSync(fs, '$basePath.stderr', entry.stderrEncoding),
-      );
-    } catch (e) {
-      throw io.ProcessException(
-          entry.executable, entry.arguments, e.toString());
-    }
-  }
-
-  static dynamic _getDataSync(FileSystem fs, String path, Encoding encoding) {
-    File file = fs.file(path);
-    return encoding == null
-        ? file.readAsBytesSync()
-        : file.readAsStringSync(encoding: encoding);
-  }
-
-  io.Process asProcess(bool daemon) {
-    assert(stdout is List<int>);
-    assert(stderr is List<int>);
-    return _ReplayProcess(this, daemon);
-  }
-}
-
-/// A [io.Process] implementation derives its data from a recording fragment.
-class _ReplayProcess implements io.Process {
-  _ReplayProcess(_ReplayResult result, bool daemon)
-      : pid = result.pid,
-        _stdout = result.stdout as List<int>,
-        _stderr = result.stderr as List<int>,
-        _stdoutController = StreamController<List<int>>(),
-        _stderrController = StreamController<List<int>>(),
-        _exitCode = result.exitCode,
-        _exitCodeCompleter = Completer<int>() {
-    // Don't flush our stdio streams until we at least reach the outer event
-    // loop. i.e. even if `streamDelay` is zero, we still want to use the timer.
-    Timer(result.manager.streamDelay, () {
-      if (!_stdoutController.isClosed) {
-        _stdoutController.add(_stdout);
-      }
-      if (!_stderrController.isClosed) {
-        _stderrController.add(_stderr);
-      }
-      if (!daemon) kill();
-    });
-  }
-
-  @override
-  final int pid;
-
-  final List<int> _stdout;
-  final List<int> _stderr;
-  final StreamController<List<int>> _stdoutController;
-  final StreamController<List<int>> _stderrController;
-  final int _exitCode;
-  final Completer<int> _exitCodeCompleter;
-
-  @override
-  Stream<List<int>> get stdout => _stdoutController.stream;
-
-  @override
-  Stream<List<int>> get stderr => _stderrController.stream;
-
-  @override
-  Future<int> get exitCode => _exitCodeCompleter.future;
-
-  @override
-  io.IOSink get stdin => throw UnimplementedError();
-
-  @override
-  bool kill([io.ProcessSignal signal = io.ProcessSignal.sigterm]) {
-    if (!_exitCodeCompleter.isCompleted) {
-      _stdoutController.close();
-      _stderrController.close();
-      _exitCodeCompleter.complete(_exitCode);
-      return true;
-    }
-    return false;
-  }
-}
diff --git a/lib/src/record_replay/run_manifest_entry.dart b/lib/src/record_replay/run_manifest_entry.dart
deleted file mode 100644
index a3dfd35..0000000
--- a/lib/src/record_replay/run_manifest_entry.dart
+++ /dev/null
@@ -1,149 +0,0 @@
-// 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:convert';
-import 'dart:io' show ProcessStartMode, systemEncoding;
-
-import 'manifest_entry.dart';
-
-/// Gets a `ProcessStartMode` value by its string name.
-ProcessStartMode _getProcessStartMode(String value) {
-  if (value != null) {
-    for (ProcessStartMode mode in ProcessStartMode.values) {
-      if (mode.toString() == value) {
-        return mode;
-      }
-    }
-    throw FormatException('Invalid value for mode: $value');
-  }
-  return null;
-}
-
-/// Gets an `Encoding` instance by the encoding name.
-Encoding _getEncoding(String encoding) {
-  if (encoding == 'system') {
-    return systemEncoding;
-  } else if (encoding != null) {
-    return Encoding.getByName(encoding);
-  }
-  return null;
-}
-
-/// An entry in the process invocation manifest for running an executable.
-class RunManifestEntry extends ManifestEntry {
-  /// Creates a new manifest entry with the given properties.
-  RunManifestEntry({
-    this.pid,
-    this.basename,
-    this.command,
-    this.workingDirectory,
-    this.environment,
-    this.includeParentEnvironment,
-    this.runInShell,
-    this.mode,
-    this.stdoutEncoding,
-    this.stderrEncoding,
-    this.exitCode,
-  });
-
-  /// Creates a new manifest entry populated with the specified JSON [data].
-  ///
-  /// If any required fields are missing from the JSON data, this will throw
-  /// a [FormatException].
-  factory RunManifestEntry.fromJson(Map<String, dynamic> data) {
-    checkRequiredField(data, 'pid');
-    checkRequiredField(data, 'basename');
-    checkRequiredField(data, 'command');
-    RunManifestEntry entry = RunManifestEntry(
-      pid: data['pid'] as int,
-      basename: data['basename'] as String,
-      command: (data['command'] as List<dynamic>)?.cast<String>(),
-      workingDirectory: data['workingDirectory'] as String,
-      environment: (data['environment'] as Map<dynamic, dynamic>)
-          ?.cast<String, String>(),
-      includeParentEnvironment: data['includeParentEnvironment'] as bool,
-      runInShell: data['runInShell'] as bool,
-      mode: _getProcessStartMode(data['mode'] as String),
-      stdoutEncoding: _getEncoding(data['stdoutEncoding'] as String),
-      stderrEncoding: _getEncoding(data['stderrEncoding'] as String),
-      exitCode: data['exitCode'] as int,
-    );
-    entry.daemon = data['daemon'] as bool;
-    entry.notResponding = data['notResponding'] as bool;
-    return entry;
-  }
-
-  @override
-  final String type = 'run';
-
-  /// The process id.
-  final int pid;
-
-  /// The base file name for this entry. `stdout` and `stderr` files for this
-  /// process will be serialized in the recording directory as
-  /// `$basename.stdout` and `$basename.stderr`, respectively.
-  final String basename;
-
-  /// The command that was run. The first element is the executable, and the
-  /// remaining elements are the arguments to the executable.
-  final List<String> command;
-
-  /// The process' working directory when it was spawned.
-  final String workingDirectory;
-
-  /// The environment variables that were passed to the process.
-  final Map<String, String> environment;
-
-  /// Whether the invoker's environment was made available to the process.
-  final bool includeParentEnvironment;
-
-  /// Whether the process was spawned through a system shell.
-  final bool runInShell;
-
-  /// The mode with which the process was spawned.
-  final ProcessStartMode mode;
-
-  /// The encoding used for the `stdout` of the process.
-  final Encoding stdoutEncoding;
-
-  /// The encoding used for the `stderr` of the process.
-  final Encoding stderrEncoding;
-
-  /// The exit code of the process.
-  int exitCode;
-
-  /// The executable that was invoked.
-  String get executable => command.first;
-
-  /// The arguments that were passed to [executable].
-  List<String> get arguments => command.skip(1).toList();
-
-  /// Indicates that the process is a daemon.
-  bool get daemon => _daemon;
-  bool _daemon = false;
-  set daemon(bool value) => _daemon = value ?? false;
-
-  /// Indicates that the process did not respond to `SIGTERM`.
-  bool get notResponding => _notResponding;
-  bool _notResponding = false;
-  set notResponding(bool value) => _notResponding = value ?? false;
-
-  /// Returns a JSON-encodable representation of this manifest entry.
-  @override
-  Map<String, dynamic> toJson() => JsonBuilder()
-      .add('pid', pid)
-      .add('basename', basename)
-      .add('command', command)
-      .add('workingDirectory', workingDirectory)
-      .add('environment', environment)
-      .add('includeParentEnvironment', includeParentEnvironment)
-      .add('runInShell', runInShell)
-      .add('mode', mode, () => mode.toString())
-      .add('stdoutEncoding', stdoutEncoding, () => stdoutEncoding.name)
-      .add('stderrEncoding', stderrEncoding, () => stderrEncoding.name)
-      .add('daemon', daemon)
-      .add('notResponding', notResponding)
-      .add('exitCode', exitCode)
-      .entry;
-}
diff --git a/pubspec.yaml b/pubspec.yaml
index b6a7362..b25b238 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -5,7 +5,6 @@
 
 dependencies:
   file: '^5.0.0'
-  meta: ^1.1.2
   path: ^1.5.1
   platform: '>=1.0.1'
 
@@ -13,4 +12,4 @@
   test: ^1.0.0
 
 environment:
-  sdk: '>=2.0.0 <3.0.0'
+  sdk: '>=2.9.0 <3.0.0'
diff --git a/test/record_test.dart b/test/record_test.dart
deleted file mode 100644
index 6f827df..0000000
--- a/test/record_test.dart
+++ /dev/null
@@ -1,174 +0,0 @@
-// 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:convert';
-import 'dart:io' show Platform, Process, ProcessResult, systemEncoding;
-
-import 'package:file/file.dart';
-import 'package:file/local.dart';
-import 'package:path/path.dart' as p;
-import 'package:process/process.dart';
-import 'package:process/record_replay.dart';
-import 'package:test/test.dart';
-
-import 'utils.dart';
-
-void main() {
-  FileSystem fs = LocalFileSystem();
-  // TODO(goderbauer): refactor when github.com/google/platform.dart/issues/1
-  //     is available.
-  String newline = Platform.isWindows ? '\r\n' : '\n';
-
-  group('RecordingProcessManager', () {
-    Directory tmp;
-    RecordingProcessManager manager;
-
-    setUp(() {
-      tmp = fs.systemTempDirectory.createTempSync('process_tests_');
-      manager = RecordingProcessManager(LocalProcessManager(), tmp);
-    });
-
-    tearDown(() {
-      tmp.deleteSync(recursive: true);
-    });
-
-    test('start', () async {
-      Process process =
-          await manager.start(<String>['echo', 'foo'], runInShell: true);
-      int pid = process.pid;
-      int exitCode = await process.exitCode;
-      List<int> stdout = await consume(process.stdout);
-      List<int> stderr = await consume(process.stderr);
-      expect(exitCode, 0);
-      expect(decode(stdout), <String>['foo']);
-      expect(stderr, isEmpty);
-
-      // Force the recording to be written to disk.
-      await manager.flush(finishRunningProcesses: true);
-
-      _Recording recording = _Recording(tmp);
-      expect(recording.manifest, hasLength(1));
-      Map<String, dynamic> entry = recording.manifest.first;
-      expect(entry['type'], 'run');
-      Map<String, dynamic> body = entry['body'] as Map<String, dynamic>;
-      expect(body['pid'], pid);
-      expect(body['command'], <String>['echo', 'foo']);
-      expect(body['mode'], 'normal');
-      expect(body['exitCode'], exitCode);
-      expect(recording.stdoutForEntryAt(0), stdout);
-      expect(recording.stderrForEntryAt(0), stderr);
-    });
-
-    test('run', () async {
-      ProcessResult result =
-          await manager.run(<String>['echo', 'bar'], runInShell: true);
-      int pid = result.pid;
-      int exitCode = result.exitCode;
-      String stdout = result.stdout as String;
-      String stderr = result.stderr as String;
-      expect(exitCode, 0);
-      expect(stdout, 'bar$newline');
-      expect(stderr, isEmpty);
-
-      // Force the recording to be written to disk.
-      await manager.flush(finishRunningProcesses: true);
-
-      _Recording recording = _Recording(tmp);
-      expect(recording.manifest, hasLength(1));
-      Map<String, dynamic> entry = recording.manifest.first;
-      expect(entry['type'], 'run');
-      Map<String, dynamic> body = entry['body'] as Map<String, dynamic>;
-      expect(body['pid'], pid);
-      expect(body['command'], <String>['echo', 'bar']);
-      expect(body['stdoutEncoding'], 'system');
-      expect(body['stderrEncoding'], 'system');
-      expect(body['exitCode'], exitCode);
-      expect(recording.stdoutForEntryAt(0), stdout);
-      expect(recording.stderrForEntryAt(0), stderr);
-    });
-
-    test('runSync', () async {
-      ProcessResult result =
-          manager.runSync(<String>['echo', 'baz'], runInShell: true);
-      int pid = result.pid;
-      int exitCode = result.exitCode;
-      String stdout = result.stdout as String;
-      String stderr = result.stderr as String;
-      expect(exitCode, 0);
-      expect(stdout, 'baz$newline');
-      expect(stderr, isEmpty);
-
-      // Force the recording to be written to disk.
-      await manager.flush(finishRunningProcesses: true);
-
-      _Recording recording = _Recording(tmp);
-      expect(recording.manifest, hasLength(1));
-      Map<String, dynamic> entry = recording.manifest.first;
-      expect(entry['type'], 'run');
-      Map<String, dynamic> body = entry['body'] as Map<String, dynamic>;
-      expect(body['pid'], pid);
-      expect(body['command'], <String>['echo', 'baz']);
-      expect(body['stdoutEncoding'], 'system');
-      expect(body['stderrEncoding'], 'system');
-      expect(body['exitCode'], exitCode);
-      expect(recording.stdoutForEntryAt(0), stdout);
-      expect(recording.stderrForEntryAt(0), stderr);
-    });
-
-    test('canRun', () async {
-      String executable = p.join(tmp.path, 'bla.exe');
-      fs.file(executable).createSync();
-
-      bool result = manager.canRun(executable);
-
-      // Force the recording to be written to disk.
-      await manager.flush(finishRunningProcesses: true);
-
-      _Recording recording = _Recording(tmp);
-      expect(recording.manifest, hasLength(1));
-      Map<String, dynamic> entry = recording.manifest.first;
-      expect(entry['type'], 'can_run');
-      Map<String, dynamic> body = entry['body'] as Map<String, dynamic>;
-      expect(body['executable'], executable);
-      expect(body['result'], result);
-    });
-  });
-}
-
-/// A testing utility class that encapsulates a recording.
-class _Recording {
-  _Recording(this.dir);
-  final Directory dir;
-
-  List<Map<String, dynamic>> get manifest {
-    return (json.decoder
-                .convert(_getFileContent('MANIFEST.txt', utf8) as String)
-            as List<dynamic>)
-        .cast<Map<String, dynamic>>();
-  }
-
-  dynamic stdoutForEntryAt(int index) => _getStdioContent(
-      manifest[index]['body'] as Map<String, dynamic>, 'stdout');
-
-  dynamic stderrForEntryAt(int index) => _getStdioContent(
-      manifest[index]['body'] as Map<String, dynamic>, 'stderr');
-
-  dynamic _getFileContent(String name, Encoding encoding) {
-    File file = dir.fileSystem.file('${dir.path}/$name');
-    return encoding == null
-        ? file.readAsBytesSync()
-        : file.readAsStringSync(encoding: encoding);
-  }
-
-  dynamic _getStdioContent(Map<String, dynamic> entry, String type) {
-    String basename = entry['basename'] as String;
-    String encodingName = entry['${type}Encoding'] as String;
-    Encoding encoding;
-    if (encodingName != null)
-      encoding = encodingName == 'system'
-          ? systemEncoding
-          : Encoding.getByName(encodingName);
-    return _getFileContent('$basename.$type', encoding);
-  }
-}
diff --git a/test/replay_test.dart b/test/replay_test.dart
deleted file mode 100644
index 045f72d..0000000
--- a/test/replay_test.dart
+++ /dev/null
@@ -1,60 +0,0 @@
-// 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' show Process, ProcessResult;
-
-import 'package:file/file.dart';
-import 'package:file/local.dart';
-import 'package:process/process.dart';
-import 'package:process/record_replay.dart';
-import 'package:test/test.dart';
-
-import 'utils.dart';
-
-void main() {
-  FileSystem fs = LocalFileSystem();
-
-  group('ReplayProcessManager', () {
-    ProcessManager manager;
-
-    setUp(() async {
-      Directory dir = fs.directory('test/data/replay');
-      manager = await ReplayProcessManager.create(dir);
-    });
-
-    test('start', () async {
-      Process process = await manager.start(<String>['sing', 'ppap']);
-      int exitCode = await process.exitCode;
-      List<int> stdout = await consume(process.stdout);
-      List<int> stderr = await consume(process.stderr);
-      expect(process.pid, 100);
-      expect(exitCode, 0);
-      expect(decode(stdout), <String>['I have a pen', 'I have a pineapple']);
-      expect(decode(stderr), <String>['Uh, pineapple pen']);
-    });
-
-    test('run', () async {
-      ProcessResult result =
-          await manager.run(<String>['dance', 'gangnam-style']);
-      expect(result.pid, 101);
-      expect(result.exitCode, 2);
-      expect(result.stdout, '');
-      expect(result.stderr, 'No one can dance like Psy\n');
-    });
-
-    test('runSync', () {
-      ProcessResult result =
-          manager.runSync(<String>['dance', 'gangnam-style']);
-      expect(result.pid, 101);
-      expect(result.exitCode, 2);
-      expect(result.stdout, '');
-      expect(result.stderr, 'No one can dance like Psy\n');
-    });
-
-    test('canRun', () {
-      bool result = manager.canRun('marathon');
-      expect(result, true);
-    });
-  });
-}