[flutter_tool] [dap] Forward Flutter progress events to DAP client (#142524)

Builds can be slow and the legacy debug adapter would handle Flutter's `app.progress` events to update the toast notification during builds. This was lost in the new adapters - we should only a single "Launching.." notification for the whole progress.

This change listens to `app.progress` events and forwards those with `finished=false` to the client if the launch progress is still active.

Fixes https://github.com/Dart-Code/Dart-Code/issues/4938

https://github.com/flutter/flutter/assets/1078012/8c60cf08-e034-4a72-b31e-9c61dca388bf
This commit is contained in:
Danny Tuppeny 2024-02-15 22:22:48 +00:00 committed by GitHub
parent 7082ae1517
commit 86613d198e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 70 additions and 1 deletions

View file

@ -420,6 +420,19 @@ class FlutterDebugAdapter extends FlutterBaseDebugAdapter with VmServiceInfoFile
);
}
/// Handles any app.progress event from Flutter.
void _handleAppProgress(Map<String, Object?> params) {
// If this is a new progress starting (and we're still launching), update
// the progress notification.
//
// We ignore finished status because we have a limited API - the next
// item will replace it (or the launch progress will be completed by
// _handleAppStarted).
if (params case {'message': final String message, 'finished': false}) {
launchProgress?.update(message: message);
}
}
/// Handles the app.started event from Flutter.
Future<void> _handleAppStarted() async {
launchProgress?.end();
@ -481,6 +494,8 @@ class FlutterDebugAdapter extends FlutterBaseDebugAdapter with VmServiceInfoFile
_handleDebugPort(params);
case 'app.start':
_handleAppStart(params);
case 'app.progress':
_handleAppProgress(params);
case 'app.started':
_handleAppStarted();
}

View file

@ -210,6 +210,45 @@ void main() {
expect(adapter.dapToFlutterRequests, isNot(contains('app.restart')));
});
test('includes build progress updates', () async {
final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter(
fileSystem: MemoryFileSystem.test(style: fsStyle),
platform: platform,
);
final Completer<void> responseCompleter = Completer<void>();
final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments(
cwd: '.',
program: 'foo.dart',
);
// Begin listening for progress events up until `progressEnd` (but don't await yet).
final Future<List<List<Object?>>> progressEventsFuture =
adapter.dapToClientProgressEvents
.takeWhile((Map<String, Object?> message) => message['event'] != 'progressEnd')
.map((Map<String, Object?> message) => <Object?>[message['event'], (message['body']! as Map<String, Object?>)['message']])
.toList();
// Initialize with progress support.
await adapter.initializeRequest(
MockRequest(),
InitializeRequestArguments(adapterID: 'test', supportsProgressReporting: true, ),
(_) {},
);
await adapter.configurationDoneRequest(MockRequest(), null, () {});
await adapter.launchRequest(MockRequest(), args, responseCompleter.complete);
await responseCompleter.future;
// Ensure we got the expected events prior to the progressEnd.
final List<List<Object?>> progressEvents = await progressEventsFuture;
expect(progressEvents, containsAllInOrder(<List<String?>>[
<String?>['progressStart', 'Launching…'],
<String?>['progressUpdate', 'Step 1…'],
<String?>['progressUpdate', 'Step 2…'],
// progressEnd isn't included because we used takeWhile to stop when it arrived above.
]));
});
test('includes Dart Debug extension progress update', () async {
final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter(
fileSystem: MemoryFileSystem.test(style: fsStyle),
@ -242,7 +281,7 @@ void main() {
await adapter.launchRequest(MockRequest(), args, responseCompleter.complete);
await responseCompleter.future;
// Ensure we got the expected events prior to the
// Ensure we got the expected events prior to the progressEnd.
final List<List<Object?>> progressEvents = await progressEventsFuture;
expect(progressEvents, containsAllInOrder(<List<String>>[
<String>['progressStart', 'Launching…'],

View file

@ -104,9 +104,22 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter {
await preAppStart?.call(this);
void sendLaunchProgress({required bool finished, String? message}) {
assert(finished == (message == null));
simulateStdoutMessage(<String, Object?>{
'event': 'app.progress',
'params': <String, Object?>{
'id': 'launch',
'message': message,
'finished': finished,
}
});
}
// Simulate the app starting by triggering handling of events that Flutter
// would usually write to stdout.
if (simulateAppStarted) {
sendLaunchProgress(message: 'Step 1…', finished: false);
simulateStdoutMessage(<String, Object?>{
'event': 'app.start',
'params': <String, Object?>{
@ -116,6 +129,8 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter {
'mode': 'debug',
}
});
sendLaunchProgress(message: 'Step 2…', finished: false);
sendLaunchProgress(finished: true);
simulateStdoutMessage(<String, Object?>{
'event': 'app.started',
});