[dds/dap] Ensure DAP always sends resume on PausePostRequest
The change at https://dart-review.googlesource.com/c/sdk/+/410760 to fix a race incorrectly assumed that once we had handled startup for a thread, we would never need to send an automatic resume again. However this was not the case - after a hot reload, we need to resume the thread even though we had technically already handled startup.
This is essentially a partial revert of 84e6ed0784, with a test to verify we trigger readyToResume on PausePostRequest.
Change-Id: I1e171043f04f38dd6f52e1692d9257d34e5248be
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/416580
Commit-Queue: Ben Konyi <bkonyi@google.com>
Reviewed-by: Ben Konyi <bkonyi@google.com>
Reviewed-by: Derek Xu <derekx@google.com>
This commit is contained in:
committed by
Commit Queue
parent
5c1b9086c0
commit
29c350dfe9
@@ -645,7 +645,12 @@ class IsolateManager {
|
||||
// after a hot restart.
|
||||
if (eventKind == vm.EventKind.kPausePostRequest) {
|
||||
await _configureIsolate(thread);
|
||||
await handleThreadStartup(thread, sendStoppedOnEntry: false);
|
||||
|
||||
// We always want to resume here regardless of whether startupHandled was
|
||||
// already `true` (because that might be from before the reload).
|
||||
// Therefore set the flag and resume always.
|
||||
thread.startupHandled = true;
|
||||
await readyToResumeThread(thread.threadId);
|
||||
} else if (eventKind == vm.EventKind.kPauseStart) {
|
||||
handleThreadStartup(thread, sendStoppedOnEntry: true);
|
||||
} else {
|
||||
|
||||
@@ -144,5 +144,27 @@ main() {
|
||||
expect(receivedUri, 'scheme://file/path.dart');
|
||||
expect(resolved, Uri.file('/returned/file/path.dart'));
|
||||
});
|
||||
|
||||
test('sends resume after kPausePostRequest', () async {
|
||||
// Trigger initial startup.
|
||||
await adapter.isolateManager.handleEvent(Event(
|
||||
isolate: adapter.mockService.isolate1,
|
||||
kind: EventKind.kIsolateRunnable));
|
||||
isolateManager.threads[0].startupHandled = true;
|
||||
|
||||
// Clear any previously called methods.
|
||||
adapter.mockService.calledMethods.clear();
|
||||
|
||||
// Now simulate a hot reload causing the isolate to pause again.
|
||||
await adapter.isolateManager.handleEvent(Event(
|
||||
isolate: adapter.mockService.isolate1,
|
||||
kind: EventKind.kPausePostRequest));
|
||||
|
||||
// Ensure we called readyToResume.
|
||||
expect(adapter.mockService.calledMethods.map((cm) => cm.method),
|
||||
contains('readyToResume'));
|
||||
|
||||
// Expect it was resumed.
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:dap/dap.dart' as dap;
|
||||
import 'package:dds/dap.dart';
|
||||
import 'package:dds/dap.dart' hide Response;
|
||||
import 'package:dds/dds.dart';
|
||||
import 'package:dds/dds_launcher.dart';
|
||||
import 'package:dds/src/dap/adapters/dart_cli_adapter.dart';
|
||||
@@ -168,6 +168,10 @@ class MockVmService implements VmService {
|
||||
/// tests.
|
||||
final List<String> requests = [];
|
||||
|
||||
/// A list of methods that were called on the Mock VM Service.
|
||||
List<({String method, String? isolateId, Map<String, dynamic>? args})>
|
||||
calledMethods = [];
|
||||
|
||||
@override
|
||||
Future<Success> setLibraryDebuggable(
|
||||
String isolateId,
|
||||
@@ -213,6 +217,30 @@ class MockVmService implements VmService {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Response> callMethod(
|
||||
String method, {
|
||||
String? isolateId,
|
||||
Map<String, dynamic>? args,
|
||||
}) async {
|
||||
calledMethods.add((method: method, isolateId: isolateId, args: args));
|
||||
|
||||
// Some DDS methods are implemented as extensions so we can't override
|
||||
// them in the mock, we need to override callMethod and handle them here
|
||||
// (and provide their responses) instead.
|
||||
|
||||
if (method == 'getDartDevelopmentServiceVersion') {
|
||||
return Version(major: 2, minor: 0);
|
||||
} else if (method == 'readyToResume') {
|
||||
isolateId ??= args != null ? args['isolateId'] : null;
|
||||
return {isolate1.id, isolate2.id}.contains(isolateId)
|
||||
? Success()
|
||||
: throw SentinelException.parse(method, {});
|
||||
}
|
||||
|
||||
throw 'MockVmService does not handle $method ($isolateId, $args)';
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Success> resume(
|
||||
String isolateId, {
|
||||
@@ -224,6 +252,18 @@ class MockVmService implements VmService {
|
||||
? Success()
|
||||
: throw SentinelException.parse('resume', {});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Success> setIsolatePauseMode(
|
||||
String isolateId, {
|
||||
/*ExceptionPauseMode*/ String? exceptionPauseMode,
|
||||
bool? shouldPauseOnExit,
|
||||
}) async {
|
||||
// Do nothing, just pretend.
|
||||
return {isolate1.id, isolate2.id}.contains(isolateId)
|
||||
? Success()
|
||||
: throw SentinelException.parse('setIsolatePauseMode', {});
|
||||
}
|
||||
}
|
||||
|
||||
class MockDartDevelopmentServiceLauncher
|
||||
|
||||
Reference in New Issue
Block a user