Unverified Commit 676a4264 authored by Danny Tuppeny's avatar Danny Tuppeny Committed by GitHub

[dap] Don't wait for appStarted before responding to launch/attach + don't...

[dap] Don't wait for appStarted before responding to launch/attach + don't call app.stop for unstarted app (#109386)
parent da09f78e
...@@ -59,11 +59,9 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments ...@@ -59,11 +59,9 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments
/// Whether or not the app.started event has been received. /// Whether or not the app.started event has been received.
bool get _receivedAppStarted => appStartedCompleter.isCompleted; bool get _receivedAppStarted => appStartedCompleter.isCompleted;
/// The VM Service URI received from the app.debugPort event.
Uri? _vmServiceUri;
/// The appId of the current running Flutter app. /// The appId of the current running Flutter app.
String? _appId; @visibleForTesting
String? appId;
/// The ID to use for the next request sent to the Flutter run daemon. /// The ID to use for the next request sent to the Flutter run daemon.
int _flutterRequestId = 1; int _flutterRequestId = 1;
...@@ -311,13 +309,6 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments ...@@ -311,13 +309,6 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments
processArgs: processArgs, processArgs: processArgs,
env: args.env, env: args.env,
); );
// Delay responding until the app is launched and (optionally) the debugger
// is connected.
await appStartedCompleter.future;
if (enableDebugger) {
await debuggerInitialized;
}
} }
@visibleForOverriding @visibleForOverriding
...@@ -404,23 +395,22 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments ...@@ -404,23 +395,22 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments
// Send a request to stop/detach to give Flutter chance to do some cleanup. // Send a request to stop/detach to give Flutter chance to do some cleanup.
// It's possible the Flutter process will terminate before we process the // It's possible the Flutter process will terminate before we process the
// response, so accept either a response or the process exiting. // response, so accept either a response or the process exiting.
final String method = isAttach ? 'app.detach' : 'app.stop'; if (appId != null) {
await Future.any<void>(<Future<void>>[ final String method = isAttach ? 'app.detach' : 'app.stop';
sendFlutterRequest(method, <String, Object?>{'appId': _appId}), await Future.any<void>(<Future<void>>[
_process?.exitCode ?? Future<void>.value(), sendFlutterRequest(method, <String, Object?>{'appId': appId}),
]); _process?.exitCode ?? Future<void>.value(),
]);
}
terminatePids(ProcessSignal.sigterm); terminatePids(ProcessSignal.sigterm);
await _process?.exitCode; await _process?.exitCode;
} }
/// Connects to the VM Service if the app.started event has fired, and a VM Service URI is available. /// Connects to the VM Service if the app.started event has fired, and a VM Service URI is available.
void _connectDebuggerIfReady() { Future<void> _connectDebugger(Uri vmServiceUri) async {
final Uri? serviceUri = _vmServiceUri;
if (_receivedAppStarted && serviceUri != null) {
if (enableDebugger) { if (enableDebugger) {
connectDebugger(serviceUri); await connectDebugger(vmServiceUri);
} else { } else {
// Usually, `connectDebugger` (in the base Dart adapter) will send this // Usually, `connectDebugger` (in the base Dart adapter) will send this
// event when it connects a debugger. Since we're not connecting a // event when it connects a debugger. Since we're not connecting a
...@@ -431,29 +421,30 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments ...@@ -431,29 +421,30 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments
// adapter once rolled into Flutter. // adapter once rolled into Flutter.
sendEvent( sendEvent(
RawEventBody(<String, Object?>{ RawEventBody(<String, Object?>{
'vmServiceUri': serviceUri.toString(), 'vmServiceUri': vmServiceUri.toString(),
}), }),
eventType: 'dart.debuggerUris', eventType: 'dart.debuggerUris',
); );
} }
}
} }
/// Handles the app.start event from Flutter. /// Handles the app.start event from Flutter.
void _handleAppStart(Map<String, Object?> params) { void _handleAppStart(Map<String, Object?> params) {
_appId = params['appId'] as String?; appId = params['appId'] as String?;
assert(_appId != null); if(appId == null) {
throw DebugAdapterException('Unexpected null `appId` in app.start event');
}
} }
/// Handles the app.started event from Flutter. /// Handles the app.started event from Flutter.
void _handleAppStarted() { Future<void> _handleAppStarted() async {
appStartedCompleter.complete(); appStartedCompleter.complete();
_connectDebuggerIfReady();
// Send a custom event so the editor knows the app has started. // Send a custom event so the editor knows the app has started.
// //
// This may be useful when there's no VM Service (for example Profile mode) // This may be useful when there's no VM Service (for example Profile mode)
// but the editor still wants to know that startup has finished. // but the editor still wants to know that startup has finished.
await debuggerInitialized; // Ensure we're fully initialized before sending.
sendEvent( sendEvent(
RawEventBody(<String, Object?>{}), RawEventBody(<String, Object?>{}),
eventType: 'flutter.appStarted', eventType: 'flutter.appStarted',
...@@ -472,13 +463,16 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments ...@@ -472,13 +463,16 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments
} }
/// Handles the app.debugPort event from Flutter, connecting to the VM Service if everything else is ready. /// Handles the app.debugPort event from Flutter, connecting to the VM Service if everything else is ready.
void _handleDebugPort(Map<String, Object?> params) { Future<void> _handleDebugPort(Map<String, Object?> params) async {
// Capture the VM Service URL which we'll connect to when we get app.started. // Capture the VM Service URL which we'll connect to when we get app.started.
final String? wsUri = params['wsUri'] as String?; final String? wsUri = params['wsUri'] as String?;
if (wsUri != null) { if (wsUri != null) {
_vmServiceUri = Uri.parse(wsUri); final Uri vmServiceUri = Uri.parse(wsUri);
// Also wait for app.started before we connect, to ensure Flutter's
// initialization is all complete.
await appStartedCompleter.future;
await _connectDebugger(vmServiceUri);
} }
_connectDebuggerIfReady();
} }
/// Handles the Flutter process exiting, terminating the debug session if it has not already begun terminating. /// Handles the Flutter process exiting, terminating the debug session if it has not already begun terminating.
...@@ -601,7 +595,7 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments ...@@ -601,7 +595,7 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments
]) async { ]) async {
try { try {
await sendFlutterRequest('app.restart', <String, Object?>{ await sendFlutterRequest('app.restart', <String, Object?>{
'appId': _appId, 'appId': appId,
'fullRestart': fullRestart, 'fullRestart': fullRestart,
'pause': enableDebugger, 'pause': enableDebugger,
'reason': reason, 'reason': reason,
......
...@@ -120,6 +120,30 @@ void main() { ...@@ -120,6 +120,30 @@ void main() {
expect(adapter.flutterRequests, contains('app.stop')); expect(adapter.flutterRequests, contains('app.stop'));
}); });
test('does not call "app.stop" on terminateRequest if app was not started', () async {
final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter(
fileSystem: MemoryFileSystem.test(style: fsStyle),
platform: platform,
simulateAppStarted: false,
);
final FlutterLaunchRequestArguments args = FlutterLaunchRequestArguments(
cwd: '/project',
program: 'foo.dart',
);
await adapter.configurationDoneRequest(MockRequest(), null, () {});
final Completer<void> launchCompleter = Completer<void>();
await adapter.launchRequest(MockRequest(), args, launchCompleter.complete);
await launchCompleter.future;
final Completer<void> terminateCompleter = Completer<void>();
await adapter.terminateRequest(MockRequest(), TerminateArguments(restart: false), terminateCompleter.complete);
await terminateCompleter.future;
expect(adapter.flutterRequests, isNot(contains('app.stop')));
});
}); });
group('attachRequest', () { group('attachRequest', () {
......
...@@ -15,6 +15,7 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter { ...@@ -15,6 +15,7 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter {
factory MockFlutterDebugAdapter({ factory MockFlutterDebugAdapter({
required FileSystem fileSystem, required FileSystem fileSystem,
required Platform platform, required Platform platform,
bool simulateAppStarted = true,
}) { }) {
final StreamController<List<int>> stdinController = StreamController<List<int>>(); final StreamController<List<int>> stdinController = StreamController<List<int>>();
final StreamController<List<int>> stdoutController = StreamController<List<int>>(); final StreamController<List<int>> stdoutController = StreamController<List<int>>();
...@@ -26,6 +27,7 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter { ...@@ -26,6 +27,7 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter {
channel, channel,
fileSystem: fileSystem, fileSystem: fileSystem,
platform: platform, platform: platform,
simulateAppStarted: simulateAppStarted,
); );
} }
...@@ -35,10 +37,12 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter { ...@@ -35,10 +37,12 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter {
ByteStreamServerChannel channel, { ByteStreamServerChannel channel, {
required FileSystem fileSystem, required FileSystem fileSystem,
required Platform platform, required Platform platform,
this.simulateAppStarted = true,
}) : super(channel, fileSystem: fileSystem, platform: platform); }) : super(channel, fileSystem: fileSystem, platform: platform);
final StreamSink<List<int>> stdin; final StreamSink<List<int>> stdin;
final Stream<List<int>> stdout; final Stream<List<int>> stdout;
final bool simulateAppStarted;
late String executable; late String executable;
late List<String> processArgs; late List<String> processArgs;
...@@ -57,7 +61,10 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter { ...@@ -57,7 +61,10 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter {
// Pretend we launched the app and got the app.started event so that // Pretend we launched the app and got the app.started event so that
// launchRequest will complete. // launchRequest will complete.
appStartedCompleter.complete(); if (simulateAppStarted) {
appId = 'TEST';
appStartedCompleter.complete();
}
} }
@override @override
......
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