// 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:test/test.dart'; import 'test_client.dart'; import 'test_scripts.dart'; import 'test_support.dart'; main() { late DapTestSession dap; setUp(() async { dap = await DapTestSession.setUp(); }); tearDown(() => dap.tearDown()); group('debug mode stack trace', () { test('includes expected names and async boundaries', () async { final client = dap.client; final testFile = dap.createTestFile(simpleAsyncProgram); final breakpointLine = lineWith(testFile, breakpointMarker); final stop = await client.hitBreakpoint(testFile, breakpointLine); final stack = await client.getValidStack( stop.threadId!, startFrame: 0, numFrames: 8, ); expect( stack.stackFrames.map((f) => f.name), equals([ 'four', 'three', '', 'two', '', 'one', '', 'main', ]), ); // Ensure async gaps have their presentationHint set to 'label'. expect( stack.stackFrames.map((f) => f.presentationHint), equals([ null, null, 'label', null, 'label', null, 'label', null, ]), ); }); test('only sets canRestart where VM can rewind', () async { final client = dap.client; final testFile = dap.createTestFile(simpleAsyncProgram); final breakpointLine = lineWith(testFile, breakpointMarker); final stop = await client.hitBreakpoint(testFile, breakpointLine); final stack = await client.getValidStack( stop.threadId!, startFrame: 0, numFrames: 8, ); expect( stack.stackFrames.map((f) => f.canRestart ?? false), equals([ // Top frame cannot be rewound to: // https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md#resume isFalse, // Other frames can isTrue, // Until after an async boundary isFalse, isFalse, isFalse, isFalse, isFalse, isFalse, ]), ); }); test('deemphasizes SDK frames when debugSdk=false', () async { final client = dap.client; final testFile = dap.createTestFile(sdkStackFrameProgram); final breakpointLine = lineWith(testFile, breakpointMarker); final stop = await client.hitBreakpoint( testFile, breakpointLine, launch: () => client.launch(testFile.path, debugSdkLibraries: false), ); final stack = await client.getValidStack( stop.threadId!, startFrame: 0, numFrames: 100, ); // Get all frames that are SDK Frames final sdkFrames = stack.stackFrames .where((frame) => frame.source?.name?.startsWith('dart:') ?? false) .toList(); // Ensure we got some frames for the test to be valid. expect(sdkFrames, isNotEmpty); for (final sdkFrame in sdkFrames) { expect( sdkFrame.source?.presentationHint, equals('deemphasize'), reason: '${sdkFrame.source!.name} should be deemphasized', ); } }); test('does not deemphasize SDK frames when debugSdk=true', () async { final client = dap.client; final testFile = dap.createTestFile(sdkStackFrameProgram); final breakpointLine = lineWith(testFile, breakpointMarker); final stop = await client.hitBreakpoint( testFile, breakpointLine, launch: () => client.launch(testFile.path, debugSdkLibraries: true), ); final stack = await client.getValidStack( stop.threadId!, startFrame: 0, numFrames: 100, ); // Get all frames that are SDK Frames final sdkFrames = stack.stackFrames .where((frame) => frame.source?.name?.startsWith('dart:') ?? false) .toList(); // Ensure we got some frames for the test to be valid. expect(sdkFrames, isNotEmpty); for (final sdkFrame in sdkFrames) { expect( sdkFrame.source?.presentationHint, isNot(equals('deemphasize')), reason: '${sdkFrame.source!.name} should not be deemphasized', ); expect( sdkFrame.source?.origin, equals('from the SDK'), reason: '${sdkFrame.source!.name} should be labelled as SDK code', ); } }); test('returns empty stack for exited threads', () async { final client = dap.client; final testFile = dap.createTestFile(isolateSpawningProgram); // Run the script and wait for the additional isolate to exit. final threadExitFuture = client.threadEvents.where((event) => event.reason == 'exited').first; await Future.wait([ threadExitFuture, client.initialize(), client.launch(testFile.path), ], eagerError: true); final exitedThreadId = (await threadExitFuture).threadId; // Verify empty stack and no error. final stack = await client.getValidStack( exitedThreadId, startFrame: 0, numFrames: 8, ); expect(stack.stackFrames, isEmpty); }); // These tests can be slow due to starting up the external server process. }, timeout: Timeout.none); }