Unverified Commit 5655225c authored by Danny Tuppeny's avatar Danny Tuppeny Committed by GitHub

Send progress notifications to clients during hot reload / hot restart (#112455)

parent a11bef96
...@@ -140,6 +140,13 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments ...@@ -140,6 +140,13 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments
Future<void> attachImpl() async { Future<void> attachImpl() async {
final FlutterAttachRequestArguments args = this.args as FlutterAttachRequestArguments; final FlutterAttachRequestArguments args = this.args as FlutterAttachRequestArguments;
final DapProgressReporter progress = startProgressNotification(
'launch',
'Flutter',
message: 'Attaching…',
);
unawaited(appStartedCompleter.future.then((_) => progress.end()));
final String? vmServiceUri = args.vmServiceUri; final String? vmServiceUri = args.vmServiceUri;
final List<String> toolArgs = <String>[ final List<String> toolArgs = <String>[
'attach', 'attach',
...@@ -255,6 +262,13 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments ...@@ -255,6 +262,13 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments
Future<void> launchImpl() async { Future<void> launchImpl() async {
final FlutterLaunchRequestArguments args = this.args as FlutterLaunchRequestArguments; final FlutterLaunchRequestArguments args = this.args as FlutterLaunchRequestArguments;
final DapProgressReporter progress = startProgressNotification(
'launch',
'Flutter',
message: 'Launching…',
);
unawaited(appStartedCompleter.future.then((_) => progress.end()));
final List<String> toolArgs = <String>[ final List<String> toolArgs = <String>[
'run', 'run',
'--machine', '--machine',
...@@ -593,6 +607,14 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments ...@@ -593,6 +607,14 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments
bool fullRestart, [ bool fullRestart, [
String? reason, String? reason,
]) async { ]) async {
final String progressId = fullRestart ? 'hotRestart' : 'hotReload';
final String progressMessage = fullRestart ? 'Hot restarting…' : 'Hot reloading…';
final DapProgressReporter progress = startProgressNotification(
progressId,
'Flutter',
message: progressMessage,
);
try { try {
await sendFlutterRequest('app.restart', <String, Object?>{ await sendFlutterRequest('app.restart', <String, Object?>{
'appId': appId, 'appId': appId,
...@@ -605,6 +627,9 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments ...@@ -605,6 +627,9 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments
final String action = fullRestart ? 'Hot Restart' : 'Hot Reload'; final String action = fullRestart ? 'Hot Restart' : 'Hot Reload';
sendOutput('console', 'Failed to $action: $error'); sendOutput('console', 'Failed to $action: $error');
} }
finally {
progress.end();
}
} }
void _sendServiceExtensionStateChanged(vm.ExtensionData? extensionData) { void _sendServiceExtensionStateChanged(vm.ExtensionData? extensionData) {
......
...@@ -27,6 +27,7 @@ class FlutterAttachRequestArguments ...@@ -27,6 +27,7 @@ class FlutterAttachRequestArguments
super.evaluateGettersInDebugViews, super.evaluateGettersInDebugViews,
super.evaluateToStringInDebugViews, super.evaluateToStringInDebugViews,
super.sendLogsToClient, super.sendLogsToClient,
super.sendCustomProgressEvents,
}); });
FlutterAttachRequestArguments.fromMap(super.obj) FlutterAttachRequestArguments.fromMap(super.obj)
...@@ -99,6 +100,7 @@ class FlutterLaunchRequestArguments ...@@ -99,6 +100,7 @@ class FlutterLaunchRequestArguments
super.evaluateGettersInDebugViews, super.evaluateGettersInDebugViews,
super.evaluateToStringInDebugViews, super.evaluateToStringInDebugViews,
super.sendLogsToClient, super.sendLogsToClient,
super.sendCustomProgressEvents,
}); });
FlutterLaunchRequestArguments.fromMap(super.obj) FlutterLaunchRequestArguments.fromMap(super.obj)
......
...@@ -218,6 +218,43 @@ void main() { ...@@ -218,6 +218,43 @@ void main() {
await dap.client.terminate(); await dap.client.terminate();
}); });
testWithoutContext('sends progress notifications during hot reload', () async {
final BasicProject project = BasicProject();
await project.setUpIn(tempDir);
// Launch the app and wait for it to print "topLevelFunction".
await Future.wait(<Future<void>>[
dap.client.stdoutOutput.firstWhere((String output) => output.startsWith('topLevelFunction')),
dap.client.initialize(supportsProgressReporting: true),
dap.client.launch(
cwd: project.dir.path,
noDebug: true,
toolArgs: <String>['-d', 'flutter-tester'],
),
], eagerError: true);
// Capture progress events during a reload.
final Future<List<Event>> progressEventsFuture = dap.client.progressEvents().toList();
await dap.client.hotReload();
await dap.client.terminate();
// Verify the progress events.
final List<Event> progressEvents = await progressEventsFuture;
expect(progressEvents, hasLength(2));
final List<String> eventKinds = progressEvents.map((Event event) => event.event).toList();
expect(eventKinds, <String>['progressStart', 'progressEnd']);
final List<Map<String, Object?>> eventBodies = progressEvents.map((Event event) => event.body).cast<Map<String, Object?>>().toList();
final ProgressStartEventBody start = ProgressStartEventBody.fromMap(eventBodies[0]);
final ProgressEndEventBody end = ProgressEndEventBody.fromMap(eventBodies[1]);
expect(start.progressId, isNotNull);
expect(start.title, 'Flutter');
expect(start.message, 'Hot reloading…');
expect(end.progressId, start.progressId);
expect(end.message, isNull);
});
testWithoutContext('can hot restart', () async { testWithoutContext('can hot restart', () async {
final BasicProject project = BasicProject(); final BasicProject project = BasicProject();
await project.setUpIn(tempDir); await project.setUpIn(tempDir);
...@@ -255,6 +292,43 @@ void main() { ...@@ -255,6 +292,43 @@ void main() {
await dap.client.terminate(); await dap.client.terminate();
}); });
testWithoutContext('sends progress notifications during hot restart', () async {
final BasicProject project = BasicProject();
await project.setUpIn(tempDir);
// Launch the app and wait for it to print "topLevelFunction".
await Future.wait(<Future<void>>[
dap.client.stdoutOutput.firstWhere((String output) => output.startsWith('topLevelFunction')),
dap.client.initialize(supportsProgressReporting: true),
dap.client.launch(
cwd: project.dir.path,
noDebug: true,
toolArgs: <String>['-d', 'flutter-tester'],
),
], eagerError: true);
// Capture progress events during a restart.
final Future<List<Event>> progressEventsFuture = dap.client.progressEvents().toList();
await dap.client.hotRestart();
await dap.client.terminate();
// Verify the progress events.
final List<Event> progressEvents = await progressEventsFuture;
expect(progressEvents, hasLength(2));
final List<String> eventKinds = progressEvents.map((Event event) => event.event).toList();
expect(eventKinds, <String>['progressStart', 'progressEnd']);
final List<Map<String, Object?>> eventBodies = progressEvents.map((Event event) => event.body).cast<Map<String, Object?>>().toList();
final ProgressStartEventBody start = ProgressStartEventBody.fromMap(eventBodies[0]);
final ProgressEndEventBody end = ProgressEndEventBody.fromMap(eventBodies[1]);
expect(start.progressId, isNotNull);
expect(start.title, 'Flutter');
expect(start.message, 'Hot restarting…');
expect(end.progressId, start.progressId);
expect(end.message, isNull);
});
testWithoutContext('can hot restart when exceptions occur on outgoing isolates', () async { testWithoutContext('can hot restart when exceptions occur on outgoing isolates', () async {
final BasicProjectThatThrows project = BasicProjectThatThrows(); final BasicProjectThatThrows project = BasicProjectThatThrows();
await project.setUpIn(tempDir); await project.setUpIn(tempDir);
......
...@@ -83,6 +83,12 @@ class DapTestClient { ...@@ -83,6 +83,12 @@ class DapTestClient {
return _eventController.stream.where((Event e) => e.event == event); return _eventController.stream.where((Event e) => e.event == event);
} }
/// Returns a stream of progress events.
Stream<Event> progressEvents() {
const Set<String> progressEvents = <String>{'progressStart', 'progressUpdate', 'progressEnd'};
return _eventController.stream.where((Event e) => progressEvents.contains(e.event));
}
/// Returns a stream of custom 'dart.serviceExtensionAdded' events. /// Returns a stream of custom 'dart.serviceExtensionAdded' events.
Stream<Map<String, Object?>> get serviceExtensionAddedEvents => Stream<Map<String, Object?>> get serviceExtensionAddedEvents =>
events('dart.serviceExtensionAdded') events('dart.serviceExtensionAdded')
...@@ -116,12 +122,14 @@ class DapTestClient { ...@@ -116,12 +122,14 @@ class DapTestClient {
Future<Response> initialize({ Future<Response> initialize({
String exceptionPauseMode = 'None', String exceptionPauseMode = 'None',
bool? supportsRunInTerminalRequest, bool? supportsRunInTerminalRequest,
bool? supportsProgressReporting,
}) async { }) async {
final List<ProtocolMessage> responses = await Future.wait(<Future<ProtocolMessage>>[ final List<ProtocolMessage> responses = await Future.wait(<Future<ProtocolMessage>>[
event('initialized'), event('initialized'),
sendRequest(InitializeRequestArguments( sendRequest(InitializeRequestArguments(
adapterID: 'test', adapterID: 'test',
supportsRunInTerminalRequest: supportsRunInTerminalRequest, supportsRunInTerminalRequest: supportsRunInTerminalRequest,
supportsProgressReporting: supportsProgressReporting,
)), )),
sendRequest( sendRequest(
SetExceptionBreakpointsArguments( SetExceptionBreakpointsArguments(
......
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