118 lines
4.6 KiB
TypeScript
118 lines
4.6 KiB
TypeScript
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<void> {
|
|
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();
|
|
}
|