import { setupTest, startEngine, startClientInTmux, openCmdFifo, writeFifoCommand, wait, ensureGenTone, execSync, waitForStatusContaining, readStatusNonBlock, teardownTest } from './test_utils'; import * as path from "path"; import * as fs from "fs"; import * as globals from "./test_globals"; export async function testSaveLoad(): Promise { console.log("\nTest: SAVE / LOAD"); setupTest(); const engine = await startEngine(); await startClientInTmux(); openCmdFifo(); await wait(1000); ensureGenTone(); // Start recording via FIFO with retry let recordAttempts = 0; while (recordAttempts < 3) { writeFifoCommand("record 0"); const st1 = await waitForStatusContaining("RECORD", 3000); if (st1.includes("RECORD")) { console.log(" DEBUG: RECORD confirmed after attempt " + (recordAttempts + 1)); break; } recordAttempts++; console.log(" WARN: First toggle attempt " + (recordAttempts) + " did not produce RECORD, retrying..."); } if (recordAttempts >= 3) { console.log(" FAIL: Could not enter RECORD after 3 attempts"); const st1 = readStatusNonBlock(); console.log(" DEBUG status after first toggle:", st1.slice(0, 200)); engine.kill(); teardownTest(); throw new Error("Could not enter RECORD"); } // Play tone into looper:input using gen_tone (synchronous, blocks until done) execSync(`${globals.GEN_TONE_BIN} 3.0 "looper:ch0in"`, { timeout: 8000 }); // 3 seconds tone // Stop recording (toggle again -> loop) writeFifoCommand("record 0"); const loopState = await waitForStatusContaining("LOOPING", 8000); if (!loopState.includes("LOOPING")) { console.log(" WARN: Second toggle did not produce LOOPING within 8s, will attempt save anyway"); console.log(" DEBUG status after second toggle:", loopState.slice(0, 200)); } else { console.log(" DEBUG: LOOPING confirmed"); } // Save via FIFO writeFifoCommand("save"); await wait(6000); // wait for synchronous save // Print engine stderr log for save debug try { const stderrLog = execSync("tail -5 /tmp/engine_stderr.log", { encoding: "utf-8" }); console.log(" Engine stderr:", stderrLog.trim()); } catch {} // Look for save file in project directory (engine writes there) const files = fs.readdirSync(globals.PROJECT_DIR); const saveFile = files.find(f => f === "save.wav"); if (saveFile) { const stat = fs.statSync(path.join(globals.PROJECT_DIR, saveFile)); if (stat.size > 44) { console.log(` PASS: save.wav created (${stat.size} bytes)`); } else { console.log(` FAIL: save.wav exists but header may be incomplete (size=${stat.size})`); engine.kill(); teardownTest(); throw new Error("save.wav too short"); } } else { console.log(" FAIL: save.wav not found in project directory"); console.log(" Directory listing: " + fs.readdirSync(globals.PROJECT_DIR).filter(f => f.endsWith(".wav")).join(",")); engine.kill(); teardownTest(); throw new Error("save.wav not created"); } // Load into channel 0 const testWavPath = path.join(globals.PROJECT_DIR, "loop.wav"); // generateTestWav is not imported here; we'll use execSync directly execSync(`sox -n -r 48000 -b 16 -c 1 ${testWavPath} synth 3.0 sine 440`, { timeout: 5000 }); await wait(500); writeFifoCommand("load loop.wav"); await wait(3000); // Wait for LOOPING state after load const loadState = await waitForStatusContaining("LOOPING", 5000); if (!loadState.includes("LOOPING")) { console.log(" WARN: Status did not show LOOPING after load command"); console.log(" Status: " + loadState.slice(0,200)); } // Check engine stderr for load success line using grep over whole file let loadSucceeded = false; try { // Look for the actual load success message (printed by exec_command->cmd_load handling) execSync("grep -q 'LOAD:' /tmp/engine_stderr.log", { timeout: 3000 }); console.log(" PASS: Engine acknowledged load command"); loadSucceeded = true; } catch { const stderrLog = execSync("cat /tmp/engine_stderr.log", { encoding: "utf-8" }).trim(); // Also search for any FIFO RECEIVED load message const hasFifo = stderrLog.includes("FIFO RECEIVED load"); console.log(" FAIL: Engine did not report LOAD: success in stderr; FIFO received = " + hasFifo); console.log(" Full stderr (last 30 lines):\n" + stderrLog.split("\n").slice(-30).join("\n")); } if (!loadSucceeded) { console.log(" FAIL: Engine load did not succeed"); engine.kill(); teardownTest(); throw new Error("Engine load reported failure"); } engine.kill(); teardownTest(); }