mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
[flutter_tools] Update DAP progress when waiting for Dart Debug extension connection (#116892)
Fixes https://github.com/Dart-Code/Dart-Code/issues/4293.
This commit is contained in:
parent
3eefb7af09
commit
9f9010f5e8
@ -36,6 +36,11 @@ class FlutterDebugAdapter extends FlutterBaseDebugAdapter {
|
||||
/// The appId of the current running Flutter app.
|
||||
String? _appId;
|
||||
|
||||
/// A progress reporter for the applications launch progress.
|
||||
///
|
||||
/// `null` if a launch is not in progress (or has completed).
|
||||
DapProgressReporter? launchProgress;
|
||||
|
||||
/// The ID to use for the next request sent to the Flutter run daemon.
|
||||
int _flutterRequestId = 1;
|
||||
|
||||
@ -123,12 +128,11 @@ class FlutterDebugAdapter extends FlutterBaseDebugAdapter {
|
||||
Future<void> attachImpl() async {
|
||||
final FlutterAttachRequestArguments args = this.args as FlutterAttachRequestArguments;
|
||||
|
||||
final DapProgressReporter progress = startProgressNotification(
|
||||
launchProgress = startProgressNotification(
|
||||
'launch',
|
||||
'Flutter',
|
||||
message: 'Attaching…',
|
||||
);
|
||||
unawaited(_appStartedCompleter.future.then((_) => progress.end()));
|
||||
|
||||
final String? vmServiceUri = args.vmServiceUri;
|
||||
final List<String> toolArgs = <String>[
|
||||
@ -230,12 +234,11 @@ class FlutterDebugAdapter extends FlutterBaseDebugAdapter {
|
||||
Future<void> launchImpl() async {
|
||||
final FlutterLaunchRequestArguments args = this.args as FlutterLaunchRequestArguments;
|
||||
|
||||
final DapProgressReporter progress = startProgressNotification(
|
||||
launchProgress = startProgressNotification(
|
||||
'launch',
|
||||
'Flutter',
|
||||
message: 'Launching…',
|
||||
);
|
||||
unawaited(_appStartedCompleter.future.then((_) => progress.end()));
|
||||
|
||||
final List<String> toolArgs = <String>[
|
||||
'run',
|
||||
@ -398,6 +401,8 @@ class FlutterDebugAdapter extends FlutterBaseDebugAdapter {
|
||||
|
||||
/// Handles the app.started event from Flutter.
|
||||
Future<void> _handleAppStarted() async {
|
||||
launchProgress?.end();
|
||||
launchProgress = null;
|
||||
_appStartedCompleter.complete();
|
||||
|
||||
// Send a custom event so the editor knows the app has started.
|
||||
@ -591,6 +596,16 @@ class FlutterDebugAdapter extends FlutterBaseDebugAdapter {
|
||||
// If the output wasn't valid JSON, it was standard stdout that should
|
||||
// be passed through to the user.
|
||||
sendOutput(outputCategory, data);
|
||||
|
||||
// Detect if the output contains a prompt about using the Dart Debug
|
||||
// extension and also update the progress notification to make it clearer
|
||||
// we're waiting for the user to do something.
|
||||
if (data.contains('Waiting for connection from Dart debug extension')) {
|
||||
launchProgress?.update(
|
||||
message: 'Please click the Dart Debug extension button in the spawned browser window',
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -147,6 +147,47 @@ void main() {
|
||||
|
||||
expect(adapter.dapToFlutterRequests, isNot(contains('app.stop')));
|
||||
});
|
||||
|
||||
test('includes Dart Debug extension progress update', () async {
|
||||
final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter(
|
||||
fileSystem: MemoryFileSystem.test(style: fsStyle),
|
||||
platform: platform,
|
||||
preAppStart: (MockFlutterDebugAdapter adapter) {
|
||||
adapter.simulateRawStdout('Waiting for connection from Dart debug extension…');
|
||||
}
|
||||
);
|
||||
final Completer<void> responseCompleter = Completer<void>();
|
||||
|
||||
final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments(
|
||||
cwd: '/project',
|
||||
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
|
||||
final List<List<Object?>> progressEvents = await progressEventsFuture;
|
||||
expect(progressEvents, containsAllInOrder(<List<String>>[
|
||||
<String>['progressStart', 'Launching…'],
|
||||
<String>['progressUpdate', 'Please click the Dart Debug extension button in the spawned browser window'],
|
||||
// progressEnd isn't included because we used takeWhile to stop when it arrived above.
|
||||
]));
|
||||
});
|
||||
});
|
||||
|
||||
group('attachRequest', () {
|
||||
@ -221,6 +262,11 @@ void main() {
|
||||
platform: platform,
|
||||
);
|
||||
|
||||
// Start listening for the forwarded event (don't await it yet, it won't
|
||||
// be triggered until the call below).
|
||||
final Future<Map<String, Object?>> forwardedEvent = adapter.dapToClientMessages
|
||||
.firstWhere((Map<String, Object?> data) => data['event'] == 'flutter.forwardedEvent');
|
||||
|
||||
// Simulate Flutter asking for a URL to be launched.
|
||||
adapter.simulateStdoutMessage(<String, Object?>{
|
||||
'event': 'app.webLaunchUrl',
|
||||
@ -230,11 +276,8 @@ void main() {
|
||||
}
|
||||
});
|
||||
|
||||
// Allow the handler to be processed.
|
||||
await pumpEventQueue(times: 5000);
|
||||
|
||||
// Find the forwarded event.
|
||||
final Map<String, Object?> message = adapter.dapToClientMessages.singleWhere((Map<String, Object?> data) => data['event'] == 'flutter.forwardedEvent');
|
||||
// Wait for the forwarded event.
|
||||
final Map<String, Object?> message = await forwardedEvent;
|
||||
// Ensure the body of the event matches the original event sent by Flutter.
|
||||
expect(message['body'], <String, Object?>{
|
||||
'event': 'app.webLaunchUrl',
|
||||
|
@ -18,6 +18,7 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter {
|
||||
required FileSystem fileSystem,
|
||||
required Platform platform,
|
||||
bool simulateAppStarted = true,
|
||||
FutureOr<void> Function(MockFlutterDebugAdapter adapter)? preAppStart,
|
||||
}) {
|
||||
final StreamController<List<int>> stdinController = StreamController<List<int>>();
|
||||
final StreamController<List<int>> stdoutController = StreamController<List<int>>();
|
||||
@ -30,6 +31,7 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter {
|
||||
fileSystem: fileSystem,
|
||||
platform: platform,
|
||||
simulateAppStarted: simulateAppStarted,
|
||||
preAppStart: preAppStart,
|
||||
);
|
||||
}
|
||||
|
||||
@ -39,6 +41,7 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter {
|
||||
required super.fileSystem,
|
||||
required super.platform,
|
||||
this.simulateAppStarted = true,
|
||||
this.preAppStart,
|
||||
}) {
|
||||
clientChannel.listen((ProtocolMessage message) {
|
||||
_handleDapToClientMessage(message);
|
||||
@ -48,13 +51,24 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter {
|
||||
int _seq = 1;
|
||||
final ByteStreamServerChannel clientChannel;
|
||||
final bool simulateAppStarted;
|
||||
final FutureOr<void> Function(MockFlutterDebugAdapter adapter)? preAppStart;
|
||||
|
||||
late String executable;
|
||||
late List<String> processArgs;
|
||||
late Map<String, String>? env;
|
||||
|
||||
/// A list of all messages sent from the adapter back to the client.
|
||||
final List<Map<String, Object?>> dapToClientMessages = <Map<String, Object?>>[];
|
||||
final StreamController<Map<String, Object?>> _dapToClientMessagesController = StreamController<Map<String, Object?>>.broadcast();
|
||||
|
||||
/// A stream of all messages sent from the adapter back to the client.
|
||||
Stream<Map<String, Object?>> get dapToClientMessages => _dapToClientMessagesController.stream;
|
||||
|
||||
/// A stream of all progress events sent from the adapter back to the client.
|
||||
Stream<Map<String, Object?>> get dapToClientProgressEvents {
|
||||
const List<String> progressEventTypes = <String>['progressStart', 'progressUpdate', 'progressEnd'];
|
||||
|
||||
return dapToClientMessages
|
||||
.where((Map<String, Object?> message) => progressEventTypes.contains(message['event'] as String?));
|
||||
}
|
||||
|
||||
/// A list of all messages sent from the adapter to the `flutter run` processes `stdin`.
|
||||
final List<Map<String, Object?>> dapToFlutterMessages = <Map<String, Object?>>[];
|
||||
@ -79,6 +93,8 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter {
|
||||
this.processArgs = processArgs;
|
||||
this.env = env;
|
||||
|
||||
await preAppStart?.call(this);
|
||||
|
||||
// Simulate the app starting by triggering handling of events that Flutter
|
||||
// would usually write to stdout.
|
||||
if (simulateAppStarted) {
|
||||
@ -96,7 +112,7 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter {
|
||||
|
||||
/// Handles messages sent from the debug adapter back to the client.
|
||||
void _handleDapToClientMessage(ProtocolMessage message) {
|
||||
dapToClientMessages.add(message.toJson());
|
||||
_dapToClientMessagesController.add(message.toJson());
|
||||
|
||||
// Pretend to be the client, delegating any reverse-requests to the relevant
|
||||
// handler that is provided by the test.
|
||||
@ -131,12 +147,22 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter {
|
||||
|
||||
/// Simulates a message emitted by the `flutter run` process by directly
|
||||
/// calling the debug adapters [handleStdout] method.
|
||||
///
|
||||
/// Use [simulateRawStdout] to simulate non-daemon text output.
|
||||
void simulateStdoutMessage(Map<String, Object?> message) {
|
||||
// Messages are wrapped in a list because Flutter only processes messages
|
||||
// wrapped in brackets.
|
||||
handleStdout(jsonEncode(<Object?>[message]));
|
||||
}
|
||||
|
||||
/// Simulates a string emitted by the `flutter run` process by directly
|
||||
/// calling the debug adapters [handleStdout] method.
|
||||
///
|
||||
/// Use [simulateStdoutMessage] to simulate a daemon JSON message.
|
||||
void simulateRawStdout(String output) {
|
||||
handleStdout(output);
|
||||
}
|
||||
|
||||
@override
|
||||
void sendFlutterMessage(Map<String, Object?> message) {
|
||||
dapToFlutterMessages.add(message);
|
||||
|
Loading…
Reference in New Issue
Block a user