mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 00:39:49 +00:00
[dds] Switch DAP server mode to directly using stdin/stdout
Change-Id: Id2a8d8c9274b96cf32788e11d4452954a4f9c091 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/205500 Reviewed-by: Ben Konyi <bkonyi@google.com>
This commit is contained in:
parent
66bd92dcaa
commit
6b4fc2670c
|
@ -53,7 +53,9 @@ class DartCliDebugAdapter extends DartDebugAdapter<DartLaunchRequestArguments> {
|
|||
enableDds: enableDds,
|
||||
enableAuthCodes: enableAuthCodes,
|
||||
logger: logger,
|
||||
);
|
||||
) {
|
||||
channel.closed.then((_) => shutdown());
|
||||
}
|
||||
|
||||
Future<void> debuggerConnected(vm.VM vmInfo) async {
|
||||
if (!isAttach) {
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
class InvalidEncodingError {
|
||||
final String headers;
|
||||
|
@ -107,28 +106,3 @@ class ProtocolHeaders {
|
|||
final String? encoding;
|
||||
ProtocolHeaders(this.rawHeaders, this.contentLength, this.encoding);
|
||||
}
|
||||
|
||||
/// Transforms a stream of [Uint8List]s to [List<int>]s. Used because
|
||||
/// [ServerSocket] and [Socket] use [Uint8List] but stdin and stdout use
|
||||
/// [List<int>] and the LSP server needs to operate against both.
|
||||
class Uint8ListTransformer extends StreamTransformerBase<Uint8List, List<int>> {
|
||||
// TODO(dantup): Is there a built-in (or better) way to do this?
|
||||
@override
|
||||
Stream<List<int>> bind(Stream<Uint8List> stream) {
|
||||
late StreamSubscription<Uint8List> input;
|
||||
late StreamController<List<int>> _output;
|
||||
_output = StreamController<List<int>>(
|
||||
onListen: () {
|
||||
input = stream.listen(
|
||||
(uints) => _output.add(uints),
|
||||
onError: _output.addError,
|
||||
onDone: _output.close,
|
||||
);
|
||||
},
|
||||
onPause: () => input.pause(),
|
||||
onResume: () => input.resume(),
|
||||
onCancel: () => input.cancel(),
|
||||
);
|
||||
return _output.stream;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,92 +3,45 @@
|
|||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dds/src/dap/adapters/dart.dart';
|
||||
import 'package:pedantic/pedantic.dart';
|
||||
|
||||
import 'adapters/dart_cli.dart';
|
||||
import 'logging.dart';
|
||||
import 'protocol_stream.dart';
|
||||
import 'protocol_stream_transformers.dart';
|
||||
|
||||
/// A DAP server that binds to a port and runs in multi-session mode.
|
||||
/// A DAP server that communicates over a [ByteStreamServerChannel], usually
|
||||
/// constructed from the processes stdin/stdout streams.
|
||||
///
|
||||
/// The server runs in single-user mode and services only a single client. For
|
||||
/// multiple debug sessions, there would be multiple servers (and the editor
|
||||
/// would have a client for each of them).
|
||||
class DapServer {
|
||||
final ServerSocket _socket;
|
||||
final ByteStreamServerChannel channel;
|
||||
late final DartDebugAdapter adapter;
|
||||
final bool ipv6;
|
||||
final bool enableDds;
|
||||
final bool enableAuthCodes;
|
||||
final Logger? logger;
|
||||
final _channels = <ByteStreamServerChannel>{};
|
||||
final _adapters = <DartDebugAdapter>{};
|
||||
|
||||
DapServer._(
|
||||
this._socket, {
|
||||
DapServer(
|
||||
Stream<List<int>> _input,
|
||||
StreamSink<List<int>> _output, {
|
||||
this.ipv6 = false,
|
||||
this.enableDds = true,
|
||||
this.enableAuthCodes = true,
|
||||
this.logger,
|
||||
}) {
|
||||
_socket.listen(_acceptConnection);
|
||||
}
|
||||
|
||||
String get host => _socket.address.host;
|
||||
int get port => _socket.port;
|
||||
|
||||
Future<void> stop() async {
|
||||
_channels.forEach((client) => client.close());
|
||||
await _socket.close();
|
||||
}
|
||||
|
||||
void _acceptConnection(Socket client) {
|
||||
final address = client.remoteAddress;
|
||||
logger?.call('Accepted connection from $address');
|
||||
client.done.then((_) {
|
||||
logger?.call('Connection from $address closed');
|
||||
});
|
||||
_createAdapter(client.transform(Uint8ListTransformer()), client);
|
||||
}
|
||||
|
||||
void _createAdapter(Stream<List<int>> _input, StreamSink<List<int>> _output) {
|
||||
// TODO(dantup): This is hard-coded to DartCliDebugAdapter but will
|
||||
// ultimately need to support having a factory passed in to support
|
||||
// tests and/or being used in flutter_tools.
|
||||
final channel = ByteStreamServerChannel(_input, _output, logger);
|
||||
final adapter = DartCliDebugAdapter(
|
||||
}) : channel = ByteStreamServerChannel(_input, _output, logger) {
|
||||
adapter = DartCliDebugAdapter(
|
||||
channel,
|
||||
ipv6: ipv6,
|
||||
enableDds: enableDds,
|
||||
enableAuthCodes: enableAuthCodes,
|
||||
logger: logger,
|
||||
);
|
||||
_channels.add(channel);
|
||||
_adapters.add(adapter);
|
||||
unawaited(channel.closed.then((_) {
|
||||
_channels.remove(channel);
|
||||
_adapters.remove(adapter);
|
||||
adapter.shutdown();
|
||||
}));
|
||||
}
|
||||
|
||||
/// Starts a DAP Server listening on [host]:[port].
|
||||
static Future<DapServer> create({
|
||||
String? host,
|
||||
int port = 0,
|
||||
bool ipv6 = false,
|
||||
bool enableDdds = true,
|
||||
bool enableAuthCodes = true,
|
||||
Logger? logger,
|
||||
}) async {
|
||||
final hostFallback =
|
||||
ipv6 ? InternetAddress.loopbackIPv6 : InternetAddress.loopbackIPv4;
|
||||
final _socket = await ServerSocket.bind(host ?? hostFallback, port);
|
||||
return DapServer._(
|
||||
_socket,
|
||||
ipv6: ipv6,
|
||||
enableDds: enableDdds,
|
||||
enableAuthCodes: enableAuthCodes,
|
||||
logger: logger,
|
||||
);
|
||||
void stop() {
|
||||
channel.close();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,56 +9,61 @@ import 'test_scripts.dart';
|
|||
import 'test_support.dart';
|
||||
|
||||
main() {
|
||||
testDap((dap) async {
|
||||
group('debug mode breakpoints', () {
|
||||
test('stops at a line breakpoint', () async {
|
||||
final client = dap.client;
|
||||
final testFile = dap.createTestFile(simpleBreakpointProgram);
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
late DapTestSession dap;
|
||||
setUp(() async {
|
||||
dap = await DapTestSession.setUp();
|
||||
});
|
||||
tearDown(() => dap.tearDown());
|
||||
|
||||
await client.hitBreakpoint(testFile, breakpointLine);
|
||||
});
|
||||
group('debug mode breakpoints', () {
|
||||
test('stops at a line breakpoint', () async {
|
||||
final client = dap.client;
|
||||
final testFile = dap.createTestFile(simpleBreakpointProgram);
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
|
||||
test('stops at a line breakpoint and can be resumed', () async {
|
||||
final client = dap.client;
|
||||
final testFile = dap.createTestFile(simpleBreakpointProgram);
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
await client.hitBreakpoint(testFile, breakpointLine);
|
||||
});
|
||||
|
||||
// Hit the initial breakpoint.
|
||||
final stop = await client.hitBreakpoint(testFile, breakpointLine);
|
||||
test('stops at a line breakpoint and can be resumed', () async {
|
||||
final client = dap.client;
|
||||
final testFile = dap.createTestFile(simpleBreakpointProgram);
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
|
||||
// Resume and expect termination (as the script will get to the end).
|
||||
await Future.wait([
|
||||
client.event('terminated'),
|
||||
client.continue_(stop.threadId!),
|
||||
], eagerError: true);
|
||||
});
|
||||
// Hit the initial breakpoint.
|
||||
final stop = await client.hitBreakpoint(testFile, breakpointLine);
|
||||
|
||||
test('stops at a line breakpoint and can step over (next)', () async {
|
||||
final testFile = dap.createTestFile(r'''
|
||||
// Resume and expect termination (as the script will get to the end).
|
||||
await Future.wait([
|
||||
client.event('terminated'),
|
||||
client.continue_(stop.threadId!),
|
||||
], eagerError: true);
|
||||
});
|
||||
|
||||
test('stops at a line breakpoint and can step over (next)', () async {
|
||||
final testFile = dap.createTestFile(r'''
|
||||
void main(List<String> args) async {
|
||||
print('Hello!'); // BREAKPOINT
|
||||
print('Hello!'); // STEP
|
||||
}
|
||||
''');
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
final stepLine = lineWith(testFile, '// STEP');
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
final stepLine = lineWith(testFile, '// STEP');
|
||||
|
||||
// Hit the initial breakpoint.
|
||||
final stop = await dap.client.hitBreakpoint(testFile, breakpointLine);
|
||||
// Hit the initial breakpoint.
|
||||
final stop = await dap.client.hitBreakpoint(testFile, breakpointLine);
|
||||
|
||||
// Step and expect stopping on the next line with a 'step' stop type.
|
||||
await Future.wait([
|
||||
dap.client.expectStop('step', file: testFile, line: stepLine),
|
||||
dap.client.next(stop.threadId!),
|
||||
], eagerError: true);
|
||||
});
|
||||
// Step and expect stopping on the next line with a 'step' stop type.
|
||||
await Future.wait([
|
||||
dap.client.expectStop('step', file: testFile, line: stepLine),
|
||||
dap.client.next(stop.threadId!),
|
||||
], eagerError: true);
|
||||
});
|
||||
|
||||
test(
|
||||
'stops at a line breakpoint and can step over (next) an async boundary',
|
||||
() async {
|
||||
final client = dap.client;
|
||||
final testFile = dap.createTestFile(r'''
|
||||
test(
|
||||
'stops at a line breakpoint and can step over (next) an async boundary',
|
||||
() async {
|
||||
final client = dap.client;
|
||||
final testFile = dap.createTestFile(r'''
|
||||
Future<void> main(List<String> args) async {
|
||||
await asyncPrint('Hello!'); // BREAKPOINT
|
||||
await asyncPrint('Hello!'); // STEP
|
||||
|
@ -68,30 +73,30 @@ Future<void> asyncPrint(String message) async {
|
|||
await Future.delayed(const Duration(milliseconds: 1));
|
||||
}
|
||||
''');
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
final stepLine = lineWith(testFile, '// STEP');
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
final stepLine = lineWith(testFile, '// STEP');
|
||||
|
||||
// Hit the initial breakpoint.
|
||||
final stop = await dap.client.hitBreakpoint(testFile, breakpointLine);
|
||||
// Hit the initial breakpoint.
|
||||
final stop = await dap.client.hitBreakpoint(testFile, breakpointLine);
|
||||
|
||||
// The first step will move from `asyncPrint` to the `await`.
|
||||
await Future.wait([
|
||||
client.expectStop('step', file: testFile, line: breakpointLine),
|
||||
client.next(stop.threadId!),
|
||||
], eagerError: true);
|
||||
// The first step will move from `asyncPrint` to the `await`.
|
||||
await Future.wait([
|
||||
client.expectStop('step', file: testFile, line: breakpointLine),
|
||||
client.next(stop.threadId!),
|
||||
], eagerError: true);
|
||||
|
||||
// The next step should go over the async boundary and to stepLine (if
|
||||
// we did not correctly send kOverAsyncSuspension we would end up in
|
||||
// the asyncPrint method).
|
||||
await Future.wait([
|
||||
client.expectStop('step', file: testFile, line: stepLine),
|
||||
client.next(stop.threadId!),
|
||||
], eagerError: true);
|
||||
});
|
||||
// The next step should go over the async boundary and to stepLine (if
|
||||
// we did not correctly send kOverAsyncSuspension we would end up in
|
||||
// the asyncPrint method).
|
||||
await Future.wait([
|
||||
client.expectStop('step', file: testFile, line: stepLine),
|
||||
client.next(stop.threadId!),
|
||||
], eagerError: true);
|
||||
});
|
||||
|
||||
test('stops at a line breakpoint and can step in', () async {
|
||||
final client = dap.client;
|
||||
final testFile = dap.createTestFile(r'''
|
||||
test('stops at a line breakpoint and can step in', () async {
|
||||
final client = dap.client;
|
||||
final testFile = dap.createTestFile(r'''
|
||||
void main(List<String> args) async {
|
||||
log('Hello!'); // BREAKPOINT
|
||||
}
|
||||
|
@ -100,22 +105,22 @@ void log(String message) { // STEP
|
|||
print(message);
|
||||
}
|
||||
''');
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
final stepLine = lineWith(testFile, '// STEP');
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
final stepLine = lineWith(testFile, '// STEP');
|
||||
|
||||
// Hit the initial breakpoint.
|
||||
final stop = await client.hitBreakpoint(testFile, breakpointLine);
|
||||
// Hit the initial breakpoint.
|
||||
final stop = await client.hitBreakpoint(testFile, breakpointLine);
|
||||
|
||||
// Step and expect stopping in the inner function with a 'step' stop type.
|
||||
await Future.wait([
|
||||
client.expectStop('step', file: testFile, line: stepLine),
|
||||
client.stepIn(stop.threadId!),
|
||||
], eagerError: true);
|
||||
});
|
||||
// Step and expect stopping in the inner function with a 'step' stop type.
|
||||
await Future.wait([
|
||||
client.expectStop('step', file: testFile, line: stepLine),
|
||||
client.stepIn(stop.threadId!),
|
||||
], eagerError: true);
|
||||
});
|
||||
|
||||
test('stops at a line breakpoint and can step out', () async {
|
||||
final client = dap.client;
|
||||
final testFile = dap.createTestFile(r'''
|
||||
test('stops at a line breakpoint and can step out', () async {
|
||||
final client = dap.client;
|
||||
final testFile = dap.createTestFile(r'''
|
||||
void main(List<String> args) async {
|
||||
log('Hello!');
|
||||
log('Hello!'); // STEP
|
||||
|
@ -125,87 +130,85 @@ void log(String message) {
|
|||
print(message); // BREAKPOINT
|
||||
}
|
||||
''');
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
final stepLine = lineWith(testFile, '// STEP');
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
final stepLine = lineWith(testFile, '// STEP');
|
||||
|
||||
// Hit the initial breakpoint.
|
||||
final stop = await client.hitBreakpoint(testFile, breakpointLine);
|
||||
// Hit the initial breakpoint.
|
||||
final stop = await client.hitBreakpoint(testFile, breakpointLine);
|
||||
|
||||
// Step and expect stopping in the inner function with a 'step' stop type.
|
||||
await Future.wait([
|
||||
client.expectStop('step', file: testFile, line: stepLine),
|
||||
client.stepOut(stop.threadId!),
|
||||
], eagerError: true);
|
||||
});
|
||||
// Step and expect stopping in the inner function with a 'step' stop type.
|
||||
await Future.wait([
|
||||
client.expectStop('step', file: testFile, line: stepLine),
|
||||
client.stepOut(stop.threadId!),
|
||||
], eagerError: true);
|
||||
});
|
||||
|
||||
test('does not step into SDK code with debugSdkLibraries=false',
|
||||
() async {
|
||||
final client = dap.client;
|
||||
final testFile = dap.createTestFile(r'''
|
||||
test('does not step into SDK code with debugSdkLibraries=false', () async {
|
||||
final client = dap.client;
|
||||
final testFile = dap.createTestFile(r'''
|
||||
void main(List<String> args) async {
|
||||
print('Hello!'); // BREAKPOINT
|
||||
print('Hello!'); // STEP
|
||||
}
|
||||
''');
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
final stepLine = lineWith(testFile, '// STEP');
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
final stepLine = lineWith(testFile, '// STEP');
|
||||
|
||||
// Hit the initial breakpoint.
|
||||
final stop = await client.hitBreakpoint(testFile, breakpointLine);
|
||||
// Hit the initial breakpoint.
|
||||
final stop = await client.hitBreakpoint(testFile, breakpointLine);
|
||||
|
||||
// Step in and expect stopping on the next line (don't go into print).
|
||||
await Future.wait([
|
||||
client.expectStop('step', file: testFile, line: stepLine),
|
||||
client.stepIn(stop.threadId!),
|
||||
], eagerError: true);
|
||||
});
|
||||
// Step in and expect stopping on the next line (don't go into print).
|
||||
await Future.wait([
|
||||
client.expectStop('step', file: testFile, line: stepLine),
|
||||
client.stepIn(stop.threadId!),
|
||||
], eagerError: true);
|
||||
});
|
||||
|
||||
test('steps into SDK code with debugSdkLibraries=true', () async {
|
||||
final client = dap.client;
|
||||
final testFile = dap.createTestFile(r'''
|
||||
test('steps into SDK code with debugSdkLibraries=true', () async {
|
||||
final client = dap.client;
|
||||
final testFile = dap.createTestFile(r'''
|
||||
void main(List<String> args) async {
|
||||
print('Hello!'); // BREAKPOINT
|
||||
print('Hello!');
|
||||
}
|
||||
''');
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
|
||||
// Hit the initial breakpoint.
|
||||
final stop = await dap.client.hitBreakpoint(
|
||||
testFile,
|
||||
breakpointLine,
|
||||
launch: () => client.launch(
|
||||
testFile.path,
|
||||
debugSdkLibraries: true,
|
||||
),
|
||||
);
|
||||
// Hit the initial breakpoint.
|
||||
final stop = await dap.client.hitBreakpoint(
|
||||
testFile,
|
||||
breakpointLine,
|
||||
launch: () => client.launch(
|
||||
testFile.path,
|
||||
debugSdkLibraries: true,
|
||||
),
|
||||
);
|
||||
|
||||
// Step in and expect to go into print.
|
||||
await Future.wait([
|
||||
client.expectStop('step', sourceName: 'dart:core/print.dart'),
|
||||
client.stepIn(stop.threadId!),
|
||||
], eagerError: true);
|
||||
});
|
||||
// Step in and expect to go into print.
|
||||
await Future.wait([
|
||||
client.expectStop('step', sourceName: 'dart:core/print.dart'),
|
||||
client.stepIn(stop.threadId!),
|
||||
], eagerError: true);
|
||||
});
|
||||
|
||||
test(
|
||||
'does not step into external package code with debugExternalPackageLibraries=false',
|
||||
() {
|
||||
// TODO(dantup): Support for debugExternalPackageLibraries
|
||||
}, skip: true);
|
||||
test(
|
||||
'does not step into external package code with debugExternalPackageLibraries=false',
|
||||
() {
|
||||
// TODO(dantup): Support for debugExternalPackageLibraries
|
||||
}, skip: true);
|
||||
|
||||
test(
|
||||
'steps into external package code with debugExternalPackageLibraries=true',
|
||||
() {
|
||||
// TODO(dantup): Support for debugExternalPackageLibraries
|
||||
}, skip: true);
|
||||
test(
|
||||
'steps into external package code with debugExternalPackageLibraries=true',
|
||||
() {
|
||||
// TODO(dantup): Support for debugExternalPackageLibraries
|
||||
}, skip: true);
|
||||
|
||||
test('allows changing debug settings during session', () {
|
||||
// TODO(dantup): !
|
||||
// Dart-Code's DAP has a custom method that allows an editor to change
|
||||
// the debug settings (debugSdkLibraries/debugExternalPackageLibraries)
|
||||
// during a debug session.
|
||||
}, skip: true);
|
||||
// These tests can be slow due to starting up the external server process.
|
||||
}, timeout: Timeout.none);
|
||||
});
|
||||
test('allows changing debug settings during session', () {
|
||||
// TODO(dantup): !
|
||||
// Dart-Code's DAP has a custom method that allows an editor to change
|
||||
// the debug settings (debugSdkLibraries/debugExternalPackageLibraries)
|
||||
// during a debug session.
|
||||
}, skip: true);
|
||||
// These tests can be slow due to starting up the external server process.
|
||||
}, timeout: Timeout.none);
|
||||
}
|
||||
|
|
|
@ -10,126 +10,131 @@ import 'test_scripts.dart';
|
|||
import 'test_support.dart';
|
||||
|
||||
main() {
|
||||
testDap((dap) async {
|
||||
group('debug mode evaluation', () {
|
||||
test('evaluates expressions with simple results', () async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(r'''
|
||||
late DapTestSession dap;
|
||||
setUp(() async {
|
||||
dap = await DapTestSession.setUp();
|
||||
});
|
||||
tearDown(() => dap.tearDown());
|
||||
|
||||
group('debug mode evaluation', () {
|
||||
test('evaluates expressions with simple results', () async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(r'''
|
||||
void main(List<String> args) {
|
||||
var a = 1;
|
||||
var b = 2;
|
||||
var c = 'test';
|
||||
print('Hello!'); // BREAKPOINT
|
||||
}''');
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
|
||||
final stop = await client.hitBreakpoint(testFile, breakpointLine);
|
||||
await client.expectTopFrameEvalResult(stop.threadId!, 'a', '1');
|
||||
await client.expectTopFrameEvalResult(stop.threadId!, 'a * b', '2');
|
||||
await client.expectTopFrameEvalResult(stop.threadId!, 'c', '"test"');
|
||||
});
|
||||
final stop = await client.hitBreakpoint(testFile, breakpointLine);
|
||||
await client.expectTopFrameEvalResult(stop.threadId!, 'a', '1');
|
||||
await client.expectTopFrameEvalResult(stop.threadId!, 'a * b', '2');
|
||||
await client.expectTopFrameEvalResult(stop.threadId!, 'c', '"test"');
|
||||
});
|
||||
|
||||
test('evaluates expressions with complex results', () async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(simpleBreakpointProgram);
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
test('evaluates expressions with complex results', () async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(simpleBreakpointProgram);
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
|
||||
final stop = await client.hitBreakpoint(testFile, breakpointLine);
|
||||
final result = await client.expectTopFrameEvalResult(
|
||||
stop.threadId!,
|
||||
'DateTime(2000, 1, 1)',
|
||||
'DateTime',
|
||||
);
|
||||
final stop = await client.hitBreakpoint(testFile, breakpointLine);
|
||||
final result = await client.expectTopFrameEvalResult(
|
||||
stop.threadId!,
|
||||
'DateTime(2000, 1, 1)',
|
||||
'DateTime',
|
||||
);
|
||||
|
||||
// Check we got a variablesReference that maps on to the fields.
|
||||
expect(result.variablesReference, greaterThan(0));
|
||||
await client.expectVariables(
|
||||
result.variablesReference,
|
||||
'''
|
||||
// Check we got a variablesReference that maps on to the fields.
|
||||
expect(result.variablesReference, greaterThan(0));
|
||||
await client.expectVariables(
|
||||
result.variablesReference,
|
||||
'''
|
||||
isUtc: false
|
||||
''',
|
||||
);
|
||||
});
|
||||
);
|
||||
});
|
||||
|
||||
test(
|
||||
'evaluates complex expressions expressions with evaluateToStringInDebugViews=true',
|
||||
() async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(simpleBreakpointProgram);
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
test(
|
||||
'evaluates complex expressions expressions with evaluateToStringInDebugViews=true',
|
||||
() async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(simpleBreakpointProgram);
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
|
||||
final stop = await client.hitBreakpoint(
|
||||
testFile,
|
||||
breakpointLine,
|
||||
launch: () =>
|
||||
client.launch(testFile.path, evaluateToStringInDebugViews: true),
|
||||
);
|
||||
final stop = await client.hitBreakpoint(
|
||||
testFile,
|
||||
breakpointLine,
|
||||
launch: () =>
|
||||
client.launch(testFile.path, evaluateToStringInDebugViews: true),
|
||||
);
|
||||
|
||||
await client.expectTopFrameEvalResult(
|
||||
stop.threadId!,
|
||||
'DateTime(2000, 1, 1)',
|
||||
'DateTime (2000-01-01 00:00:00.000)',
|
||||
);
|
||||
});
|
||||
await client.expectTopFrameEvalResult(
|
||||
stop.threadId!,
|
||||
'DateTime(2000, 1, 1)',
|
||||
'DateTime (2000-01-01 00:00:00.000)',
|
||||
);
|
||||
});
|
||||
|
||||
test(
|
||||
'evaluates $threadExceptionExpression to the threads exception (simple type)',
|
||||
() async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(r'''
|
||||
test(
|
||||
'evaluates $threadExceptionExpression to the threads exception (simple type)',
|
||||
() async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(r'''
|
||||
void main(List<String> args) {
|
||||
throw 'my error';
|
||||
}''');
|
||||
|
||||
final stop = await client.hitException(testFile);
|
||||
final stop = await client.hitException(testFile);
|
||||
|
||||
final result = await client.expectTopFrameEvalResult(
|
||||
stop.threadId!,
|
||||
threadExceptionExpression,
|
||||
'"my error"',
|
||||
);
|
||||
expect(result.variablesReference, equals(0));
|
||||
});
|
||||
final result = await client.expectTopFrameEvalResult(
|
||||
stop.threadId!,
|
||||
threadExceptionExpression,
|
||||
'"my error"',
|
||||
);
|
||||
expect(result.variablesReference, equals(0));
|
||||
});
|
||||
|
||||
test(
|
||||
'evaluates $threadExceptionExpression to the threads exception (complex type)',
|
||||
() async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(r'''
|
||||
test(
|
||||
'evaluates $threadExceptionExpression to the threads exception (complex type)',
|
||||
() async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(r'''
|
||||
void main(List<String> args) {
|
||||
throw Exception('my error');
|
||||
}''');
|
||||
|
||||
final stop = await client.hitException(testFile);
|
||||
final result = await client.expectTopFrameEvalResult(
|
||||
stop.threadId!,
|
||||
threadExceptionExpression,
|
||||
'_Exception',
|
||||
);
|
||||
expect(result.variablesReference, greaterThan(0));
|
||||
});
|
||||
final stop = await client.hitException(testFile);
|
||||
final result = await client.expectTopFrameEvalResult(
|
||||
stop.threadId!,
|
||||
threadExceptionExpression,
|
||||
'_Exception',
|
||||
);
|
||||
expect(result.variablesReference, greaterThan(0));
|
||||
});
|
||||
|
||||
test(
|
||||
'evaluates $threadExceptionExpression.x.y to x.y on the threads exception',
|
||||
() async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(r'''
|
||||
test(
|
||||
'evaluates $threadExceptionExpression.x.y to x.y on the threads exception',
|
||||
() async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(r'''
|
||||
void main(List<String> args) {
|
||||
throw Exception('12345');
|
||||
}
|
||||
''');
|
||||
|
||||
final stop = await client.hitException(testFile);
|
||||
await client.expectTopFrameEvalResult(
|
||||
stop.threadId!,
|
||||
'$threadExceptionExpression.message.length',
|
||||
'5',
|
||||
);
|
||||
});
|
||||
final stop = await client.hitException(testFile);
|
||||
await client.expectTopFrameEvalResult(
|
||||
stop.threadId!,
|
||||
'$threadExceptionExpression.message.length',
|
||||
'5',
|
||||
);
|
||||
});
|
||||
|
||||
test('can evaluate expressions in non-top frames', () async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(r'''
|
||||
test('can evaluate expressions in non-top frames', () async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(r'''
|
||||
void main(List<String> args) {
|
||||
var a = 999;
|
||||
foo();
|
||||
|
@ -138,17 +143,16 @@ void main(List<String> args) {
|
|||
void foo() {
|
||||
var a = 111; // BREAKPOINT
|
||||
}''');
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
|
||||
final stop = await client.hitBreakpoint(testFile, breakpointLine);
|
||||
final stack = await client.getValidStack(stop.threadId!,
|
||||
startFrame: 0, numFrames: 2);
|
||||
final secondFrameId = stack.stackFrames[1].id;
|
||||
final stop = await client.hitBreakpoint(testFile, breakpointLine);
|
||||
final stack = await client.getValidStack(stop.threadId!,
|
||||
startFrame: 0, numFrames: 2);
|
||||
final secondFrameId = stack.stackFrames[1].id;
|
||||
|
||||
await client.expectEvalResult(secondFrameId, 'a', '999');
|
||||
});
|
||||
await client.expectEvalResult(secondFrameId, 'a', '999');
|
||||
});
|
||||
|
||||
// These tests can be slow due to starting up the external server process.
|
||||
}, timeout: Timeout.none);
|
||||
});
|
||||
// These tests can be slow due to starting up the external server process.
|
||||
}, timeout: Timeout.none);
|
||||
}
|
||||
|
|
|
@ -7,10 +7,15 @@ import 'package:test/test.dart';
|
|||
import 'test_support.dart';
|
||||
|
||||
main() {
|
||||
testDap((dap) async {
|
||||
group('debug mode', () {
|
||||
test('prints messages from dart:developer log()', () async {
|
||||
final testFile = dap.createTestFile(r'''
|
||||
late DapTestSession dap;
|
||||
setUp(() async {
|
||||
dap = await DapTestSession.setUp();
|
||||
});
|
||||
tearDown(() => dap.tearDown());
|
||||
|
||||
group('debug mode', () {
|
||||
test('prints messages from dart:developer log()', () async {
|
||||
final testFile = dap.createTestFile(r'''
|
||||
import 'dart:developer';
|
||||
|
||||
void main(List<String> args) async {
|
||||
|
@ -19,19 +24,18 @@ void main(List<String> args) async {
|
|||
}
|
||||
''');
|
||||
|
||||
var outputEvents = await dap.client.collectOutput(file: testFile);
|
||||
var outputEvents = await dap.client.collectOutput(file: testFile);
|
||||
|
||||
// Skip the first line because it's the VM Service connection info.
|
||||
final output = outputEvents.skip(1).map((e) => e.output).join();
|
||||
expectLines(output, [
|
||||
'[log] this is a test',
|
||||
' across two lines',
|
||||
'[foo] this is a test',
|
||||
'',
|
||||
'Exited.',
|
||||
]);
|
||||
});
|
||||
// These tests can be slow due to starting up the external server process.
|
||||
}, timeout: Timeout.none);
|
||||
});
|
||||
// Skip the first line because it's the VM Service connection info.
|
||||
final output = outputEvents.skip(1).map((e) => e.output).join();
|
||||
expectLines(output, [
|
||||
'[log] this is a test',
|
||||
' across two lines',
|
||||
'[foo] this is a test',
|
||||
'',
|
||||
'Exited.',
|
||||
]);
|
||||
});
|
||||
// These tests can be slow due to starting up the external server process.
|
||||
}, timeout: Timeout.none);
|
||||
}
|
||||
|
|
|
@ -10,10 +10,15 @@ import 'test_scripts.dart';
|
|||
import 'test_support.dart';
|
||||
|
||||
main() {
|
||||
testDap((dap) async {
|
||||
group('debug mode', () {
|
||||
test('runs a simple script', () async {
|
||||
final testFile = dap.createTestFile(r'''
|
||||
group('debug mode', () {
|
||||
late DapTestSession dap;
|
||||
setUp(() async {
|
||||
dap = await DapTestSession.setUp();
|
||||
});
|
||||
tearDown(() => dap.tearDown());
|
||||
|
||||
test('runs a simple script', () async {
|
||||
final testFile = dap.createTestFile(r'''
|
||||
void main(List<String> args) async {
|
||||
print('Hello!');
|
||||
print('World!');
|
||||
|
@ -21,100 +26,99 @@ void main(List<String> args) async {
|
|||
}
|
||||
''');
|
||||
|
||||
final outputEvents = await dap.client.collectOutput(
|
||||
launch: () => dap.client.launch(
|
||||
testFile.path,
|
||||
args: ['one', 'two'],
|
||||
),
|
||||
);
|
||||
final outputEvents = await dap.client.collectOutput(
|
||||
launch: () => dap.client.launch(
|
||||
testFile.path,
|
||||
args: ['one', 'two'],
|
||||
),
|
||||
);
|
||||
|
||||
// Expect a "console" output event that prints the URI of the VM Service
|
||||
// the debugger connects to.
|
||||
final vmConnection = outputEvents.first;
|
||||
expect(vmConnection.output,
|
||||
startsWith('Connecting to VM Service at ws://127.0.0.1:'));
|
||||
expect(vmConnection.category, equals('console'));
|
||||
// Expect a "console" output event that prints the URI of the VM Service
|
||||
// the debugger connects to.
|
||||
final vmConnection = outputEvents.first;
|
||||
expect(vmConnection.output,
|
||||
startsWith('Connecting to VM Service at ws://127.0.0.1:'));
|
||||
expect(vmConnection.category, equals('console'));
|
||||
|
||||
// Expect the normal applications output.
|
||||
final output = outputEvents.skip(1).map((e) => e.output).join();
|
||||
expectLines(output, [
|
||||
'Hello!',
|
||||
'World!',
|
||||
'args: [one, two]',
|
||||
'',
|
||||
'Exited.',
|
||||
]);
|
||||
});
|
||||
// Expect the normal applications output.
|
||||
final output = outputEvents.skip(1).map((e) => e.output).join();
|
||||
expectLines(output, [
|
||||
'Hello!',
|
||||
'World!',
|
||||
'args: [one, two]',
|
||||
'',
|
||||
'Exited.',
|
||||
]);
|
||||
});
|
||||
|
||||
test('provides a list of threads', () async {
|
||||
final client = dap.client;
|
||||
final testFile = dap.createTestFile(simpleBreakpointProgram);
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
test('provides a list of threads', () async {
|
||||
final client = dap.client;
|
||||
final testFile = dap.createTestFile(simpleBreakpointProgram);
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
|
||||
await client.hitBreakpoint(testFile, breakpointLine);
|
||||
final response = await client.getValidThreads();
|
||||
await client.hitBreakpoint(testFile, breakpointLine);
|
||||
final response = await client.getValidThreads();
|
||||
|
||||
expect(response.threads, hasLength(1));
|
||||
expect(response.threads.first.name, equals('main'));
|
||||
});
|
||||
expect(response.threads, hasLength(1));
|
||||
expect(response.threads.first.name, equals('main'));
|
||||
});
|
||||
|
||||
test('runs with DDS', () async {
|
||||
final client = dap.client;
|
||||
final testFile = dap.createTestFile(simpleBreakpointProgram);
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
test('runs with DDS by default', () async {
|
||||
final client = dap.client;
|
||||
final testFile = dap.createTestFile(simpleBreakpointProgram);
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
|
||||
await client.hitBreakpoint(testFile, breakpointLine);
|
||||
expect(await client.ddsAvailable, isTrue);
|
||||
});
|
||||
// These tests can be slow due to starting up the external server process.
|
||||
}, timeout: Timeout.none);
|
||||
await client.hitBreakpoint(testFile, breakpointLine);
|
||||
expect(await client.ddsAvailable, isTrue);
|
||||
});
|
||||
|
||||
test('runs with auth codes enabled', () async {
|
||||
test('runs with auth codes enabled by default', () async {
|
||||
final testFile = dap.createTestFile(emptyProgram);
|
||||
|
||||
final outputEvents = await dap.client.collectOutput(file: testFile);
|
||||
expect(_hasAuthCode(outputEvents.first), isTrue);
|
||||
final vmServiceUri = _extractVmServiceUri(outputEvents.first);
|
||||
expect(vmServiceUri.path, matches(vmServiceAuthCodePathPattern));
|
||||
});
|
||||
});
|
||||
// These tests can be slow due to starting up the external server process.
|
||||
}, timeout: Timeout.none);
|
||||
|
||||
testDap((dap) async {
|
||||
group('debug mode', () {
|
||||
test('runs without DDS', () async {
|
||||
final client = dap.client;
|
||||
final testFile = dap.createTestFile(simpleBreakpointProgram);
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
group('debug mode', () {
|
||||
test('can run without DDS', () async {
|
||||
final dap = await DapTestSession.setUp(additionalArgs: ['--no-dds']);
|
||||
addTearDown(dap.tearDown);
|
||||
|
||||
await client.hitBreakpoint(testFile, breakpointLine);
|
||||
final client = dap.client;
|
||||
final testFile = dap.createTestFile(simpleBreakpointProgram);
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
|
||||
expect(await client.ddsAvailable, isFalse);
|
||||
});
|
||||
await client.hitBreakpoint(testFile, breakpointLine);
|
||||
|
||||
test('runs with auth tokens disabled', () async {
|
||||
final testFile = dap.createTestFile(emptyProgram);
|
||||
expect(await client.ddsAvailable, isFalse);
|
||||
});
|
||||
|
||||
final outputEvents = await dap.client.collectOutput(file: testFile);
|
||||
expect(_hasAuthCode(outputEvents.first), isFalse);
|
||||
});
|
||||
// These tests can be slow due to starting up the external server process.
|
||||
}, timeout: Timeout.none);
|
||||
}, additionalArgs: ['--no-dds', '--no-auth-codes']);
|
||||
test('can run without auth codes', () async {
|
||||
final dap =
|
||||
await DapTestSession.setUp(additionalArgs: ['--no-auth-codes']);
|
||||
addTearDown(dap.tearDown);
|
||||
|
||||
testDap((dap) async {
|
||||
group('debug mode', () {
|
||||
test('can run with ipv6', () async {
|
||||
final testFile = dap.createTestFile(emptyProgram);
|
||||
final testFile = dap.createTestFile(emptyProgram);
|
||||
final outputEvents = await dap.client.collectOutput(file: testFile);
|
||||
final vmServiceUri = _extractVmServiceUri(outputEvents.first);
|
||||
expect(vmServiceUri.path, isNot(matches(vmServiceAuthCodePathPattern)));
|
||||
});
|
||||
|
||||
final outputEvents = await dap.client.collectOutput(file: testFile);
|
||||
final vmServiceUri = _extractVmServiceUri(outputEvents.first);
|
||||
test('can run with ipv6', () async {
|
||||
final dap = await DapTestSession.setUp(additionalArgs: ['--ipv6']);
|
||||
addTearDown(dap.tearDown);
|
||||
|
||||
// Check DAP server host.
|
||||
expect(dap.server.host, equals('::1'));
|
||||
// Check VM Service/DDS host.
|
||||
expect(vmServiceUri.host, equals('::1'));
|
||||
});
|
||||
// These tests can be slow due to starting up the external server process.
|
||||
}, timeout: Timeout.none);
|
||||
}, additionalArgs: ['--ipv6']);
|
||||
final testFile = dap.createTestFile(emptyProgram);
|
||||
final outputEvents = await dap.client.collectOutput(file: testFile);
|
||||
final vmServiceUri = _extractVmServiceUri(outputEvents.first);
|
||||
|
||||
expect(vmServiceUri.host, equals('::1'));
|
||||
});
|
||||
// These tests can be slow due to starting up the external server process.
|
||||
}, timeout: Timeout.none);
|
||||
}
|
||||
|
||||
/// Extracts the VM Service URI from the "Connecting to ..." banner output by
|
||||
|
@ -122,15 +126,6 @@ void main(List<String> args) async {
|
|||
Uri _extractVmServiceUri(OutputEventBody vmConnectionBanner) {
|
||||
// TODO(dantup): Change this to use the dart.debuggerUris custom event
|
||||
// if implemented (whch VS Code also needs).
|
||||
final vmServiceUriPattern = RegExp(r'Connecting to VM Service at ([^\s]+)\s');
|
||||
final match = vmServiceUriPattern.firstMatch(vmConnectionBanner.output);
|
||||
return Uri.parse(match!.group(1)!);
|
||||
}
|
||||
|
||||
/// Checks for the presence of an auth token in a VM Service URI in the
|
||||
/// "Connecting to VM Service" [OutputEvent].
|
||||
bool _hasAuthCode(OutputEventBody vmConnectionBanner) {
|
||||
final vmServiceUri = _extractVmServiceUri(vmConnectionBanner);
|
||||
final authCodePattern = RegExp(r'^/[\w=]{5,15}/ws');
|
||||
return authCodePattern.hasMatch(vmServiceUri.path);
|
||||
}
|
||||
|
|
|
@ -8,11 +8,16 @@ import 'test_client.dart';
|
|||
import 'test_support.dart';
|
||||
|
||||
main() {
|
||||
testDap((dap) async {
|
||||
group('debug mode variables', () {
|
||||
test('provides variable list for frames', () async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(r'''
|
||||
late DapTestSession dap;
|
||||
setUp(() async {
|
||||
dap = await DapTestSession.setUp();
|
||||
});
|
||||
tearDown(() => dap.tearDown());
|
||||
|
||||
group('debug mode variables', () {
|
||||
test('provides variable list for frames', () async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(r'''
|
||||
void main(List<String> args) {
|
||||
final myVariable = 1;
|
||||
foo();
|
||||
|
@ -23,135 +28,135 @@ void foo() {
|
|||
print('Hello!'); // BREAKPOINT
|
||||
}
|
||||
''');
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
|
||||
final stop = await client.hitBreakpoint(testFile, breakpointLine);
|
||||
final stack = await client.getValidStack(
|
||||
stop.threadId!,
|
||||
startFrame: 0,
|
||||
numFrames: 2,
|
||||
);
|
||||
final stop = await client.hitBreakpoint(testFile, breakpointLine);
|
||||
final stack = await client.getValidStack(
|
||||
stop.threadId!,
|
||||
startFrame: 0,
|
||||
numFrames: 2,
|
||||
);
|
||||
|
||||
// Check top two frames (in `foo` and in `main`).
|
||||
await client.expectScopeVariables(
|
||||
stack.stackFrames[0].id, // Top frame: foo
|
||||
'Variables',
|
||||
'''
|
||||
// Check top two frames (in `foo` and in `main`).
|
||||
await client.expectScopeVariables(
|
||||
stack.stackFrames[0].id, // Top frame: foo
|
||||
'Variables',
|
||||
'''
|
||||
b: 2
|
||||
''',
|
||||
);
|
||||
await client.expectScopeVariables(
|
||||
stack.stackFrames[1].id, // Second frame: main
|
||||
'Variables',
|
||||
'''
|
||||
);
|
||||
await client.expectScopeVariables(
|
||||
stack.stackFrames[1].id, // Second frame: main
|
||||
'Variables',
|
||||
'''
|
||||
args: List (0 items)
|
||||
myVariable: 1
|
||||
''',
|
||||
);
|
||||
});
|
||||
);
|
||||
});
|
||||
|
||||
test('provides simple exception types for frames', () async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(r'''
|
||||
test('provides simple exception types for frames', () async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(r'''
|
||||
void main(List<String> args) {
|
||||
throw 'my error';
|
||||
}
|
||||
''');
|
||||
|
||||
final stop = await client.hitException(testFile);
|
||||
final stack = await client.getValidStack(
|
||||
stop.threadId!,
|
||||
startFrame: 0,
|
||||
numFrames: 1,
|
||||
);
|
||||
final topFrameId = stack.stackFrames.first.id;
|
||||
final stop = await client.hitException(testFile);
|
||||
final stack = await client.getValidStack(
|
||||
stop.threadId!,
|
||||
startFrame: 0,
|
||||
numFrames: 1,
|
||||
);
|
||||
final topFrameId = stack.stackFrames.first.id;
|
||||
|
||||
// Check for an additional Scope named "Exceptions" that includes the
|
||||
// exception.
|
||||
await client.expectScopeVariables(
|
||||
topFrameId,
|
||||
'Exceptions',
|
||||
'''
|
||||
// Check for an additional Scope named "Exceptions" that includes the
|
||||
// exception.
|
||||
await client.expectScopeVariables(
|
||||
topFrameId,
|
||||
'Exceptions',
|
||||
'''
|
||||
String: "my error"
|
||||
''',
|
||||
);
|
||||
});
|
||||
);
|
||||
});
|
||||
|
||||
test('provides complex exception types frames', () async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(r'''
|
||||
test('provides complex exception types frames', () async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(r'''
|
||||
void main(List<String> args) {
|
||||
throw ArgumentError.notNull('args');
|
||||
}
|
||||
''');
|
||||
|
||||
final stop = await client.hitException(testFile);
|
||||
final stack = await client.getValidStack(
|
||||
stop.threadId!,
|
||||
startFrame: 0,
|
||||
numFrames: 1,
|
||||
);
|
||||
final topFrameId = stack.stackFrames.first.id;
|
||||
final stop = await client.hitException(testFile);
|
||||
final stack = await client.getValidStack(
|
||||
stop.threadId!,
|
||||
startFrame: 0,
|
||||
numFrames: 1,
|
||||
);
|
||||
final topFrameId = stack.stackFrames.first.id;
|
||||
|
||||
// Check for an additional Scope named "Exceptions" that includes the
|
||||
// exception.
|
||||
await client.expectScopeVariables(
|
||||
topFrameId,
|
||||
'Exceptions',
|
||||
// TODO(dantup): evaluateNames
|
||||
'''
|
||||
// Check for an additional Scope named "Exceptions" that includes the
|
||||
// exception.
|
||||
await client.expectScopeVariables(
|
||||
topFrameId,
|
||||
'Exceptions',
|
||||
// TODO(dantup): evaluateNames
|
||||
'''
|
||||
invalidValue: null
|
||||
message: "Must not be null"
|
||||
name: "args"
|
||||
''',
|
||||
);
|
||||
});
|
||||
);
|
||||
});
|
||||
|
||||
test('includes simple variable fields', () async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(r'''
|
||||
test('includes simple variable fields', () async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(r'''
|
||||
void main(List<String> args) {
|
||||
final myVariable = DateTime(2000, 1, 1);
|
||||
print('Hello!'); // BREAKPOINT
|
||||
}
|
||||
''');
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
|
||||
final stop = await client.hitBreakpoint(testFile, breakpointLine);
|
||||
await client.expectLocalVariable(
|
||||
stop.threadId!,
|
||||
expectedName: 'myVariable',
|
||||
expectedDisplayString: 'DateTime',
|
||||
expectedVariables: '''
|
||||
final stop = await client.hitBreakpoint(testFile, breakpointLine);
|
||||
await client.expectLocalVariable(
|
||||
stop.threadId!,
|
||||
expectedName: 'myVariable',
|
||||
expectedDisplayString: 'DateTime',
|
||||
expectedVariables: '''
|
||||
isUtc: false
|
||||
''',
|
||||
);
|
||||
});
|
||||
);
|
||||
});
|
||||
|
||||
test('includes variable getters when evaluateGettersInDebugViews=true',
|
||||
() async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(r'''
|
||||
test('includes variable getters when evaluateGettersInDebugViews=true',
|
||||
() async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(r'''
|
||||
void main(List<String> args) {
|
||||
final myVariable = DateTime(2000, 1, 1);
|
||||
print('Hello!'); // BREAKPOINT
|
||||
}
|
||||
''');
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
|
||||
final stop = await client.hitBreakpoint(
|
||||
testFile,
|
||||
breakpointLine,
|
||||
launch: () => client.launch(
|
||||
testFile.path,
|
||||
evaluateGettersInDebugViews: true,
|
||||
),
|
||||
);
|
||||
await client.expectLocalVariable(
|
||||
stop.threadId!,
|
||||
expectedName: 'myVariable',
|
||||
expectedDisplayString: 'DateTime',
|
||||
expectedVariables: '''
|
||||
final stop = await client.hitBreakpoint(
|
||||
testFile,
|
||||
breakpointLine,
|
||||
launch: () => client.launch(
|
||||
testFile.path,
|
||||
evaluateGettersInDebugViews: true,
|
||||
),
|
||||
);
|
||||
await client.expectLocalVariable(
|
||||
stop.threadId!,
|
||||
expectedName: 'myVariable',
|
||||
expectedDisplayString: 'DateTime',
|
||||
expectedVariables: '''
|
||||
day: 1
|
||||
hour: 0
|
||||
isUtc: false
|
||||
|
@ -165,72 +170,71 @@ void main(List<String> args) {
|
|||
weekday: 6
|
||||
year: 2000
|
||||
''',
|
||||
ignore: {
|
||||
// Don't check fields that may very based on timezone as it'll make
|
||||
// these tests fragile, and this isn't really what's being tested.
|
||||
'timeZoneName',
|
||||
'microsecondsSinceEpoch',
|
||||
'millisecondsSinceEpoch',
|
||||
},
|
||||
);
|
||||
});
|
||||
ignore: {
|
||||
// Don't check fields that may very based on timezone as it'll make
|
||||
// these tests fragile, and this isn't really what's being tested.
|
||||
'timeZoneName',
|
||||
'microsecondsSinceEpoch',
|
||||
'millisecondsSinceEpoch',
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test('renders a simple list', () async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(r'''
|
||||
test('renders a simple list', () async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(r'''
|
||||
void main(List<String> args) {
|
||||
final myVariable = ["first", "second", "third"];
|
||||
print('Hello!'); // BREAKPOINT
|
||||
}
|
||||
''');
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
|
||||
final stop = await client.hitBreakpoint(testFile, breakpointLine);
|
||||
await client.expectLocalVariable(
|
||||
stop.threadId!,
|
||||
expectedName: 'myVariable',
|
||||
expectedDisplayString: 'List (3 items)',
|
||||
// TODO(dantup): evaluateNames
|
||||
expectedVariables: '''
|
||||
final stop = await client.hitBreakpoint(testFile, breakpointLine);
|
||||
await client.expectLocalVariable(
|
||||
stop.threadId!,
|
||||
expectedName: 'myVariable',
|
||||
expectedDisplayString: 'List (3 items)',
|
||||
// TODO(dantup): evaluateNames
|
||||
expectedVariables: '''
|
||||
0: "first"
|
||||
1: "second"
|
||||
2: "third"
|
||||
''',
|
||||
);
|
||||
});
|
||||
);
|
||||
});
|
||||
|
||||
test('renders a simple list subset', () async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(r'''
|
||||
test('renders a simple list subset', () async {
|
||||
final client = dap.client;
|
||||
final testFile = await dap.createTestFile(r'''
|
||||
void main(List<String> args) {
|
||||
final myVariable = ["first", "second", "third"];
|
||||
print('Hello!'); // BREAKPOINT
|
||||
}
|
||||
''');
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
final breakpointLine = lineWith(testFile, '// BREAKPOINT');
|
||||
|
||||
final stop = await client.hitBreakpoint(testFile, breakpointLine);
|
||||
await client.expectLocalVariable(
|
||||
stop.threadId!,
|
||||
expectedName: 'myVariable',
|
||||
expectedDisplayString: 'List (3 items)',
|
||||
// TODO(dantup): evaluateNames
|
||||
expectedVariables: '''
|
||||
final stop = await client.hitBreakpoint(testFile, breakpointLine);
|
||||
await client.expectLocalVariable(
|
||||
stop.threadId!,
|
||||
expectedName: 'myVariable',
|
||||
expectedDisplayString: 'List (3 items)',
|
||||
// TODO(dantup): evaluateNames
|
||||
expectedVariables: '''
|
||||
1: "second"
|
||||
''',
|
||||
start: 1,
|
||||
count: 1,
|
||||
);
|
||||
});
|
||||
start: 1,
|
||||
count: 1,
|
||||
);
|
||||
});
|
||||
|
||||
test('renders a simple map', () {
|
||||
// TODO(dantup): Implement this (inc evaluateNames)
|
||||
}, skip: true);
|
||||
test('renders a simple map', () {
|
||||
// TODO(dantup): Implement this (inc evaluateNames)
|
||||
}, skip: true);
|
||||
|
||||
test('renders a simple map subset', () {
|
||||
// TODO(dantup): Implement this (inc evaluateNames)
|
||||
}, skip: true);
|
||||
// These tests can be slow due to starting up the external server process.
|
||||
}, timeout: Timeout.none);
|
||||
});
|
||||
test('renders a simple map subset', () {
|
||||
// TODO(dantup): Implement this (inc evaluateNames)
|
||||
}, skip: true);
|
||||
// These tests can be slow due to starting up the external server process.
|
||||
}, timeout: Timeout.none);
|
||||
}
|
||||
|
|
|
@ -7,10 +7,15 @@ import 'package:test/test.dart';
|
|||
import 'test_support.dart';
|
||||
|
||||
main() {
|
||||
testDap((dap) async {
|
||||
group('noDebug mode', () {
|
||||
test('runs a simple script', () async {
|
||||
final testFile = dap.createTestFile(r'''
|
||||
late DapTestSession dap;
|
||||
setUp(() async {
|
||||
dap = await DapTestSession.setUp();
|
||||
});
|
||||
tearDown(() => dap.tearDown());
|
||||
|
||||
group('noDebug mode', () {
|
||||
test('runs a simple script', () async {
|
||||
final testFile = dap.createTestFile(r'''
|
||||
void main(List<String> args) async {
|
||||
print('Hello!');
|
||||
print('World!');
|
||||
|
@ -18,24 +23,23 @@ void main(List<String> args) async {
|
|||
}
|
||||
''');
|
||||
|
||||
final outputEvents = await dap.client.collectOutput(
|
||||
launch: () => dap.client.launch(
|
||||
testFile.path,
|
||||
noDebug: true,
|
||||
args: ['one', 'two'],
|
||||
),
|
||||
);
|
||||
final outputEvents = await dap.client.collectOutput(
|
||||
launch: () => dap.client.launch(
|
||||
testFile.path,
|
||||
noDebug: true,
|
||||
args: ['one', 'two'],
|
||||
),
|
||||
);
|
||||
|
||||
final output = outputEvents.map((e) => e.output).join();
|
||||
expectLines(output, [
|
||||
'Hello!',
|
||||
'World!',
|
||||
'args: [one, two]',
|
||||
'',
|
||||
'Exited.',
|
||||
]);
|
||||
});
|
||||
// These tests can be slow due to starting up the external server process.
|
||||
}, timeout: Timeout.none);
|
||||
});
|
||||
final output = outputEvents.map((e) => e.output).join();
|
||||
expectLines(output, [
|
||||
'Hello!',
|
||||
'World!',
|
||||
'args: [one, two]',
|
||||
'',
|
||||
'Exited.',
|
||||
]);
|
||||
});
|
||||
// These tests can be slow due to starting up the external server process.
|
||||
}, timeout: Timeout.none);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import 'package:dds/src/dap/adapters/dart.dart';
|
|||
import 'package:dds/src/dap/logging.dart';
|
||||
import 'package:dds/src/dap/protocol_generated.dart';
|
||||
import 'package:dds/src/dap/protocol_stream.dart';
|
||||
import 'package:dds/src/dap/protocol_stream_transformers.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:vm_service/vm_service.dart' as vm;
|
||||
|
||||
|
@ -22,19 +21,17 @@ import 'test_server.dart';
|
|||
/// Methods on this class should map directly to protocol methods. Additional
|
||||
/// helpers are available in [DapTestClientExtension].
|
||||
class DapTestClient {
|
||||
final Socket _socket;
|
||||
final ByteStreamServerChannel _channel;
|
||||
late final StreamSubscription<String> _subscription;
|
||||
|
||||
final Logger? _logger;
|
||||
final bool captureVmServiceTraffic;
|
||||
final _requestWarningDuration = const Duration(seconds: 2);
|
||||
final _requestWarningDuration = const Duration(seconds: 5);
|
||||
final Map<int, _OutgoingRequest> _pendingRequests = {};
|
||||
final _eventController = StreamController<Event>.broadcast();
|
||||
int _seq = 1;
|
||||
|
||||
DapTestClient._(
|
||||
this._socket,
|
||||
this._channel,
|
||||
this._logger, {
|
||||
this.captureVmServiceTraffic = false,
|
||||
|
@ -226,7 +223,6 @@ class DapTestClient {
|
|||
|
||||
Future<void> stop() async {
|
||||
_channel.close();
|
||||
await _socket.close();
|
||||
await _subscription.cancel();
|
||||
}
|
||||
|
||||
|
@ -305,16 +301,12 @@ class DapTestClient {
|
|||
/// Creates a [DapTestClient] that connects the server listening on
|
||||
/// [host]:[port].
|
||||
static Future<DapTestClient> connect(
|
||||
String host,
|
||||
int port, {
|
||||
DapTestServer server, {
|
||||
bool captureVmServiceTraffic = false,
|
||||
Logger? logger,
|
||||
}) async {
|
||||
final socket = await Socket.connect(host, port);
|
||||
final channel = ByteStreamServerChannel(
|
||||
socket.transform(Uint8ListTransformer()), socket, logger);
|
||||
|
||||
return DapTestClient._(socket, channel, logger,
|
||||
final channel = ByteStreamServerChannel(server.stream, server.sink, logger);
|
||||
return DapTestClient._(channel, logger,
|
||||
captureVmServiceTraffic: captureVmServiceTraffic);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,10 +13,9 @@ import 'package:path/path.dart' as path;
|
|||
import 'package:pedantic/pedantic.dart';
|
||||
|
||||
abstract class DapTestServer {
|
||||
List<String> get errorLogs;
|
||||
String get host;
|
||||
int get port;
|
||||
Future<void> stop();
|
||||
StreamSink<List<int>> get sink;
|
||||
Stream<List<int>> get stream;
|
||||
}
|
||||
|
||||
/// An instance of a DAP server running in-process (to aid debugging).
|
||||
|
@ -25,22 +24,27 @@ abstract class DapTestServer {
|
|||
/// serialized and deserialized but it's not quite the same running out of
|
||||
/// process.
|
||||
class InProcessDapTestServer extends DapTestServer {
|
||||
final DapServer _server;
|
||||
late final DapServer _server;
|
||||
final stdinController = StreamController<List<int>>();
|
||||
final stdoutController = StreamController<List<int>>();
|
||||
|
||||
InProcessDapTestServer._(this._server);
|
||||
StreamSink<List<int>> get sink => stdinController.sink;
|
||||
Stream<List<int>> get stream => stdoutController.stream;
|
||||
|
||||
String get host => _server.host;
|
||||
int get port => _server.port;
|
||||
List<String> get errorLogs => const []; // In-proc errors just throw in-line.
|
||||
InProcessDapTestServer._() {
|
||||
_server = DapServer(stdinController.stream, stdoutController.sink);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> stop() async {
|
||||
await _server.stop();
|
||||
_server.stop();
|
||||
}
|
||||
|
||||
static Future<InProcessDapTestServer> create({Logger? logger}) async {
|
||||
final DapServer server = await DapServer.create(logger: logger);
|
||||
return InProcessDapTestServer._(server);
|
||||
static Future<InProcessDapTestServer> create({
|
||||
Logger? logger,
|
||||
List<String>? additionalArgs,
|
||||
}) async {
|
||||
return InProcessDapTestServer._();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,28 +56,22 @@ class InProcessDapTestServer extends DapTestServer {
|
|||
class OutOfProcessDapTestServer extends DapTestServer {
|
||||
var _isShuttingDown = false;
|
||||
final Process _process;
|
||||
final int port;
|
||||
final String host;
|
||||
final List<String> _errors = [];
|
||||
|
||||
List<String> get errorLogs => _errors;
|
||||
StreamSink<List<int>> get sink => _process.stdin;
|
||||
Stream<List<int>> get stream => _process.stdout;
|
||||
|
||||
OutOfProcessDapTestServer._(
|
||||
this._process,
|
||||
this.host,
|
||||
this.port,
|
||||
Logger? logger,
|
||||
) {
|
||||
// Treat anything written to stderr as the DAP crashing and fail the test.
|
||||
_process.stderr.transform(utf8.decoder).listen((error) {
|
||||
logger?.call(error);
|
||||
_errors.add(error);
|
||||
throw error;
|
||||
});
|
||||
unawaited(_process.exitCode.then((code) {
|
||||
final message = 'Out-of-process DAP server terminated with code $code';
|
||||
logger?.call(message);
|
||||
_errors.add(message);
|
||||
if (!_isShuttingDown && code != 0) {
|
||||
throw message;
|
||||
}
|
||||
|
@ -107,27 +105,6 @@ class OutOfProcessDapTestServer extends DapTestServer {
|
|||
],
|
||||
);
|
||||
|
||||
final startedCompleter = Completer<void>();
|
||||
late String host;
|
||||
late int port;
|
||||
|
||||
// Scrape the `started` event to get the host/port. Any other output
|
||||
// should be sent to the logger (as it may be verbose output for diagnostic
|
||||
// purposes).
|
||||
_process.stdout.transform(utf8.decoder).listen((text) {
|
||||
if (!startedCompleter.isCompleted) {
|
||||
final event = jsonDecode(text);
|
||||
if (event['state'] == 'started') {
|
||||
host = event['dapHost'];
|
||||
port = event['dapPort'];
|
||||
startedCompleter.complete();
|
||||
return;
|
||||
}
|
||||
}
|
||||
logger?.call(text);
|
||||
});
|
||||
await startedCompleter.future;
|
||||
|
||||
return OutOfProcessDapTestServer._(_process, host, port, logger);
|
||||
return OutOfProcessDapTestServer._(_process, logger);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,20 +12,6 @@ import 'package:test/test.dart';
|
|||
import 'test_client.dart';
|
||||
import 'test_server.dart';
|
||||
|
||||
/// A logger to use to log all traffic (both DAP and VM) to stdout.
|
||||
///
|
||||
/// If the enviroment variable is `DAP_TEST_VERBOSE` then `print` will be used,
|
||||
/// otherwise there will be no verbose logging.
|
||||
///
|
||||
/// DAP_TEST_VERBOSE=true pub run test --chain-stack-traces test/dap/integration
|
||||
///
|
||||
///
|
||||
/// When using the out-of-process DAP, this causes `--verbose` to be passed to
|
||||
/// the server which causes it to write all traffic to `stdout` which is then
|
||||
/// picked up by [OutOfProcessDapTestServer] and passed to this logger.
|
||||
final logger =
|
||||
Platform.environment['DAP_TEST_VERBOSE'] == 'true' ? print : null;
|
||||
|
||||
/// Whether to run the DAP server in-process with the tests, or externally in
|
||||
/// another process.
|
||||
///
|
||||
|
@ -35,6 +21,14 @@ final logger =
|
|||
/// simplified in VS Code by using a launch config with custom CodeLens links).
|
||||
final useInProcessDap = Platform.environment['DAP_TEST_INTERNAL'] == 'true';
|
||||
|
||||
/// A [RegExp] that matches the `path` part of a VM Service URI that contains
|
||||
/// an authentication token.
|
||||
final vmServiceAuthCodePathPattern = RegExp(r'^/[\w_\-=]{5,15}/ws$');
|
||||
|
||||
/// A [RegExp] that matches the "Connecting to VM Service" banner that is sent
|
||||
/// as the first output event for a debug session.
|
||||
final vmServiceUriPattern = RegExp(r'Connecting to VM Service at ([^\s]+)\s');
|
||||
|
||||
/// Expects [actual] to equal the lines [expected], ignoring differences in line
|
||||
/// endings.
|
||||
void expectLines(String actual, List<String> expected) {
|
||||
|
@ -45,33 +39,13 @@ void expectLines(String actual, List<String> expected) {
|
|||
int lineWith(File file, String searchText) =>
|
||||
file.readAsLinesSync().indexWhere((line) => line.contains(searchText)) + 1;
|
||||
|
||||
/// A helper function to wrap all tests in a library with setup/teardown functions
|
||||
/// to start a shared server for all tests in the library and an individual
|
||||
/// client for each test.
|
||||
testDap(
|
||||
Future<void> Function(DapTestSession session) tests, {
|
||||
List<String>? additionalArgs,
|
||||
}) {
|
||||
final session = DapTestSession(additionalArgs: additionalArgs);
|
||||
|
||||
setUpAll(session.setUpAll);
|
||||
tearDownAll(session.tearDownAll);
|
||||
setUp(session.setUp);
|
||||
tearDown(session.tearDown);
|
||||
|
||||
return tests(session);
|
||||
}
|
||||
|
||||
/// A helper class provided to DAP integration tests run with [testDap] to
|
||||
/// easily share setup/teardown without sharing state across tests from different
|
||||
/// files.
|
||||
/// A helper class containing the DAP server/client for DAP integration tests.
|
||||
class DapTestSession {
|
||||
late DapTestServer server;
|
||||
late DapTestClient client;
|
||||
DapTestServer server;
|
||||
DapTestClient client;
|
||||
final _testFolders = <Directory>[];
|
||||
final List<String>? additionalArgs;
|
||||
|
||||
DapTestSession({this.additionalArgs});
|
||||
DapTestSession._(this.server, this.client);
|
||||
|
||||
/// Creates a file in a temporary folder to be used as an application for testing.
|
||||
///
|
||||
|
@ -84,60 +58,32 @@ class DapTestSession {
|
|||
return testFile;
|
||||
}
|
||||
|
||||
Future<void> setUp() async {
|
||||
client = await _startClient(server);
|
||||
static Future<DapTestSession> setUp({List<String>? additionalArgs}) async {
|
||||
final server = await _startServer(additionalArgs: additionalArgs);
|
||||
final client = await DapTestClient.connect(server);
|
||||
return DapTestSession._(server, client);
|
||||
}
|
||||
|
||||
Future<void> setUpAll() async {
|
||||
server = await _startServer(logger: logger, additionalArgs: additionalArgs);
|
||||
}
|
||||
|
||||
Future<void> tearDown() => client.stop();
|
||||
|
||||
Future<void> tearDownAll() async {
|
||||
Future<void> tearDown() async {
|
||||
await client.stop();
|
||||
await server.stop();
|
||||
|
||||
// Clean up any temp folders created during the test runs.
|
||||
_testFolders.forEach((dir) => dir.deleteSync(recursive: true));
|
||||
}
|
||||
|
||||
/// Creates and connects a new [DapTestClient] to [server].
|
||||
Future<DapTestClient> _startClient(DapTestServer server) async {
|
||||
// Since we don't get a signal from the DAP server when it's ready and we
|
||||
// just started it, add a short retry to connections.
|
||||
// Since the bots can be quite slow, it may take 6-7 seconds for the server
|
||||
// to initially start up (including compilation).
|
||||
var attempt = 1;
|
||||
while (attempt++ <= 100) {
|
||||
try {
|
||||
return await DapTestClient.connect(server.host, server.port);
|
||||
} catch (e) {
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
}
|
||||
}
|
||||
|
||||
final errorMessage = StringBuffer();
|
||||
errorMessage.writeln(
|
||||
'Failed to connect to DAP server on port ${server.port}'
|
||||
' after $attempt attempts. Did the server start correctly?',
|
||||
);
|
||||
|
||||
final serverErrorLogs = server.errorLogs;
|
||||
if (serverErrorLogs.isNotEmpty) {
|
||||
errorMessage.writeln('Server errors:');
|
||||
errorMessage.writeAll(serverErrorLogs);
|
||||
}
|
||||
|
||||
throw Exception(errorMessage.toString());
|
||||
_testFolders
|
||||
..forEach((dir) => dir.deleteSync(recursive: true))
|
||||
..clear();
|
||||
}
|
||||
|
||||
/// Starts a DAP server that can be shared across tests.
|
||||
Future<DapTestServer> _startServer({
|
||||
static Future<DapTestServer> _startServer({
|
||||
Logger? logger,
|
||||
List<String>? additionalArgs,
|
||||
}) async {
|
||||
return useInProcessDap
|
||||
? await InProcessDapTestServer.create(logger: logger)
|
||||
? await InProcessDapTestServer.create(
|
||||
logger: logger,
|
||||
additionalArgs: additionalArgs,
|
||||
)
|
||||
: await OutOfProcessDapTestServer.create(
|
||||
logger: logger,
|
||||
additionalArgs: additionalArgs,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// 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:convert';
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:args/command_runner.dart';
|
||||
|
@ -12,7 +12,7 @@ Future<void> main(List<String> arguments) async {
|
|||
// TODO(dantup): "dap_tool" is a placeholder and will likely eventually be a
|
||||
// "dart" command.
|
||||
final runner = CommandRunner('dap_tool', 'Dart DAP Tool')
|
||||
..addCommand(DapCommand());
|
||||
..addCommand(DapCommand(stdin, stdout.nonBlocking));
|
||||
|
||||
try {
|
||||
await runner.run(arguments);
|
||||
|
@ -23,33 +23,22 @@ Future<void> main(List<String> arguments) async {
|
|||
}
|
||||
|
||||
class DapCommand extends Command {
|
||||
static const argHost = 'host';
|
||||
static const argPort = 'port';
|
||||
static const argIpv6 = 'ipv6';
|
||||
static const argDds = 'dds';
|
||||
static const argAuthCodes = 'auth-codes';
|
||||
static const argVerbose = 'verbose';
|
||||
|
||||
final Stream<List<int>> _inputStream;
|
||||
final StreamSink<List<int>> _outputSink;
|
||||
|
||||
@override
|
||||
final String description = 'Start a DAP debug server.';
|
||||
|
||||
@override
|
||||
final String name = 'dap';
|
||||
|
||||
DapCommand() {
|
||||
DapCommand(this._inputStream, this._outputSink) {
|
||||
argParser
|
||||
..addOption(
|
||||
argHost,
|
||||
help: 'The hostname/IP to bind the server to. If not supplied, will'
|
||||
' use the appropriate loopback address depending on whether'
|
||||
' --ipv6 is set',
|
||||
)
|
||||
..addOption(
|
||||
argPort,
|
||||
abbr: 'p',
|
||||
defaultsTo: '0',
|
||||
help: 'The port to bind the server to',
|
||||
)
|
||||
..addFlag(
|
||||
argIpv6,
|
||||
help: 'Whether to bind DAP/VM Service/DDS to IPv6 addresses',
|
||||
|
@ -63,33 +52,21 @@ class DapCommand extends Command {
|
|||
argAuthCodes,
|
||||
defaultsTo: true,
|
||||
help: 'Whether to enable authentication codes for VM Services',
|
||||
)
|
||||
..addFlag(
|
||||
argVerbose,
|
||||
abbr: 'v',
|
||||
help: 'Whether to print diagnostic output to stdout',
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> run() async {
|
||||
final args = argResults!;
|
||||
final port = int.parse(args[argPort]);
|
||||
final host = args[argHost];
|
||||
final ipv6 = args[argIpv6] as bool;
|
||||
|
||||
final server = await DapServer.create(
|
||||
host: host,
|
||||
port: port,
|
||||
final server = DapServer(
|
||||
_inputStream,
|
||||
_outputSink,
|
||||
ipv6: ipv6,
|
||||
enableDdds: args[argDds],
|
||||
enableDds: args[argDds],
|
||||
enableAuthCodes: args[argAuthCodes],
|
||||
logger: args[argVerbose] ? print : null,
|
||||
);
|
||||
|
||||
stdout.write(jsonEncode({
|
||||
'state': 'started',
|
||||
'dapHost': server.host,
|
||||
'dapPort': server.port,
|
||||
}));
|
||||
await server.channel.closed;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue