blob: fb66ea7a1ca34ca18a9cbfb204a8f7f993e175c0 [file] [edit]
// Copyright (c) 2022, 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 'dart:async';
import 'dart:io';
import 'package:dap/dap.dart';
import 'package:dap_adapters/src/adapters/dart.dart';
import 'package:dap_adapters/src/isolate_manager.dart';
import 'package:dap_adapters/src/protocol_stream.dart';
import 'package:path/path.dart' as path;
import 'package:test/test.dart';
import 'package:vm_service/vm_service.dart' as vm;
import 'mocks.dart';
main() {
group('dart base adapter', () {
final vmPath = Platform.resolvedExecutable;
final sdkRoot = path.dirname(path.dirname(vmPath));
group('default Dart SDK', () {
final testPath = path.join(sdkRoot, 'lib', 'core.dart');
final testUri = Uri.parse('org-dartlang-sdk:///sdk/lib/core.dart');
final adapter = MockDartCliDebugAdapter();
test('converts SDK paths to org-dartlang-sdk:///', () async {
expect(adapter.convertUriToOrgDartlangSdk(Uri.file(testPath)), testUri);
});
test('converts org-dartlang-sdk:/// to SDK paths', () async {
expect(
adapter.convertOrgDartlangSdkToPath(testUri),
Uri.file(testPath),
);
});
});
group('custom Dart SDK', () {
final testPath = path.join(sdkRoot, 'lib', 'core.dart');
final testUri = Uri.parse(
'org-dartlang-sdk:///custom-dart/sdk/lib/core.dart',
);
final defaultSdkTestUri = Uri.parse(
'org-dartlang-sdk:///sdk/lib/core.dart',
);
final adapter = MockCustomDartCliDebugAdapter({
sdkRoot: Uri.parse('org-dartlang-sdk:///custom-dart/sdk'),
});
test('converts SDK paths to custom org-dartlang-sdk:///', () async {
expect(adapter.convertUriToOrgDartlangSdk(Uri.file(testPath)), testUri);
});
test('converts custom org-dartlang-sdk:/// to SDK paths', () async {
expect(
adapter.convertOrgDartlangSdkToPath(testUri),
Uri.file(testPath),
);
});
test(
'does not convert default org-dartlang-sdk:/// to SDK paths',
() async {
expect(
adapter.convertOrgDartlangSdkToPath(defaultSdkTestUri),
isNull,
);
},
);
});
group('additional SDKs', () {
final customSdkRootPath = path.join('/my', 'flutter', 'sdk');
final customSdkRootUri = Uri.parse('org-dartlang-sdk:///flutter/sdk');
final testPath = path.join(customSdkRootPath, 'lib', 'ui.dart');
final testUri = Uri.parse('org-dartlang-sdk:///flutter/sdk/lib/ui.dart');
final adapter = MockCustomDartCliDebugAdapter({
customSdkRootPath: customSdkRootUri,
});
test(
'converts additional SDK paths to custom org-dartlang-sdk:///',
() async {
expect(
adapter.convertUriToOrgDartlangSdk(Uri.file(testPath)),
testUri,
);
},
);
test('converts additional SDK org-dartlang-sdk:/// to paths', () async {
expect(
adapter.convertOrgDartlangSdkToPath(testUri),
Uri.file(testPath),
);
});
});
group('handles DebugAdapterExceptions', () {
final request = MockRequest();
test('launch errors are shown to the user', () async {
final adapter = MockCustomDartCliDebugAdapter();
await adapter.configurationDoneRequest(request, null, () {});
final args = DartLaunchRequestArguments(
program: 'foo.dart',
vmAdditionalArgs: ['vm_arg'],
noDebug: true,
);
try {
await adapter.launchRequest(request, args, () {});
fail('Did not catch expected DebugAdapterException!');
} on DebugAdapterException catch (e) {
expect(e.showToUser, isTrue);
}
});
test('attach errors are shown to the user', () async {
final adapter = MockCustomDartCliDebugAdapter();
await adapter.configurationDoneRequest(request, null, () {});
final args = DartAttachRequestArguments(vmServiceUri: 'phony-uri');
try {
await adapter.attachRequest(request, args, () {});
fail('Did not catch expected DebugAdapterException!');
} on DebugAdapterException catch (e) {
expect(e.showToUser, isTrue);
}
});
});
test(
'getFullString falls back to the truncated value if expansion fails',
() async {
final adapter = MockDartCliDebugAdapter()
..vmService = ThrowingInvokeVmService();
final thread = ThreadInfo(
adapter.isolateManager,
1,
vm.IsolateRef(id: 'isolate1'),
);
final result = await adapter.getFullString(
thread,
vm.InstanceRef(
id: 'string1',
kind: vm.InstanceKind.kString,
valueAsString: 'truncated value',
valueAsStringIsTruncated: true,
),
);
expect(result, 'truncated value…');
},
);
});
}
/// A version of [MockVmService] that throws when [invoke] is called.
class ThrowingInvokeVmService extends MockVmService {
@override
Future<vm.Response> invoke(
String isolateId,
String targetId,
String selector,
List<String> argumentIds, {
bool? disableBreakpoints,
String? idZoneId,
}) {
throw StateError('forced failure fetching full string');
}
}
class MockCustomDartCliDebugAdapter extends MockDartCliDebugAdapter {
factory MockCustomDartCliDebugAdapter([Map<String, Uri>? customMappings]) {
final stdinController = StreamController<List<int>>();
final stdoutController = StreamController<List<int>>();
final channel = ByteStreamServerChannel(
stdinController.stream,
stdoutController.sink,
null,
);
return MockCustomDartCliDebugAdapter._(
customMappings,
stdinController.sink,
stdoutController.stream,
channel,
);
}
MockCustomDartCliDebugAdapter._(
Map<String, Uri>? customMappings,
StreamSink<List<int>> stdin,
Stream<List<int>> stdout,
ByteStreamServerChannel channel,
) : super.withStreams(stdin, stdout, channel) {
if (customMappings != null) {
orgDartlangSdkMappings
..clear()
..addAll(customMappings);
}
}
@override
Future<void> attachImpl() {
throw DebugAdapterException('Unexpected attach error!', showToUser: false);
}
@override
Future<void> launchAndRespond(void Function() _) {
throw DebugAdapterException('Unexpected launch error!', showToUser: false);
}
}