feat: play moving-source acoustic timelines

This commit is contained in:
Codex
2026-08-28 16:23:26 +09:00
parent 01a37eac69
commit a54aa25cd9
8 changed files with 665 additions and 8 deletions
+91
View File
@@ -209,3 +209,94 @@ test("Acoustic Camera가 8×8 CPU 실행·결과 저장·stale·재실행 흐름
frame.remove();
}
});
test("Acoustic Camera prepares, scrubs, plays, and pauses the moving-source example", async () => {
const frame = document.createElement("iframe");
frame.src = "/apps/acoustic-camera/?integration-test=1";
frame.style.cssText = "position:fixed;left:-20000px;top:0;width:1400px;height:1000px;border:0";
document.body.append(frame);
await new Promise((resolve, reject) => {
frame.addEventListener("load", resolve, { once: true });
frame.addEventListener("error", () => reject(new Error("Acoustic Camera live iframe load failed")), {
once: true,
});
});
const windowObject = frame.contentWindow;
const documentObject = frame.contentDocument;
try {
await waitFor(
() => documentObject.documentElement.dataset.appReady === "true",
"Acoustic Camera live initialization",
);
documentObject.querySelector("#load-moving-example-button").click();
await waitFor(
() => documentObject.documentElement.dataset.inputBusy === "false" &&
documentObject.documentElement.dataset.cameraValid === "true" &&
documentObject.documentElement.dataset.liveState === "idle" &&
documentObject.querySelector("#experiment-id").value === "moving-tone-live" &&
!documentObject.querySelector("#prepare-live-button").disabled,
"valid moving example trajectory",
);
dispatchValue(windowObject, documentObject.querySelector("#scan-columns"), 8);
dispatchValue(windowObject, documentObject.querySelector("#scan-rows"), 8);
dispatchValue(windowObject, documentObject.querySelector("#backend"), "cpu");
dispatchValue(windowObject, documentObject.querySelector("#live-map-rate"), 2);
await waitFor(
() => documentObject.documentElement.dataset.cameraValid === "true" &&
documentObject.documentElement.dataset.liveState === "idle" &&
documentObject.querySelector("#scan-columns").value === "8" &&
documentObject.querySelector("#scan-rows").value === "8" &&
documentObject.querySelector("#backend").value === "cpu" &&
documentObject.querySelector("#live-map-rate").value === "2" &&
!documentObject.querySelector("#prepare-live-button").disabled,
"8x8 CPU 2 Hz live configuration",
);
documentObject.querySelector("#prepare-live-button").click();
await waitFor(
() => documentObject.documentElement.dataset.liveState === "ready",
"moving-source live preparation",
45000,
);
const frameCount = Number(documentObject.documentElement.dataset.liveFrameCount);
assert(frameCount > 1, `multiple live frames: ${frameCount}`);
assert(documentObject.querySelector("#live-status").textContent.includes("CPU"), "live backend is CPU");
dispatchValue(windowObject, documentObject.querySelector("#live-time"), 0.6, "input");
await waitFor(
() => Number(documentObject.documentElement.dataset.liveTime) >= 0.599,
"live scrub to a later time",
);
const scrubbedTime = Number(documentObject.documentElement.dataset.liveTime);
assert(Number(documentObject.documentElement.dataset.liveFrame) > 0, "scrub selects a later map frame");
documentObject.querySelector("#live-play-button").click();
await waitFor(
() => documentObject.documentElement.dataset.liveState === "playing",
"live playback start",
);
await waitFor(
() => Number(documentObject.documentElement.dataset.liveTime) > scrubbedTime + 0.05,
"live playback time advance",
5000,
);
documentObject.querySelector("#live-play-button").click();
await waitFor(
() => documentObject.documentElement.dataset.liveState === "ready",
"live playback pause",
);
const pausedTime = Number(documentObject.documentElement.dataset.liveTime);
await new Promise((resolve) => setTimeout(resolve, 120));
const timeAfterPause = Number(documentObject.documentElement.dataset.liveTime);
assert(
Math.abs(timeAfterPause - pausedTime) <= 0.01,
`paused live time remains stable: ${pausedTime} -> ${timeAfterPause}`,
);
} finally {
frame.remove();
}
});