| // Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'package:dds/src/dap/isolate_manager.dart'; |
| import 'package:test/test.dart'; |
| import 'package:vm_service/vm_service.dart'; |
| |
| import 'mocks.dart'; |
| |
| main() { |
| group('IsolateManager', () { |
| late MockDartCliDebugAdapter adapter; |
| late IsolateManager isolateManager; |
| |
| setUp(() async { |
| adapter = MockDartCliDebugAdapter(); |
| isolateManager = adapter.isolateManager; |
| |
| isolateManager.debug = true; |
| isolateManager.debugSdkLibraries = false; |
| isolateManager.debugExternalPackageLibraries = true; |
| await isolateManager.registerIsolate( |
| adapter.mockService.isolate1, |
| EventKind.kIsolateStart, |
| ); |
| await isolateManager.registerIsolate( |
| adapter.mockService.isolate2, |
| EventKind.kIsolateStart, |
| ); |
| }); |
| |
| test('sends only changes to SDK libraries debuggable flag', () async { |
| // Default is false, so should not send anything. |
| isolateManager.debugSdkLibraries = false; |
| await isolateManager.applyDebugOptions(); |
| expect( |
| adapter.mockService.requests, |
| isNot( |
| contains(startsWith('setLibraryDebuggable(isolate1, libSdk,')), |
| ), |
| ); |
| |
| // Changing to non-default should send a request. |
| isolateManager.debugSdkLibraries = true; |
| await isolateManager.applyDebugOptions(); |
| expect( |
| adapter.mockService.requests, |
| contains('setLibraryDebuggable(isolate1, libSdk, true)'), |
| ); |
| |
| // Setting back to default should now send. |
| isolateManager.debugSdkLibraries = false; |
| await isolateManager.applyDebugOptions(); |
| expect( |
| adapter.mockService.requests, |
| contains('setLibraryDebuggable(isolate1, libSdk, false)'), |
| ); |
| }); |
| |
| test('sends only changes to external package libraries debuggable flag', |
| () async { |
| // Default is true, so should not send anything. |
| isolateManager.debugExternalPackageLibraries = true; |
| await isolateManager.applyDebugOptions(); |
| expect( |
| adapter.mockService.requests, |
| isNot( |
| contains( |
| startsWith('setLibraryDebuggable(isolate1, libPkgExternal,')), |
| ), |
| ); |
| |
| // Changing to non-default should send a request. |
| isolateManager.debugExternalPackageLibraries = false; |
| await isolateManager.applyDebugOptions(); |
| expect( |
| adapter.mockService.requests, |
| contains('setLibraryDebuggable(isolate1, libPkgExternal, false)'), |
| ); |
| |
| // Setting back to default should now send. |
| isolateManager.debugExternalPackageLibraries = true; |
| await isolateManager.applyDebugOptions(); |
| expect( |
| adapter.mockService.requests, |
| contains('setLibraryDebuggable(isolate1, libPkgExternal, true)'), |
| ); |
| }); |
| |
| /// Local packages are always debuggable, in the VM and because we have no |
| /// settings. No changes should ever cause requests for them. |
| test('never sends values for local package libraries debuggable flag', |
| () async { |
| isolateManager.debugSdkLibraries = true; |
| isolateManager.debugExternalPackageLibraries = true; |
| await isolateManager.applyDebugOptions(); |
| |
| isolateManager.debugSdkLibraries = false; |
| isolateManager.debugExternalPackageLibraries = false; |
| await isolateManager.applyDebugOptions(); |
| |
| expect( |
| adapter.mockService.requests, |
| isNot( |
| contains(startsWith('setLibraryDebuggable(isolate1, libPkgLocal,')), |
| ), |
| ); |
| }); |
| |
| test('clears thread data on resume', () async { |
| final thread1 = isolateManager.threads[0]; |
| final thread2 = isolateManager.threads[1]; |
| |
| // Store some data in both threads. |
| final ref1 = thread1.storeData("test1"); |
| final ref2 = thread2.storeData("test2"); |
| |
| // Resume thread1 |
| thread1.paused = true; // Fake pause to allow resume. |
| await isolateManager.resumeThread(thread1.threadId); |
| |
| // Ensure thread1 had data cleared, but thread2 did not. |
| expect(isolateManager.getStoredData(ref1), isNull); |
| expect(isolateManager.getStoredData(ref2), isNotNull); |
| }); |
| |
| test('uses uri converter to convert resolved uri', () async { |
| String? receivedUri; |
| adapter.setUriConverter((uri) { |
| receivedUri = uri; |
| return '/returned/file/path.dart'; |
| }); |
| |
| adapter.mockService.lookupResolvedPackageUrisResponse = |
| UriList(uris: ['scheme://file/path.dart']); |
| |
| final thread = isolateManager.threads[0]; |
| final resolved = |
| await thread.resolveUriToPath(Uri.parse('package:pkg/path.dart')); |
| |
| expect(adapter.mockService.receivedLookupResolvedPackageUris, |
| ['package:pkg/path.dart']); |
| 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. |
| }); |
| }); |
| } |