feat: play moving-source acoustic timelines
This commit is contained in:
@@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -11,6 +11,24 @@ async function loadJson(path) {
|
||||
return response.json();
|
||||
}
|
||||
|
||||
test("moving-tone sample Scene and Experiment satisfy their linked contracts", async () => {
|
||||
const [arrayDefinition, scene, experiment] = await Promise.all([
|
||||
loadJson("/assets/array-geometries/rectangular-16x12.array.json"),
|
||||
loadJson("/assets/sample-scenes/moving-tone.scene.json"),
|
||||
loadJson("/assets/sample-scenes/moving-tone.experiment.json"),
|
||||
]);
|
||||
const sceneIssues = await validateScene(scene);
|
||||
const experimentIssues = await validateExperiment(experiment, { arrayDefinition, scene });
|
||||
|
||||
assertEqual(sceneIssues.length, 0, `moving Scene issues: ${JSON.stringify(sceneIssues)}`);
|
||||
assertEqual(experimentIssues.length, 0, `moving Experiment issues: ${JSON.stringify(experimentIssues)}`);
|
||||
assertEqual(experiment.sceneId, scene.id, "moving example linkage");
|
||||
assert(
|
||||
scene.sources.some((source) => Array.isArray(source.trajectory) && source.trajectory.length >= 2),
|
||||
"moving example contains a usable trajectory",
|
||||
);
|
||||
});
|
||||
|
||||
test("192채널 기준 배열이 계약을 통과한다", async () => {
|
||||
const arrayDefinition = await loadJson("/assets/array-geometries/rectangular-16x12.array.json");
|
||||
assertEqual(arrayDefinition.microphones.length, 192, "microphone count");
|
||||
|
||||
Reference in New Issue
Block a user