Move common process/IO-related mocks to mocks.dart (#14255)

Moves MockProcess, MockStdio and a few other useful mocks from
packages_test.dart to common/mocks.dart. These are useful for testing
code with interactive IO.

This adds a new constructor to MockProcess to provide additional flexibility.
diff --git a/packages/flutter_tools/test/commands/packages_test.dart b/packages/flutter_tools/test/commands/packages_test.dart
index a3f9be3..86bbc28 100644
--- a/packages/flutter_tools/test/commands/packages_test.dart
+++ b/packages/flutter_tools/test/commands/packages_test.dart
@@ -3,8 +3,6 @@
 // found in the LICENSE file.
 
 import 'dart:async';
-import 'dart:convert';
-import 'dart:io' show IOSink;
 
 import 'package:args/command_runner.dart';
 import 'package:flutter_tools/src/base/file_system.dart' hide IOSink;
@@ -16,6 +14,7 @@
 
 import '../src/common.dart';
 import '../src/context.dart';
+import '../src/mocks.dart' show MockProcessManager, MockStdio, PromptingProcess;
 
 void main() {
   Cache.disableLocking();
@@ -130,180 +129,3 @@
     });
   });
 }
-
-/// A strategy for creating Process objects from a list of commands.
-typedef Process ProcessFactory(List<String> command);
-
-/// A ProcessManager that starts Processes by delegating to a ProcessFactory.
-class MockProcessManager implements ProcessManager {
-  ProcessFactory processFactory = (List<String> commands) => new MockProcess();
-  List<String> commands;
-
-  @override
-  Future<Process> start(
-    List<dynamic> command, {
-    String workingDirectory,
-    Map<String, String> environment,
-    bool includeParentEnvironment: true,
-    bool runInShell: false,
-    ProcessStartMode mode: ProcessStartMode.NORMAL,
-  }) {
-    commands = command;
-    return new Future<Process>.value(processFactory(command));
-  }
-
-  @override
-  dynamic noSuchMethod(Invocation invocation) => null;
-}
-
-/// A process that prompts the user to proceed, then asynchronously writes
-/// some lines to stdout before it exits.
-class PromptingProcess implements Process {
-  Future<Null> showPrompt(String prompt, List<String> outputLines) async {
-    _stdoutController.add(UTF8.encode(prompt));
-    final List<int> bytesOnStdin = await _stdin.future;
-    // Echo stdin to stdout.
-    _stdoutController.add(bytesOnStdin);
-    if (bytesOnStdin[0] == UTF8.encode('y')[0]) {
-      for (final String line in outputLines)
-        _stdoutController.add(UTF8.encode('$line\n'));
-    }
-    await _stdoutController.close();
-  }
-
-  final StreamController<List<int>> _stdoutController = new StreamController<List<int>>();
-  final CompleterIOSink _stdin = new CompleterIOSink();
-
-  @override
-  Stream<List<int>> get stdout => _stdoutController.stream;
-
-  @override
-  Stream<List<int>> get stderr => const Stream<List<int>>.empty();
-
-  @override
-  IOSink get stdin => _stdin;
-
-  @override
-  Future<int> get exitCode async {
-    await _stdoutController.done;
-    return 0;
-  }
-
-  @override
-  dynamic noSuchMethod(Invocation invocation) => null;
-}
-
-/// An inactive process that collects stdin and produces no output.
-class MockProcess implements Process {
-  final IOSink _stdin = new MemoryIOSink();
-
-  @override
-  Stream<List<int>> get stdout => const Stream<List<int>>.empty();
-
-  @override
-  Stream<List<int>> get stderr => const Stream<List<int>>.empty();
-
-  @override
-  IOSink get stdin => _stdin;
-
-  @override
-  Future<int> get exitCode => new Future<int>.value(0);
-
-  @override
-  dynamic noSuchMethod(Invocation invocation) => null;
-}
-
-/// An IOSink that completes a future with the first line written to it.
-class CompleterIOSink extends MemoryIOSink {
-  final Completer<List<int>> _completer = new Completer<List<int>>();
-
-  Future<List<int>> get future => _completer.future;
-
-  @override
-  void add(List<int> data) {
-    if (!_completer.isCompleted)
-      _completer.complete(data);
-    super.add(data);
-  }
-}
-
-/// A Stdio that collects stdout and supports simulated stdin.
-class MockStdio extends Stdio {
-  final MemoryIOSink _stdout = new MemoryIOSink();
-  final StreamController<List<int>> _stdin = new StreamController<List<int>>();
-
-  @override
-  IOSink get stdout => _stdout;
-
-  @override
-  Stream<List<int>> get stdin => _stdin.stream;
-
-  void simulateStdin(String line) {
-    _stdin.add(UTF8.encode('$line\n'));
-  }
-
-  List<String> get writtenToStdout => _stdout.writes.map(_stdout.encoding.decode).toList();
-}
-
-/// An IOSink that collects whatever is written to it.
-class MemoryIOSink implements IOSink {
-  @override
-  Encoding encoding = UTF8;
-
-  final List<List<int>> writes = <List<int>>[];
-
-  @override
-  void add(List<int> data) {
-    writes.add(data);
-  }
-
-  @override
-  Future<Null> addStream(Stream<List<int>> stream) {
-    final Completer<Null> completer = new Completer<Null>();
-    stream.listen((List<int> data) {
-      add(data);
-    }).onDone(() => completer.complete(null));
-    return completer.future;
-  }
-
-  @override
-  void writeCharCode(int charCode) {
-    add(<int>[charCode]);
-  }
-
-  @override
-  void write(Object obj) {
-    add(encoding.encode('$obj'));
-  }
-
-  @override
-  void writeln([Object obj = '']) {
-    add(encoding.encode('$obj\n'));
-  }
-
-  @override
-  void writeAll(Iterable<dynamic> objects, [String separator = '']) {
-    bool addSeparator = false;
-    for (dynamic object in objects) {
-      if (addSeparator) {
-        write(separator);
-      }
-      write(object);
-      addSeparator = true;
-    }
-  }
-
-  @override
-  void addError(dynamic error, [StackTrace stackTrace]) {
-    throw new UnimplementedError();
-  }
-
-  @override
-  Future<Null> get done => close();
-
-  @override
-  Future<Null> close() async => null;
-
-  @override
-  Future<Null> flush() async => null;
-}
diff --git a/packages/flutter_tools/test/src/mocks.dart b/packages/flutter_tools/test/src/mocks.dart
index acf5352..657a8d0 100644
--- a/packages/flutter_tools/test/src/mocks.dart
+++ b/packages/flutter_tools/test/src/mocks.dart
@@ -3,9 +3,12 @@
 // found in the LICENSE file.
 
 import 'dart:async';
