Unverified Commit 7b150f81 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

move reload and restart handling into terminal (#35846)

parent 14d489ad
......@@ -559,6 +559,9 @@ abstract class ResidentRunner {
});
}
/// Whether this runner can hot reload.
bool get canHotReload => hotMode;
/// Start the app and keep the process running during its lifetime.
///
/// Returns the exit code that we should use for the flutter tool process; 0
......@@ -838,35 +841,6 @@ abstract class ResidentRunner {
/// Called right before we exit.
Future<void> cleanupAtFinish();
/// Called when the runner should handle a terminal command.
Future<void> handleTerminalCommand(String code) async {
switch (code) {
case 'r':
final OperationResult result = await restart(fullRestart: false);
if (!result.isOk) {
printStatus('Try again after fixing the above error(s).', emphasis: true);
}
return;
case 'R':
// If hot restart is not supported for all devices, ignore the command.
if (!canHotRestart) {
return;
}
final OperationResult result = await restart(fullRestart: true);
if (!result.isOk) {
printStatus('Try again after fixing the above error(s).', emphasis: true);
}
return;
case 'l':
case 'L':
final List<FlutterView> views = flutterDevices.expand((FlutterDevice d) => d.views).toList();
printStatus('Connected ${pluralize('view', views.length)}:');
for (FlutterView v in views) {
printStatus('${v.uiIsolate.name} (${v.uiIsolate.id})', indent: 2);
}
}
}
}
class OperationResult {
......@@ -918,6 +892,9 @@ class TerminalHandler {
bool _processingUserRequest = false;
StreamSubscription<void> subscription;
@visibleForTesting
String lastReceivedCommand;
void setupTerminal() {
if (!logger.quiet) {
printStatus('');
......@@ -964,6 +941,14 @@ class TerminalHandler {
return true;
}
return false;
case 'l':
final List<FlutterView> views = residentRunner.flutterDevices
.expand((FlutterDevice d) => d.views).toList();
printStatus('Connected ${pluralize('view', views.length)}:');
for (FlutterView v in views) {
printStatus('${v.uiIsolate.name} (${v.uiIsolate.id})', indent: 2);
}
return true;
case 'L':
if (residentRunner.supportsServiceProtocol) {
await residentRunner.debugDumpLayerTree();
......@@ -1000,6 +985,25 @@ class TerminalHandler {
await residentRunner.screenshot(device);
}
return true;
case 'r':
if (!residentRunner.canHotReload) {
return false;
}
final OperationResult result = await residentRunner.restart(fullRestart: false);
if (!result.isOk) {
printStatus('Try again after fixing the above error(s).', emphasis: true);
}
return true;
case 'R':
// If hot restart is not supported for all devices, ignore the command.
if (!residentRunner.canHotRestart || !residentRunner.hotMode) {
return false;
}
final OperationResult result = await residentRunner.restart(fullRestart: true);
if (!result.isOk) {
printStatus('Try again after fixing the above error(s).', emphasis: true);
}
return true;
case 'S':
if (residentRunner.supportsServiceProtocol) {
await residentRunner.debugDumpSemanticsTreeInTraversalOrder();
......@@ -1031,11 +1035,9 @@ class TerminalHandler {
await residentRunner.debugToggleDebugCheckElevationsEnabled();
return true;
}
return false;
}
Future<void> processTerminalInput(String command) async {
// When terminal doesn't support line mode, '\n' can sneak into the input.
command = command.trim();
......@@ -1045,9 +1047,8 @@ class TerminalHandler {
}
_processingUserRequest = true;
try {
final bool handled = await _commonTerminalInputHandler(command);
if (!handled)
await residentRunner.handleTerminalCommand(command);
lastReceivedCommand = command;
await _commonTerminalInputHandler(command);
} catch (error, st) {
printError('$error\n$st');
await _cleanUpAndExit(null);
......
......@@ -49,6 +49,9 @@ class ResidentWebRunner extends ResidentRunner {
WipConnection _connection;
final FlutterProject flutterProject;
@override
bool get canHotReload => false;
@override
Future<int> attach(
{Completer<DebugConnectionInfo> connectionInfoCompleter,
......@@ -73,17 +76,6 @@ class ResidentWebRunner extends ResidentRunner {
await _server?.dispose();
}
@override
Future<void> handleTerminalCommand(String code) async {
if (code == 'R') {
// If hot restart is not supported for all devices, ignore the command.
if (!canHotRestart) {
return;
}
await restart(fullRestart: true);
}
}
@override
void printHelp({bool details}) {
const String fire = '🔥';
......
......@@ -38,6 +38,12 @@ class ColdRunner extends ResidentRunner {
final File applicationBinary;
bool _didAttach = false;
@override
bool get canHotReload => false;
@override
bool get canHotRestart => false;
@override
Future<int> run({
Completer<DebugConnectionInfo> connectionInfoCompleter,
......
......@@ -3,9 +3,12 @@
// found in the LICENSE file.
import 'dart:async';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/globals.dart';
import 'package:flutter_tools/src/resident_runner.dart';
import 'package:flutter_tools/src/vmservice.dart';
import 'package:mockito/mockito.dart';
import 'src/common.dart';
......@@ -24,37 +27,21 @@ void main() {
testUsingContext('single help character', () async {
final TestRunner testRunner = createTestRunner();
final TerminalHandler terminalHandler = TerminalHandler(testRunner);
expect(testRunner.hasHelpBeenPrinted, isFalse);
expect(testRunner.hasHelpBeenPrinted, false);
await terminalHandler.processTerminalInput('h');
expect(testRunner.hasHelpBeenPrinted, isTrue);
expect(testRunner.hasHelpBeenPrinted, true);
});
testUsingContext('help character surrounded with newlines', () async {
final TestRunner testRunner = createTestRunner();
final TerminalHandler terminalHandler = TerminalHandler(testRunner);
expect(testRunner.hasHelpBeenPrinted, isFalse);
expect(testRunner.hasHelpBeenPrinted, false);
await terminalHandler.processTerminalInput('\nh\n');
expect(testRunner.hasHelpBeenPrinted, isTrue);
});
testUsingContext('reload character with trailing newline', () async {
final TestRunner testRunner = createTestRunner();
final TerminalHandler terminalHandler = TerminalHandler(testRunner);
expect(testRunner.receivedCommand, isNull);
await terminalHandler.processTerminalInput('r\n');
expect(testRunner.receivedCommand, equals('r'));
});
testUsingContext('newlines', () async {
final TestRunner testRunner = createTestRunner();
final TerminalHandler terminalHandler = TerminalHandler(testRunner);
expect(testRunner.receivedCommand, isNull);
await terminalHandler.processTerminalInput('\n\n');
expect(testRunner.receivedCommand, equals(''));
expect(testRunner.hasHelpBeenPrinted, true);
});
});
group('keycode verification, brought to you by the letter r', () {
group('keycode verification, brought to you by the letter', () {
MockResidentRunner mockResidentRunner;
TerminalHandler terminalHandler;
......@@ -62,7 +49,18 @@ void main() {
mockResidentRunner = MockResidentRunner();
terminalHandler = TerminalHandler(mockResidentRunner);
when(mockResidentRunner.supportsServiceProtocol).thenReturn(true);
when(mockResidentRunner.handleTerminalCommand(any)).thenReturn(null);
});
testUsingContext('a, can handle trailing newlines', () async {
await terminalHandler.processTerminalInput('a\n');
expect(terminalHandler.lastReceivedCommand, 'a');
});
testUsingContext('n, can handle trailing only newlines', () async {
await terminalHandler.processTerminalInput('\n\n');
expect(terminalHandler.lastReceivedCommand, '');
});
testUsingContext('a - debugToggleProfileWidgetBuilds with service protocol', () async {
......@@ -116,6 +114,19 @@ void main() {
verifyNever(mockResidentRunner.debugToggleWidgetInspector());
});
testUsingContext('l - list flutter views', () async {
final MockFlutterDevice mockFlutterDevice = MockFlutterDevice();
when(mockResidentRunner.isRunningDebug).thenReturn(true);
when(mockResidentRunner.flutterDevices).thenReturn(<FlutterDevice>[mockFlutterDevice]);
when(mockFlutterDevice.views).thenReturn(<FlutterView>[]);
await terminalHandler.processTerminalInput('l');
final BufferLogger bufferLogger = logger;
expect(bufferLogger.statusText, contains('Connected views:\n'));
});
testUsingContext('L - debugDumpLayerTree with service protocol', () async {
await terminalHandler.processTerminalInput('L');
......@@ -209,6 +220,74 @@ void main() {
verify(mockResidentRunner.screenshot(mockFlutterDevice)).called(1);
});
testUsingContext('r - hotReload supported and succeeds', () async {
when(mockResidentRunner.canHotReload).thenReturn(true);
when(mockResidentRunner.restart(fullRestart: false))
.thenAnswer((Invocation invocation) async {
return OperationResult(0, '');
});
await terminalHandler.processTerminalInput('r');
verify(mockResidentRunner.restart(fullRestart: false)).called(1);
});
testUsingContext('r - hotReload supported and fails', () async {
when(mockResidentRunner.canHotReload).thenReturn(true);
when(mockResidentRunner.restart(fullRestart: false))
.thenAnswer((Invocation invocation) async {
return OperationResult(1, '');
});
await terminalHandler.processTerminalInput('r');
verify(mockResidentRunner.restart(fullRestart: false)).called(1);
final BufferLogger bufferLogger = logger;
expect(bufferLogger.statusText, contains('Try again after fixing the above error(s).'));
});
testUsingContext('r - hotReload unsupported', () async {
when(mockResidentRunner.canHotReload).thenReturn(false);
await terminalHandler.processTerminalInput('r');
verifyNever(mockResidentRunner.restart(fullRestart: false));
});
testUsingContext('R - hotRestart supported and succeeds', () async {
when(mockResidentRunner.canHotRestart).thenReturn(true);
when(mockResidentRunner.hotMode).thenReturn(true);
when(mockResidentRunner.restart(fullRestart: true))
.thenAnswer((Invocation invocation) async {
return OperationResult(0, '');
});
await terminalHandler.processTerminalInput('R');
verify(mockResidentRunner.restart(fullRestart: true)).called(1);
});
testUsingContext('R - hotRestart supported and fails', () async {
when(mockResidentRunner.canHotRestart).thenReturn(true);
when(mockResidentRunner.hotMode).thenReturn(true);
when(mockResidentRunner.restart(fullRestart: true))
.thenAnswer((Invocation invocation) async {
return OperationResult(1, 'fail');
});
await terminalHandler.processTerminalInput('R');
verify(mockResidentRunner.restart(fullRestart: true)).called(1);
final BufferLogger bufferLogger = logger;
expect(bufferLogger.statusText, contains('Try again after fixing the above error(s).'));
});
testUsingContext('R - hot restart unsupported', () async {
when(mockResidentRunner.canHotRestart).thenReturn(false);
await terminalHandler.processTerminalInput('R');
verifyNever(mockResidentRunner.restart(fullRestart: true));
});
testUsingContext('S - debugDumpSemanticsTreeInTraversalOrder with service protocol', () async {
await terminalHandler.processTerminalInput('S');
......@@ -306,11 +385,6 @@ class TestRunner extends ResidentRunner {
@override
Future<void> cleanupAtFinish() async { }
@override
Future<void> handleTerminalCommand(String code) async {
receivedCommand = code;
}
@override
void printHelp({ bool details }) {
hasHelpBeenPrinted = true;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment