Unverified Commit 8cac409d authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Clarify hot restart behavior on the web (#42931)

parent f6d3dbd6
...@@ -120,8 +120,8 @@ class ResidentWebRunner extends ResidentRunner { ...@@ -120,8 +120,8 @@ class ResidentWebRunner extends ResidentRunner {
return printHelpDetails(); return printHelpDetails();
} }
const String fire = '🔥'; const String fire = '🔥';
const String rawMessage = const String rawMessage = ' To hot restart changes while running, press "r". '
' To hot restart (and rebuild state), press "r" or "R".'; 'To hot restart (and refresh the browser), press "R".';
final String message = terminal.color( final String message = terminal.color(
fire + terminal.bolden(rawMessage), fire + terminal.bolden(rawMessage),
TerminalColor.red, TerminalColor.red,
...@@ -297,16 +297,21 @@ class ResidentWebRunner extends ResidentRunner { ...@@ -297,16 +297,21 @@ class ResidentWebRunner extends ResidentRunner {
final Duration recompileDuration = timer.elapsed; final Duration recompileDuration = timer.elapsed;
flutterUsage.sendTiming('hot', 'web-recompile', recompileDuration); flutterUsage.sendTiming('hot', 'web-recompile', recompileDuration);
try { try {
final vmservice.Response reloadResponse = await _vmService.callServiceExtension('hotRestart'); final vmservice.Response reloadResponse = fullRestart
printStatus('Restarted application in ${getElapsedAsMilliseconds(timer.elapsed)}.'); ? await _vmService.callServiceExtension('fullReload')
: await _vmService.callServiceExtension('hotRestart');
final String verb = fullRestart ? 'Restarted' : 'Reloaded';
printStatus('$verb application in ${getElapsedAsMilliseconds(timer.elapsed)}.');
// Send timing analytics for full restart and for refresh. // Send timing analytics for full restart and for refresh.
final bool wasSuccessful = reloadResponse.type == 'Success'; final bool wasSuccessful = reloadResponse.type == 'Success';
if (!wasSuccessful) { if (!wasSuccessful) {
return OperationResult(1, reloadResponse.toString()); return OperationResult(1, reloadResponse.toString());
} }
if (!fullRestart) {
flutterUsage.sendTiming('hot', 'web-restart', timer.elapsed); flutterUsage.sendTiming('hot', 'web-restart', timer.elapsed);
flutterUsage.sendTiming('hot', 'web-refresh', timer.elapsed - recompileDuration); flutterUsage.sendTiming('hot', 'web-refresh', timer.elapsed - recompileDuration);
}
return OperationResult.ok; return OperationResult.ok;
} on vmservice.RPCError { } on vmservice.RPCError {
return OperationResult(1, 'Page requires refresh.'); return OperationResult(1, 'Page requires refresh.');
......
...@@ -176,6 +176,7 @@ void main() { ...@@ -176,6 +176,7 @@ void main() {
test('Can hot reload after attaching', () => testbed.run(() async { test('Can hot reload after attaching', () => testbed.run(() async {
_setupMocks(); _setupMocks();
final BufferLogger bufferLogger = logger;
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run( unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter, connectionInfoCompleter: connectionInfoCompleter,
...@@ -189,6 +190,7 @@ void main() { ...@@ -189,6 +190,7 @@ void main() {
}); });
final OperationResult result = await residentWebRunner.restart(fullRestart: false); final OperationResult result = await residentWebRunner.restart(fullRestart: false);
expect(bufferLogger.statusText, contains('Reloaded application in'));
expect(result.code, 0); expect(result.code, 0);
// ensure that analytics are sent. // ensure that analytics are sent.
verify(Usage.instance.sendEvent('hot', 'restart', parameters: <String, String>{ verify(Usage.instance.sendEvent('hot', 'restart', parameters: <String, String>{
...@@ -206,6 +208,7 @@ void main() { ...@@ -206,6 +208,7 @@ void main() {
test('Can hot restart after attaching', () => testbed.run(() async { test('Can hot restart after attaching', () => testbed.run(() async {
_setupMocks(); _setupMocks();
final BufferLogger bufferLogger = logger;
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run( unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter, connectionInfoCompleter: connectionInfoCompleter,
...@@ -214,11 +217,12 @@ void main() { ...@@ -214,11 +217,12 @@ void main() {
when(mockWebFs.recompile()).thenAnswer((Invocation invocation) async { when(mockWebFs.recompile()).thenAnswer((Invocation invocation) async {
return true; return true;
}); });
when(mockVmService.callServiceExtension('hotRestart')).thenAnswer((Invocation _) async { when(mockVmService.callServiceExtension('fullReload')).thenAnswer((Invocation _) async {
return Response.parse(<String, Object>{'type': 'Success'}); return Response.parse(<String, Object>{'type': 'Success'});
}); });
final OperationResult result = await residentWebRunner.restart(fullRestart: true); final OperationResult result = await residentWebRunner.restart(fullRestart: true);
expect(bufferLogger.statusText, contains('Restarted application in'));
expect(result.code, 0); expect(result.code, 0);
// ensure that analytics are sent. // ensure that analytics are sent.
verify(Usage.instance.sendEvent('hot', 'restart', parameters: <String, String>{ verify(Usage.instance.sendEvent('hot', 'restart', parameters: <String, String>{
...@@ -227,8 +231,8 @@ void main() { ...@@ -227,8 +231,8 @@ void main() {
'cd29': 'false', 'cd29': 'false',
'cd30': 'true', 'cd30': 'true',
})).called(1); })).called(1);
verify(Usage.instance.sendTiming('hot', 'web-restart', any)).called(1); verifyNever(Usage.instance.sendTiming('hot', 'web-restart', any));
verify(Usage.instance.sendTiming('hot', 'web-refresh', any)).called(1); verifyNever(Usage.instance.sendTiming('hot', 'web-refresh', any));
verify(Usage.instance.sendTiming('hot', 'web-recompile', any)).called(1); verify(Usage.instance.sendTiming('hot', 'web-recompile', any)).called(1);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Usage: () => MockFlutterUsage(), Usage: () => MockFlutterUsage(),
...@@ -255,7 +259,7 @@ void main() { ...@@ -255,7 +259,7 @@ void main() {
Usage: () => MockFlutterUsage(), Usage: () => MockFlutterUsage(),
})); }));
test('Fails on vmservice response error', () => testbed.run(() async { test('Fails on vmservice response error for hot restart', () => testbed.run(() async {
_setupMocks(); _setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run( unawaited(residentWebRunner.run(
...@@ -265,7 +269,7 @@ void main() { ...@@ -265,7 +269,7 @@ void main() {
when(mockWebFs.recompile()).thenAnswer((Invocation _) async { when(mockWebFs.recompile()).thenAnswer((Invocation _) async {
return true; return true;
}); });
when(mockVmService.callServiceExtension('hotRestart')).thenAnswer((Invocation _) async { when(mockVmService.callServiceExtension('fullReload')).thenAnswer((Invocation _) async {
return Response.parse(<String, Object>{'type': 'Failed'}); return Response.parse(<String, Object>{'type': 'Failed'});
}); });
final OperationResult result = await residentWebRunner.restart(fullRestart: true); final OperationResult result = await residentWebRunner.restart(fullRestart: true);
...@@ -279,6 +283,30 @@ void main() { ...@@ -279,6 +283,30 @@ void main() {
Usage: () => MockFlutterUsage(), Usage: () => MockFlutterUsage(),
})); }));
test('Fails on vmservice response error for hot reload', () => testbed.run(() async {
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
when(mockWebFs.recompile()).thenAnswer((Invocation _) async {
return true;
});
when(mockVmService.callServiceExtension('hotRestart')).thenAnswer((Invocation _) async {
return Response.parse(<String, Object>{'type': 'Failed'});
});
final OperationResult result = await residentWebRunner.restart(fullRestart: false);
expect(result.code, 1);
expect(result.message, contains('Failed'));
verifyNever(Usage.instance.sendTiming('hot', 'web-restart', any));
verifyNever(Usage.instance.sendTiming('hot', 'web-refresh', any));
verify(Usage.instance.sendTiming('hot', 'web-recompile', any)).called(1);
}, overrides: <Type, Generator>{
Usage: () => MockFlutterUsage(),
}));
test('Fails on vmservice RpcError', () => testbed.run(() async { test('Fails on vmservice RpcError', () => testbed.run(() async {
_setupMocks(); _setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
...@@ -290,7 +318,7 @@ void main() { ...@@ -290,7 +318,7 @@ void main() {
return true; return true;
}); });
when(mockVmService.callServiceExtension('hotRestart')).thenThrow(RPCError('', 2, '123')); when(mockVmService.callServiceExtension('hotRestart')).thenThrow(RPCError('', 2, '123'));
final OperationResult result = await residentWebRunner.restart(fullRestart: true); final OperationResult result = await residentWebRunner.restart(fullRestart: false);
expect(result.code, 1); expect(result.code, 1);
expect(result.message, contains('Page requires refresh')); expect(result.message, contains('Page requires refresh'));
......
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