diff --git a/extensions/vscode/src/test/runTest.ts b/extensions/vscode/src/test/runTest.ts index 0ceb97fc..00ade41d 100644 --- a/extensions/vscode/src/test/runTest.ts +++ b/extensions/vscode/src/test/runTest.ts @@ -28,6 +28,10 @@ type DebugMessage = { }; const BREAKPOINT_SYNC_TEST_LOG_ENV = "SOROBAN_DEBUG_BREAKPOINT_SYNC_TEST_LOG"; +const FIXTURE_BREAKPOINT_MARKERS = { + nonExportedHelper: "BREAKPOINT_MARKER: non-exported-helper", + exportedEcho: "BREAKPOINT_MARKER: exported-echo", +} as const; async function startMockDebuggerServer(options: { evaluateDelayMs: number; @@ -195,6 +199,17 @@ async function assertProcessLogAbsent( ); } +function findFixtureLine(sourcePath: string, marker: string): number { + const lines = fs.readFileSync(sourcePath, "utf8").split(/\r?\n/); + const lineNumber = lines.findIndex((line) => line.includes(marker)) + 1; + assert.notEqual( + lineNumber, + 0, + `Expected fixture marker '${marker}' in ${sourcePath}`, + ); + return lineNumber; +} + async function main(): Promise { if (!(await isLoopbackAvailable())) { console.warn( @@ -785,10 +800,18 @@ async function main(): Promise { "lib.rs", ); assert.ok(fs.existsSync(sourcePath), `Missing fixture source: ${sourcePath}`); + const exportedEchoLine = findFixtureLine( + sourcePath, + FIXTURE_BREAKPOINT_MARKERS.exportedEcho, + ); + const nonExportedHelperLine = findFixtureLine( + sourcePath, + FIXTURE_BREAKPOINT_MARKERS.nonExportedHelper, + ); const exportedFunctions = await debuggerProcess.getContractFunctions(); const resolvedBreakpoints = resolveSourceBreakpoints( sourcePath, - [14], + [exportedEchoLine], exportedFunctions, ); assert.equal( @@ -805,7 +828,7 @@ async function main(): Promise { const nonExportedBreakpoints = resolveSourceBreakpoints( sourcePath, - [10], + [nonExportedHelperLine], exportedFunctions, ); assert.equal( @@ -832,7 +855,7 @@ async function main(): Promise { // Test HEURISTIC_NO_FUNCTION behavior for lines outside any function const noFunctionBreakpoints = resolveSourceBreakpoints( sourcePath, - [1, 2, 12, 16], // Lines outside any function in lib.rs + [1, 2, 13, 18], // Lines outside any function in lib.rs exportedFunctions, ); @@ -1038,7 +1061,14 @@ async function runDapHappyPathE2E( const setBps = await client.request("setBreakpoints", { source: { path: fixtures.sourcePath }, - breakpoints: [{ line: 14 }], + breakpoints: [ + { + line: findFixtureLine( + fixtures.sourcePath, + FIXTURE_BREAKPOINT_MARKERS.exportedEcho, + ), + }, + ], }); assert.equal( setBps.success, @@ -1062,7 +1092,14 @@ async function runDapHappyPathE2E( const privateBps = await client.request("setBreakpoints", { source: { path: fixtures.sourcePath }, - breakpoints: [{ line: 10 }], + breakpoints: [ + { + line: findFixtureLine( + fixtures.sourcePath, + FIXTURE_BREAKPOINT_MARKERS.nonExportedHelper, + ), + }, + ], }); assert.equal( privateBps.success, diff --git a/extensions/vscode/src/test/suites.ts b/extensions/vscode/src/test/suites.ts index 2d71a3bf..f7e1da1a 100644 --- a/extensions/vscode/src/test/suites.ts +++ b/extensions/vscode/src/test/suites.ts @@ -29,6 +29,10 @@ type TestFixtures = { }; const BREAKPOINT_SYNC_TEST_LOG_ENV = "SOROBAN_DEBUG_BREAKPOINT_SYNC_TEST_LOG"; +const FIXTURE_BREAKPOINT_MARKERS = { + nonExportedHelper: "BREAKPOINT_MARKER: non-exported-helper", + exportedEcho: "BREAKPOINT_MARKER: exported-echo", +} as const; async function startMockDebuggerServer(options: { evaluateDelayMs: number; @@ -233,6 +237,17 @@ function resolveFixtures(): TestFixtures { }; } +function findFixtureLine(sourcePath: string, marker: string): number { + const lines = fs.readFileSync(sourcePath, "utf8").split(/\r?\n/); + const lineNumber = lines.findIndex((line) => line.includes(marker)) + 1; + assert.notEqual( + lineNumber, + 0, + `Expected fixture marker '${marker}' in ${sourcePath}`, + ); + return lineNumber; +} + export async function runSmokeSuite(): Promise { { const mock = "backend: 1.2.3 protocol: 4.5.6"; @@ -575,10 +590,18 @@ export async function runSmokeSuite(): Promise { await debuggerProcess.start(); await debuggerProcess.ping(); + const exportedEchoLine = findFixtureLine( + fixtures.sourcePath, + FIXTURE_BREAKPOINT_MARKERS.exportedEcho, + ); + const nonExportedHelperLine = findFixtureLine( + fixtures.sourcePath, + FIXTURE_BREAKPOINT_MARKERS.nonExportedHelper, + ); const exportedFunctions = await debuggerProcess.getContractFunctions(); const resolvedBreakpoints = resolveSourceBreakpoints( fixtures.sourcePath, - [14], + [exportedEchoLine], exportedFunctions, ); assert.equal( @@ -595,7 +618,7 @@ export async function runSmokeSuite(): Promise { const nonExportedBreakpoints = resolveSourceBreakpoints( fixtures.sourcePath, - [10], + [nonExportedHelperLine], exportedFunctions, ); assert.equal( @@ -622,7 +645,7 @@ export async function runSmokeSuite(): Promise { // Test HEURISTIC_NO_FUNCTION behavior for lines outside any function const noFunctionBreakpoints = resolveSourceBreakpoints( fixtures.sourcePath, - [1, 2, 12, 16], // Lines outside any function in lib.rs + [1, 2, 13, 18], // Lines outside any function in lib.rs exportedFunctions, ); @@ -844,7 +867,14 @@ async function runDapHappyPathE2E( const setBps = await client.request("setBreakpoints", { source: { path: fixtures.sourcePath }, - breakpoints: [{ line: 14 }], + breakpoints: [ + { + line: findFixtureLine( + fixtures.sourcePath, + FIXTURE_BREAKPOINT_MARKERS.exportedEcho, + ), + }, + ], }); assert.equal( setBps.success, @@ -878,7 +908,14 @@ async function runDapHappyPathE2E( const privateBps = await client.request("setBreakpoints", { source: { path: fixtures.sourcePath }, - breakpoints: [{ line: 10 }], + breakpoints: [ + { + line: findFixtureLine( + fixtures.sourcePath, + FIXTURE_BREAKPOINT_MARKERS.nonExportedHelper, + ), + }, + ], }); assert.equal( privateBps.success, diff --git a/src/repl/session.rs b/src/repl/session.rs index 4ca5a208..2d748501 100644 --- a/src/repl/session.rs +++ b/src/repl/session.rs @@ -116,9 +116,15 @@ impl Completer for ReplHelper { impl ReplSession { /// Create a new REPL session pub fn new(config: ReplConfig) -> Result { - let history_path = dirs::home_dir() - .unwrap_or_else(std::env::temp_dir) - .join(".soroban_repl_history"); + let history_base_dir = dirs::home_dir().unwrap_or_else(|| { + let fallback_dir = std::env::temp_dir(); + tracing::warn!( + "HOME directory is unavailable; REPL history will be stored in temporary directory: {}", + fallback_dir.display() + ); + fallback_dir + }); + let history_path = history_base_dir.join(".soroban_repl_history"); let executor = ReplExecutor::new(&config)?; let helper = ReplHelper::new( diff --git a/tests/fixtures/contracts/echo/src/lib.rs b/tests/fixtures/contracts/echo/src/lib.rs index fdd1035b..269fcaa0 100644 --- a/tests/fixtures/contracts/echo/src/lib.rs +++ b/tests/fixtures/contracts/echo/src/lib.rs @@ -7,10 +7,12 @@ pub struct Echo; #[contractimpl] impl Echo { fn helper(v: Val) -> Val { + // BREAKPOINT_MARKER: non-exported-helper v } pub fn echo(_env: Env, v: Val) -> Val { + // BREAKPOINT_MARKER: exported-echo Self::helper(v) } }