+import 'dart:convert';
+import 'dart:io' as io show IOSink;
 
 import 'package:flutter_tools/src/android/android_device.dart';
 import 'package:flutter_tools/src/application_package.dart';
+import 'package:flutter_tools/src/base/io.dart';
 import 'package:flutter_tools/src/build_info.dart';
 import 'package:flutter_tools/src/devfs.dart';
 import 'package:flutter_tools/src/device.dart';
@@ -13,6 +16,7 @@
 import 'package:flutter_tools/src/ios/simulators.dart';
 import 'package:flutter_tools/src/runner/flutter_command.dart';
 import 'package:mockito/mockito.dart';
+import 'package:process/process.dart';
 import 'package:test/test.dart';
 
 class MockApplicationPackageStore extends ApplicationPackageStore {
@@ -29,6 +33,190 @@
   );
 }
 
+/// A strategy for creating Process objects from a list of commands.
+typedef Process ProcessFactory(List<String> command);
+
+/// A ProcessManager that starts Processes by delegating to a ProcessFactory.
+class MockProcessManager implements ProcessManager {
+  ProcessFactory processFactory = (List<String> commands) => new MockProcess();
+  List<String> commands;
+
+  @override
+  Future<Process> start(
+    List<dynamic> command, {
+    String workingDirectory,
+    Map<String, String> environment,
+    bool includeParentEnvironment: true,
+    bool runInShell: false,
+    ProcessStartMode mode: ProcessStartMode.NORMAL,
+  }) {
+    commands = command;
+    return new Future<Process>.value(processFactory(command));
+  }
+
+  @override
+  dynamic noSuchMethod(Invocation invocation) => null;
+}
+
+/// A process that exits successfully with no output and ignores all input.
+class MockProcess extends Mock implements Process {
+  MockProcess({
+    this.pid: 1,
+    Future<int> exitCode,
+    Stream<List<int>> stdin,
+    this.stdout: const Stream<List<int>>.empty(),
+    this.stderr: const Stream<List<int>>.empty(),
+  }) : exitCode = exitCode ?? new Future<int>.value(0),
+       stdin = stdin ?? new MemoryIOSink();
+
+  @override
+  final int pid;
+
+  @override
+  final Future<int> exitCode;
+
+  @override
+  final io.IOSink stdin;
+
+  @override
+  final Stream<List<int>> stdout;
+
+  @override
+  final Stream<List<int>> stderr;
+}
+
+/// A process that prompts the user to proceed, then asynchronously writes
+/// some lines to stdout before it exits.
+class PromptingProcess implements Process {
+  Future<Null> showPrompt(String prompt, List<String> outputLines) async {
+    _stdoutController.add(UTF8.encode(prompt));
+    final List<int> bytesOnStdin = await _stdin.future;
+    // Echo stdin to stdout.
+    _stdoutController.add(bytesOnStdin);
+    if (bytesOnStdin[0] == UTF8.encode('y')[0]) {
+      for (final String line in outputLines)
+        _stdoutController.add(UTF8.encode('$line\n'));
+    }
+    await _stdoutController.close();
+  }
+
+  final StreamController<List<int>> _stdoutController = new StreamController<List<int>>();
+  final CompleterIOSink _stdin = new CompleterIOSink();
+
+  @override
+  Stream<List<int>> get stdout => _stdoutController.stream;
+
+  @override
+  Stream<List<int>> get stderr => const Stream<List<int>>.empty();
+
+  @override
+  IOSink get stdin => _stdin;
+
+  @override
+  Future<int> get exitCode async {
+    await _stdoutController.done;
+    return 0;
+  }
+
+  @override
+  dynamic noSuchMethod(Invocation invocation) => null;
+}
+
+/// An IOSink that completes a future with the first line written to it.
+class CompleterIOSink extends MemoryIOSink {
+  final Completer<List<int>> _completer = new Completer<List<int>>();
+
+  Future<List<int>> get future => _completer.future;
+
+  @override
+  void add(List<int> data) {
+    if (!_completer.isCompleted)
+      _completer.complete(data);
+    super.add(data);
+  }
+}
+
+/// An IOSink that collects whatever is written to it.
+class MemoryIOSink implements IOSink {
+  @override
+  Encoding encoding = UTF8;
+
+  final List<List<int>> writes = <List<int>>[];
+
+  @override
+  void add(List<int> data) {
+    writes.add(data);
+  }
+
+  @override
+  Future<Null> addStream(Stream<List<int>> stream) {
+    final Completer<Null> completer = new Completer<Null>();
+    stream.listen((List<int> data) {
+      add(data);
+    }).onDone(() => completer.complete(null));
+    return completer.future;
+  }
+
+  @override
+  void writeCharCode(int charCode) {
+    add(<int>[charCode]);
+  }
+
+  @override
+  void write(Object obj) {
+    add(encoding.encode('$obj'));
+  }
+
+  @override
+  void writeln([Object obj = '']) {
+    add(encoding.encode('$obj\n'));
+  }
+
+  @override
+  void writeAll(Iterable<dynamic> objects, [String separator = '']) {
+    bool addSeparator = false;
+    for (dynamic object in objects) {
+      if (addSeparator) {
+        write(separator);
+      }
+      write(object);
+      addSeparator = true;
+    }
+  }
+
+  @override
+  void addError(dynamic error, [StackTrace stackTrace]) {
+    throw new UnimplementedError();
+  }
+
+  @override
+  Future<Null> get done => close();
+
+  @override
+  Future<Null> close() async => null;
+
+  @override
+  Future<Null> flush() async => null;
+}
+
+/// A Stdio that collects stdout and supports simulated stdin.
+class MockStdio extends Stdio {
+  final MemoryIOSink _stdout = new MemoryIOSink();
+  final StreamController<List<int>> _stdin = new StreamController<List<int>>();
+
+  @override
+  IOSink get stdout => _stdout;
+
+  @override
+  Stream<List<int>> get stdin => _stdin.stream;
+
+  void simulateStdin(String line) {
+    _stdin.add(UTF8.encode('$line\n'));
+  }
+
+  List<String> get writtenToStdout => _stdout.writes.map(_stdout.encoding.decode).toList();
+}
+
 class MockPollingDeviceDiscovery extends PollingDeviceDiscovery {
   final List<Device> _devices = <Device>[];
   final StreamController<Device> _onAddedController = new StreamController<Device>.broadcast();