Skip to content

Commit 675b961

Browse files
authored
Unpause and remove breakpoints when detaching from Flutter process with DAP (#101695)
1 parent b086473 commit 675b961

File tree

3 files changed

+40
-6
lines changed

3 files changed

+40
-6
lines changed

packages/flutter_tools/lib/src/debug_adapters/flutter_adapter.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,9 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments
205205
/// quickly and therefore may leave orphaned processes.
206206
@override
207207
Future<void> disconnectImpl() async {
208+
if (isAttach) {
209+
await preventBreakingAndResume();
210+
}
208211
terminatePids(ProcessSignal.sigkill);
209212
}
210213

@@ -379,6 +382,9 @@ class FlutterDebugAdapter extends DartDebugAdapter<FlutterLaunchRequestArguments
379382
/// Called by [terminateRequest] to request that we gracefully shut down the app being run (or in the case of an attach, disconnect).
380383
@override
381384
Future<void> terminateImpl() async {
385+
if (isAttach) {
386+
await preventBreakingAndResume();
387+
}
382388
terminatePids(ProcessSignal.sigterm);
383389
await _process?.exitCode;
384390
}

packages/flutter_tools/test/integration.shard/debug_adapter/flutter_adapter_test.dart

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -380,13 +380,35 @@ void main() {
380380
stoppedFuture,
381381
dap.client.setBreakpoint(breakpointFilePath, breakpointLine),
382382
], eagerError: true);
383-
final int threadId = (await stoppedFuture).threadId!;
383+
});
384+
385+
testWithoutContext('resumes and removes breakpoints on detach', () async {
386+
final Uri vmServiceUri = await testProcess.vmServiceUri;
387+
388+
// Launch the app and wait for it to print "topLevelFunction".
389+
await Future.wait(<Future<void>>[
390+
dap.client.stdoutOutput.firstWhere((String output) => output.startsWith('topLevelFunction')),
391+
dap.client.start(
392+
launch: () => dap.client.attach(
393+
cwd: project.dir.path,
394+
toolArgs: <String>['-d', 'flutter-tester'],
395+
vmServiceUri: vmServiceUri.toString(),
396+
),
397+
),
398+
], eagerError: true);
384399

385-
// Remove the breakpoint and resume.
386-
await dap.client.clearBreakpoints(breakpointFilePath);
387-
await dap.client.continue_(threadId);
400+
// Set a breakpoint and expect to hit it.
401+
final Future<StoppedEventBody> stoppedFuture = dap.client.stoppedEvents.firstWhere((StoppedEventBody e) => e.reason == 'breakpoint');
402+
await Future.wait(<Future<void>>[
403+
stoppedFuture,
404+
dap.client.setBreakpoint(breakpointFilePath, breakpointLine),
405+
], eagerError: true);
388406

407+
// Detach.
389408
await dap.client.terminate();
409+
410+
// Ensure we get additional output (confirming the process resumed).
411+
await testProcess.output.first;
390412
});
391413
});
392414
}

packages/flutter_tools/test/integration.shard/debug_adapter/test_support.dart

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ class SimpleFlutterRunner {
6363
unawaited(process.exitCode.then(_handleExitCode));
6464
}
6565

66+
final StreamController<String> _output = StreamController<String>.broadcast();
67+
68+
/// A broadcast stream of any non-JSON output from the process.
69+
Stream<String> get output => _output.stream;
70+
6671
void _handleExitCode(int code) {
6772
if (!_vmServiceUriCompleter.isCompleted) {
6873
_vmServiceUriCompleter.completeError('Flutter process ended without producing a VM Service URI');
@@ -91,8 +96,9 @@ class SimpleFlutterRunner {
9196
}
9297
}
9398
} on FormatException {
94-
// `flutter run` writes a lot of text to stdout so just ignore anything
95-
// that's not valid JSON.
99+
// `flutter run` writes a lot of text to stdout that isn't daemon messages
100+
// (not valid JSON), so just pass that one for tests that may want it.
101+
_output.add(outputLine);
96102
}
97103
}
98104

0 commit comments

Comments
 (0)