Filter out '\n' from terminal input. (#10220)

* Remove '\n' from terminal input.

* Use trim instead of replaceAll

* Add unit test

* Cleanup the test

* Fixed lint

* Style adjustments

* Forgotten @override

* Revert "Forgotten @override"

Accidently added extra files.

This reverts commit 0aba24fc8ea321b3a4d0cd8aed7f589378393d96.

* Just @override change
diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart
index 9194e17..300d4ff 100644
--- a/packages/flutter_tools/lib/src/resident_runner.dart
+++ b/packages/flutter_tools/lib/src/resident_runner.dart
@@ -624,6 +624,8 @@
   }
 
   Future<Null> processTerminalInput(String command) async {
+    // When terminal doesn't support line mode, '\n' can sneak into the input.
+    command = command.trim();
     if (_processingUserRequest) {
       printTrace('Ignoring terminal input: "$command" because we are busy.');
       return;
diff --git a/packages/flutter_tools/test/resident_runner_test.dart b/packages/flutter_tools/test/resident_runner_test.dart
new file mode 100644
index 0000000..62cf116
--- /dev/null
+++ b/packages/flutter_tools/test/resident_runner_test.dart
@@ -0,0 +1,82 @@
+// Copyright 2017 The Chromium Authors. 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 'package:flutter_tools/src/device.dart';
+import 'package:flutter_tools/src/resident_runner.dart';
+import 'package:mockito/mockito.dart';
+import 'package:test/test.dart';
+
+import 'src/context.dart';
+
+class TestRunner extends ResidentRunner {
+  TestRunner(List<FlutterDevice> devices)
+      : super(devices);
+
+  bool hasHelpBeenPrinted = false;
+  String receivedCommand;
+
+  @override
+  Future<Null> cleanupAfterSignal() => null;
+
+  @override
+  Future<Null> cleanupAtFinish() => null;
+
+  @override
+  Future<Null> handleTerminalCommand(String code) async {
+    receivedCommand = code;
+  }
+
+  @override
+  void printHelp({ bool details }) {
+    hasHelpBeenPrinted = true;
+  }
+
+  @override
+  Future<int> run({
+    Completer<DebugConnectionInfo> connectionInfoCompleter,
+    Completer<dynamic> appStartedCompleter,
+    String route,
+    bool shouldBuild: true,
+  }) => null;
+}
+
+void main() {
+  TestRunner testRunner;
+
+  setUp(() {
+    testRunner = new TestRunner(
+        <FlutterDevice>[new FlutterDevice(new MockDevice())]
+    );
+  });
+
+  group('keyboard input handling', () {
+    testUsingContext('single help character', () async {
+      expect(testRunner.hasHelpBeenPrinted, isFalse);
+      await testRunner.processTerminalInput('h');
+      expect(testRunner.hasHelpBeenPrinted, isTrue);
+    });
+    testUsingContext('help character surrounded with newlines', () async {
+      expect(testRunner.hasHelpBeenPrinted, isFalse);
+      await testRunner.processTerminalInput('\nh\n');
+      expect(testRunner.hasHelpBeenPrinted, isTrue);
+    });
+    testUsingContext('reload character with trailing newline', () async {
+      expect(testRunner.receivedCommand, isNull);
+      await testRunner.processTerminalInput('r\n');
+      expect(testRunner.receivedCommand, equals('r'));
+    });
+    testUsingContext('newlines', () async {
+      expect(testRunner.receivedCommand, isNull);
+      await testRunner.processTerminalInput('\n\n');
+      expect(testRunner.receivedCommand, equals(''));
+    });
+  });
+}
+
+class MockDevice extends Mock implements Device {
+  MockDevice() {
+    when(isSupported()).thenReturn(true);
+  }
+}