From 44f230147cfd9e3dd48a781524372eb06b713044 Mon Sep 17 00:00:00 2001 From: JF Date: Sat, 22 Aug 2026 12:59:03 -0400 Subject: [PATCH] test(shared): cover the LLDB policy wrappers' own members - cpp 36%->100%, rust 71%->100% adapter-policy-cpp.ts read 35.7% lines / 0% branches not because its behavior was untested but because the file is a thin composition wrapper: 15 members are bare references into lldb-policy-shared.ts (94% lines, 100% functions), so the 234-line cpp test's assertions were credited to the shared file. Only 14 statements belong to the wrapper itself, so the 7 one-line members no test called dragged the percentage down; rust, structurally identical, sat at 70.6% for the same reason. - adapter-policy-cpp.test.ts: call the 7 uncalled members (child-session throw, isChildReadyEvent, getLocalScopeName, resolveExecutablePath both arms, getDebuggerConfiguration, requiresCommandQueueing, shouldQueueCommand) plus one getAdapterSpawnConfig call omitting platform/arch so the default-arg branches fire -> 100% on all metrics - adapter-policy-rust.test.ts: same treatment for rust's 5 missed members -> 100% on all metrics - resolve-exception-filters.test.ts: add cpp to the guardrail matrix (uncaught: [], all: ['cpp_throw'], default 'uncaught') - it was the only policy with an exception-filter table missing from the deliberate-change guardrail Test-only; no source changes. Co-Authored-By: Claude Fable 5 --- .../tests/unit/adapter-policy-cpp.test.ts | 48 +++++++++++++++++++ .../tests/unit/adapter-policy-rust.test.ts | 25 ++++++++++ .../unit/resolve-exception-filters.test.ts | 5 ++ 3 files changed, 78 insertions(+) diff --git a/packages/shared/tests/unit/adapter-policy-cpp.test.ts b/packages/shared/tests/unit/adapter-policy-cpp.test.ts index c18ae8b8..79c2c168 100644 --- a/packages/shared/tests/unit/adapter-policy-cpp.test.ts +++ b/packages/shared/tests/unit/adapter-policy-cpp.test.ts @@ -156,6 +156,12 @@ describe('CppAdapterPolicy', () => { const env = (config as { env?: NodeJS.ProcessEnv }).env; expect(env?.LLDB_USE_NATIVE_PDB_READER).toBe('1'); }); + + it('defaults platform/arch to the host when omitted', () => { + const config = CppAdapterPolicy.getAdapterSpawnConfig!(payload); + expect((config as { command: string }).command).toBeTruthy(); + expect((config as { args: string[] }).args).toEqual(['--port', '4711']); + }); }); describe('matchesAdapter', () => { @@ -170,6 +176,48 @@ describe('CppAdapterPolicy', () => { expect(CppAdapterPolicy.isSessionReady!(SessionState.RUNNING)).toBe(false); }); + it('throws when building child session args', () => { + expect(() => CppAdapterPolicy.buildChildStartArgs!('pending-1', {})).toThrow( + 'CppAdapterPolicy does not support child sessions' + ); + }); + + it("treats only 'initialized' as the child-ready event", () => { + const event = (name: string): DebugProtocol.Event => ({ seq: 1, type: 'event', event: name }); + expect(CppAdapterPolicy.isChildReadyEvent!(event('initialized'))).toBe(true); + expect(CppAdapterPolicy.isChildReadyEvent!(event('stopped'))).toBe(false); + }); + + it('reads locals from the shared LLDB scope names', () => { + expect(CppAdapterPolicy.getLocalScopeName!()).toEqual(['Local', 'Locals']); + }); + + it('resolves executable path from inputs only, deferring to the adapter otherwise', () => { + expect(CppAdapterPolicy.resolveExecutablePath!('/custom/codelldb')).toBe('/custom/codelldb'); + // CODELLDB_PATH env var is handled in codelldb-resolver.ts, not here + expect(CppAdapterPolicy.resolveExecutablePath!()).toBeUndefined(); + }); + + it('reports CodeLLDB debugger capabilities', () => { + expect(CppAdapterPolicy.getDebuggerConfiguration!()).toEqual({ + requiresStrictHandshake: false, + skipConfigurationDone: false, + supportsVariableType: true, + supportsValueFormat: true, + supportsMemoryReferences: true + }); + }); + + it('never queues commands', () => { + expect(CppAdapterPolicy.requiresCommandQueueing!()).toBe(false); + const handling = CppAdapterPolicy.shouldQueueCommand!('next', CppAdapterPolicy.createInitialState!()); + expect(handling).toEqual({ + shouldQueue: false, + shouldDefer: false, + reason: 'C++/CodeLLDB adapter does not queue commands' + }); + }); + it('tracks initialization state via the shared LLDB state helpers', () => { const state = CppAdapterPolicy.createInitialState!(); expect(CppAdapterPolicy.isInitialized!(state)).toBe(false); diff --git a/packages/shared/tests/unit/adapter-policy-rust.test.ts b/packages/shared/tests/unit/adapter-policy-rust.test.ts index 31768974..d07a5500 100644 --- a/packages/shared/tests/unit/adapter-policy-rust.test.ts +++ b/packages/shared/tests/unit/adapter-policy-rust.test.ts @@ -365,11 +365,36 @@ describe('RustAdapterPolicy', () => { }); it('never queues commands', () => { + expect(RustAdapterPolicy.requiresCommandQueueing!()).toBe(false); const result = RustAdapterPolicy.shouldQueueCommand!(); expect(result.shouldQueue).toBe(false); expect(result.shouldDefer).toBe(false); }); + it("treats only 'initialized' as the child-ready event", () => { + const event = (name: string): DebugProtocol.Event => ({ seq: 1, type: 'event', event: name }); + expect(RustAdapterPolicy.isChildReadyEvent!(event('initialized'))).toBe(true); + expect(RustAdapterPolicy.isChildReadyEvent!(event('stopped'))).toBe(false); + }); + + it('reads locals from the shared LLDB scope names', () => { + expect(RustAdapterPolicy.getLocalScopeName!()).toEqual(['Local', 'Locals']); + }); + + it('uses the CodeLLDB adapter type', () => { + expect(RustAdapterPolicy.getDapAdapterConfiguration!()).toEqual({ type: 'lldb' }); + }); + + it('reports CodeLLDB debugger capabilities', () => { + expect(RustAdapterPolicy.getDebuggerConfiguration!()).toEqual({ + requiresStrictHandshake: false, + skipConfigurationDone: false, + supportsVariableType: true, + supportsValueFormat: true, + supportsMemoryReferences: true + }); + }); + it('matches CodeLLDB adapter invocations', () => { const match = RustAdapterPolicy.matchesAdapter!({ command: '/opt/codelldb/adapter/codelldb', diff --git a/packages/shared/tests/unit/resolve-exception-filters.test.ts b/packages/shared/tests/unit/resolve-exception-filters.test.ts index f3e26f58..891bafd2 100644 --- a/packages/shared/tests/unit/resolve-exception-filters.test.ts +++ b/packages/shared/tests/unit/resolve-exception-filters.test.ts @@ -15,6 +15,7 @@ import { DotnetAdapterPolicy, RubyAdapterPolicy, RustAdapterPolicy, + CppAdapterPolicy, MockAdapterPolicy } from '../../src/index.js'; @@ -37,6 +38,10 @@ const EXPECTATIONS: PolicyExpectation[] = [ // Ruby: no uncaught-only filter in rdbg → no launch default; crashes run to termination { name: 'ruby', policy: RubyAdapterPolicy, uncaught: [], all: ['any'], defaultMode: undefined }, { name: 'rust', policy: RustAdapterPolicy, uncaught: ['rust_panic'], all: ['rust_panic', 'cpp_throw'], defaultMode: 'uncaught' }, + // Cpp: LLDB has no uncaught-only C++ filter — cpp_throw fires on every throw, + // and uncaught exceptions reach std::terminate -> SIGABRT, which LLDB stops + // on natively. So 'uncaught' maps to NO filters (issue #244 default). + { name: 'cpp', policy: CppAdapterPolicy, uncaught: [], all: ['cpp_throw'], defaultMode: 'uncaught' }, { name: 'mock', policy: MockAdapterPolicy, uncaught: ['uncaught'], all: ['all'], defaultMode: 'uncaught' } ];