Unverified Commit 5d31b07e authored by Danny Tuppeny's avatar Danny Tuppeny Committed by GitHub

[flutter_tools] [dap] Ensure DAP sends app.stop/app.detach during terminate (#108310)

* [flutter_tools] [dap] Ensure DAP sends app.stop/app.detach during terminate

Fixes an issue where the flutter_tester device may not be cleaned up correctly if we just terminate the Flutter process.

* Update integration test expectations

* Revert accidental commit
parent 925bee92
...@@ -400,6 +400,16 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments ...@@ -400,6 +400,16 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments
if (isAttach) { if (isAttach) {
await preventBreakingAndResume(); await preventBreakingAndResume();
} }
// 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(),
]);
terminatePids(ProcessSignal.sigterm); terminatePids(ProcessSignal.sigterm);
await _process?.exitCode; await _process?.exitCode;
} }
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import 'dart:async'; import 'dart:async';
import 'package:dds/dap.dart';
import 'package:file/memory.dart'; import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
...@@ -96,6 +97,29 @@ void main() { ...@@ -96,6 +97,29 @@ void main() {
// Ensure the VM's pid was not recorded. // Ensure the VM's pid was not recorded.
expect(adapter.pidsToTerminate, isNot(contains(123))); expect(adapter.pidsToTerminate, isNot(contains(123)));
}); });
test('calls "app.stop" on terminateRequest', () async {
final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter(
fileSystem: MemoryFileSystem.test(style: fsStyle),
platform: platform,
);
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, contains('app.stop'));
});
}); });
group('attachRequest', () { group('attachRequest', () {
...@@ -139,6 +163,28 @@ void main() { ...@@ -139,6 +163,28 @@ void main() {
// Ensure the VM's pid was not recorded. // Ensure the VM's pid was not recorded.
expect(adapter.pidsToTerminate, isNot(contains(123))); expect(adapter.pidsToTerminate, isNot(contains(123)));
}); });
test('calls "app.detach" on terminateRequest', () async {
final MockFlutterDebugAdapter adapter = MockFlutterDebugAdapter(
fileSystem: MemoryFileSystem.test(style: fsStyle),
platform: platform,
);
final FlutterAttachRequestArguments args = FlutterAttachRequestArguments(
cwd: '/project',
);
await adapter.configurationDoneRequest(MockRequest(), null, () {});
final Completer<void> attachCompleter = Completer<void>();
await adapter.attachRequest(MockRequest(), args, attachCompleter.complete);
await attachCompleter.future;
final Completer<void> terminateCompleter = Completer<void>();
await adapter.terminateRequest(MockRequest(), TerminateArguments(restart: false), terminateCompleter.complete);
await terminateCompleter.future;
expect(adapter.flutterRequests, contains('app.detach'));
});
}); });
group('--start-paused', () { group('--start-paused', () {
......
...@@ -43,6 +43,7 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter { ...@@ -43,6 +43,7 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter {
late String executable; late String executable;
late List<String> processArgs; late List<String> processArgs;
late Map<String, String>? env; late Map<String, String>? env;
final List<String> flutterRequests = <String>[];
@override @override
Future<void> launchAsProcess({ Future<void> launchAsProcess({
...@@ -59,6 +60,16 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter { ...@@ -59,6 +60,16 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter {
appStartedCompleter.complete(); appStartedCompleter.complete();
} }
@override
Future<Object?> sendFlutterRequest(
String method,
Map<String, Object?>? params, {
bool failSilently = true,
}) {
flutterRequests.add(method);
return super.sendFlutterRequest(method, params, failSilently: failSilently);
}
@override @override
Future<void> get debuggerInitialized { Future<void> get debuggerInitialized {
// If we were mocking debug mode, then simulate the debugger initializing. // If we were mocking debug mode, then simulate the debugger initializing.
......
...@@ -64,6 +64,7 @@ void main() { ...@@ -64,6 +64,7 @@ void main() {
'Launching $relativeMainPath on Flutter test device in debug mode...', 'Launching $relativeMainPath on Flutter test device in debug mode...',
startsWith('Connecting to VM Service at'), startsWith('Connecting to VM Service at'),
'topLevelFunction', 'topLevelFunction',
'Application finished.',
'', '',
startsWith('Exited'), startsWith('Exited'),
]); ]);
...@@ -94,6 +95,7 @@ void main() { ...@@ -94,6 +95,7 @@ void main() {
expectLines(output, <Object>[ expectLines(output, <Object>[
'Launching $relativeMainPath on Flutter test device in debug mode...', 'Launching $relativeMainPath on Flutter test device in debug mode...',
'topLevelFunction', 'topLevelFunction',
'Application finished.',
'', '',
startsWith('Exited'), startsWith('Exited'),
]); ]);
......
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