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
/// Whether or not the app.started event has been received.
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.
String? _appId;
@visibleForTesting
String? appId;
/// The ID to use for the next request sent to the Flutter run daemon.
int _flutterRequestId = 1;
......@@ -311,13 +309,6 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments
processArgs: processArgs,
env: args.env,
);
// Delay responding until the app is launched and (optionally) the debugger
// is connected.
await appStartedCompleter.future;
if (enableDebugger) {
await debuggerInitialized;
}
}
@visibleForOverriding
......@@ -404,23 +395,22 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments
// 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
// response, so accept either a response or the process exiting.
final String method = isAttach ? 'app.detach' : 'app.stop';
await Future.any<void>(<Future<void>>[
sendFlutterRequest(method, <String, Object?>{'appId': _appId}),
_process?.exitCode ?? Future<void>.value(),
]);
if (appId != null) {
final String method = isAttach ? 'app.detach' : 'app.stop';
await Future.any<void>(<Future<void>>[
sendFlutterRequest(method, <String, Object?>{'appId': appId}),
_process?.exitCode ?? Future<void>.value(),
]);
}
terminatePids(ProcessSignal.sigterm);
await _process?.exitCode;
}
/// Connects to the VM Service if the app.started event has fired, and a VM Service URI is available.
void _connectDebuggerIfReady() {
final Uri? serviceUri = _vmServiceUri;
if (_receivedAppStarted && serviceUri != null) {
Future<void> _connectDebugger(Uri vmServiceUri) async {
if (enableDebugger) {
connectDebugger(serviceUri);
await connectDebugger(vmServiceUri);
} else {
// Usually, `connectDebugger` (in the base Dart adapter) will send this
// event when it connects a debugger. Since we're not connecting a
......@@ -431,29 +421,30 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments
// adapter once rolled into Flutter.
sendEvent(
RawEventBody(<String, Object?>{
'vmServiceUri': serviceUri.toString(),
'vmServiceUri': vmServiceUri.toString(),
}),
eventType: 'dart.debuggerUris',
);
}
}
}
/// Handles the app.start event from Flutter.
void _handleAppStart(Map<String, Object?> params) {
_appId = params['appId'] as String?;
assert(_appId != null);
appId = params['appId'] as String?;
if(appId == null) {
throw DebugAdapterException('Unexpected null `appId` in app.start event');
}
}
/// Handles the app.started event from Flutter.
void _handleAppStarted() {
Future<void> _handleAppStarted() async {
appStartedCompleter.complete();
_connectDebuggerIfReady();
// 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)
// but the editor still wants to know that startup has finished.
await debuggerInitialized; // Ensure we're fully initialized before sending.
sendEvent(
RawEventBody(<String, Object?>{}),
eventType: 'flutter.appStarted',
......@@ -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.
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.
final String? wsUri = params['wsUri'] as String?;
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.
......@@ -601,7 +595,7 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments
]) async {
try {
await sendFlutterRequest('app.restart', <String, Object?>{
'appId': _appId,
'appId': appId,
'fullRestart': fullRestart,
'pause': enableDebugger,
'reason': reason,
......
......@@ -120,6 +120,30 @@ void main() {
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', () {
......
......@@ -15,6 +15,7 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter {
factory MockFlutterDebugAdapter({
required FileSystem fileSystem,
required Platform platform,
bool simulateAppStarted = true,
}) {
final StreamController<List<int>> stdinController = StreamController<List<int>>();
final StreamController<List<int>> stdoutController = StreamController<List<int>>();
......@@ -26,6 +27,7 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter {
channel,
fileSystem: fileSystem,
platform: platform,
simulateAppStarted: simulateAppStarted,
);
}
......@@ -35,10 +37,12 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter {
ByteStreamServerChannel channel, {
required FileSystem fileSystem,
required Platform platform,
this.simulateAppStarted = true,
}) : super(channel, fileSystem: fileSystem, platform: platform);
final StreamSink<List<int>> stdin;
final Stream<List<int>> stdout;
final bool simulateAppStarted;
late String executable;
late List<String> processArgs;
......@@ -57,7 +61,10 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter {
// Pretend we launched the app and got the app.started event so that
// launchRequest will complete.
appStartedCompleter.complete();
if (simulateAppStarted) {
appId = 'TEST';
appStartedCompleter.complete();
}
}
@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