blob: 707214e29c91b132f50c2a4f63d783021ef1fce8 [file] [log] [blame]
// Copyright (c) 2021, 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:path/path.dart' as path;
import 'package:test/test.dart';
import 'test_client.dart';
import 'test_scripts.dart';
import 'test_support.dart';
main() {
group('debug mode', () {
late DapTestSession dap;
setUp(() async {
dap = await DapTestSession.setUp(
/// This boolean is temporarily set to `true` to aid debugging
/// https://github.com/dart-lang/sdk/issues/55313 and will be reverted
/// soon.
forceVerboseLogging: true,
);
});
tearDown(() => dap.tearDown());
test('can attach to a simple script using vmServiceUri', () async {
final testFile = dap.createTestFile(simpleArgPrintingProgram);
final args = ['one', 'two'];
final proc = await startDartProcessPaused(
testFile.path,
args,
cwd: dap.testAppDir.path,
pauseOnExit: true, // To ensure we capture all output
);
final vmServiceUri = await waitForStdoutVmServiceBanner(proc);
final outputEvents = await dap.client.collectOutput(
launch: () => dap.client.attach(
vmServiceUri: vmServiceUri.toString(),
autoResumeOnEntry: true,
autoResumeOnExit: true,
cwd: dap.testAppDir.path,
),
);
expectLines(outputEvents.map((output) => output.output).join(), [
startsWith('Connecting to VM Service at ws://127.0.0.1:'),
'Connected to the VM Service.',
startsWith('The Dart VM service is listening on'),
startsWith('The Dart DevTools debugger and profiler is available at'),
'Hello!',
'World!',
'args: [one, two]',
'',
'Exited.',
]);
// Ensure the categories were set correctly.
for (final output in outputEvents) {
if (output.output.contains('VM Service') ||
output.output.contains('Exited')) {
expect(output.category, anyOf('console', isNull));
} else {
// User output.
expect(output.category, 'stdout');
}
}
});
test('can attach to a simple script using vmServiceInfoFile', () async {
final testFile = dap.createTestFile(simpleArgPrintingProgram);
// Spawn the program using --write-service-info which we'll pass the path
// of directly to the DAP to read.
final vmServiceInfoFilePath = path.join(
dap.testAppDir.path,
'vmServiceInfo.json',
);
await startDartProcessPaused(
testFile.path,
['one', 'two'],
cwd: dap.testAppDir.path,
vmArgs: ['--write-service-info=${Uri.file(vmServiceInfoFilePath)}'],
pauseOnExit: true, // To ensure we capture all output
);
final outputEvents = await dap.client.collectOutput(
launch: () => dap.client.attach(
vmServiceInfoFile: vmServiceInfoFilePath,
autoResumeOnEntry: true,
autoResumeOnExit: true,
cwd: dap.testAppDir.path,
),
);
expectLines(outputEvents.map((output) => output.output).join(), [
startsWith('Connecting to VM Service at ws://127.0.0.1:'),
'Connected to the VM Service.',
startsWith('The Dart VM service is listening on'),
startsWith('The Dart DevTools debugger and profiler is available at'),
'Hello!',
'World!',
'args: [one, two]',
'',
'Exited.',
]);
// Ensure the categories were set correctly.
for (final output in outputEvents) {
if (output.output.contains('VM Service') ||
output.output.contains('Exited')) {
expect(output.category, anyOf('console', isNull));
} else {
// User output.
expect(output.category, 'stdout');
}
}
});
test('reports initialization failures if can\'t connect to the VM Service',
() async {
final outputEvents = await dap.client.collectOutput(
launch: () => dap.client.attach(
vmServiceUri: 'ws://bogus.local/',
autoResumeOnEntry: false,
autoResumeOnExit: false,
),
);
expect(
outputEvents.map((e) => e.output).join(),
contains('Invalid --vm-service-uri argument: http://bogus.local'),
);
});
test('removes breakpoints/pause and resumes on detach', () async {
final client = dap.client;
final testFile = dap.createTestFile(simpleBreakpointAndThrowProgram);
final proc = await startDartProcessPaused(
testFile.path,
[],
cwd: dap.testAppDir.path,
// Disable user-pause-on-exit because we're checking DAP resumes and
// if the VM waits for user-resume, we won't complete. We don't want to
// send an explicit user-resume because that would force resume,
// invalidating this test that we did a DAP resume.
pauseOnExit: false,
);
final vmServiceUri = await waitForStdoutVmServiceBanner(proc);
// Attach to the paused script without resuming and wait for the startup
// pause event.
await Future.wait([
client.expectStop('entry'),
client.start(
launch: () => client.attach(
vmServiceUri: vmServiceUri.toString(),
autoResumeOnEntry: false,
autoResumeOnExit: false,
cwd: dap.testAppDir.path,
),
),
]);
// Set a breakpoint that we expect not to be hit, as detach should disable
// it and resume.
final breakpointLine = lineWith(testFile, breakpointMarker);
await client.setBreakpoint(testFile, breakpointLine);
// Expect that termination is reported as 'Detached' when we explicitly
// requested a detach.
expect(
client.outputEvents.map((output) => output.output.trim()),
// emitsThrough because we might still get "Hello" in the output
// because we resume as part of detach.
emitsThrough('Detached.'),
);
// Detach using terminateRequest. Despite the name, terminateRequest is
// the request for a graceful detach (and disconnectRequest is the
// forceful shutdown).
await client.terminate();
// Expect the process terminates (and hasn't got stuck on the breakpoint
// or exception).
await proc.exitCode;
});
// These tests can be slow due to starting up the external server process.
}, timeout: Timeout.none);
}