78 lines
5.0 KiB
JavaScript
78 lines
5.0 KiB
JavaScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { interpolate, modeLabel } from "./job.js";
|
|
import { derived, gaveUp, onGaveUp, swap, wentThrough, waitJob, jobsRunning, onJobs, stopJobs, JobStopped } from "../../shell/renders.js";
|
|
|
|
const m = (f, sub = "") => ({ filename: f, subfolder: sub, type: "output" });
|
|
|
|
test("the options reach the job, the job is waited for, and an error is thrown in words", async () => {
|
|
const seen = [], seq = [{ state: "running" }, { state: "done", media: m("fi.mp4", "funui_interpolated") }];
|
|
const api = { interpolate: async (...a) => (seen.push(a), { job_id: "id" }), interpolateJob: async () => seq.shift() };
|
|
assert.equal((await interpolate(api, m("x.mp4"), { multiplier: 2 }, async () => {})).filename, "fi.mp4");
|
|
assert.deepEqual(seen[0][1], { multiplier: 2 });
|
|
const bad = { interpolate: async () => ({ job_id: "id" }), interpolateJob: async () => ({ state: "error", detail: "no libnvidia-ngx" }) };
|
|
await assert.rejects(interpolate(bad, m("x.mp4"), {}, async () => {}), /libnvidia-ngx/);
|
|
});
|
|
|
|
test("a restart is named, a dropped reply is retried", async () => {
|
|
const gone = { interpolate: async () => ({ job_id: "id" }), interpolateJob: async () => { throw Object.assign(new Error("x"), { status: 404 }); } };
|
|
await assert.rejects(interpolate(gone, m("x.mp4"), {}, async () => {}), /restarted/);
|
|
let n = 0;
|
|
const flaky = { interpolate: async () => ({ job_id: "id" }), interpolateJob: async () => { if (++n < 5) throw new Error("net"); return { state: "done", media: m("u.mp4") }; } };
|
|
assert.equal((await interpolate(flaky, m("x.mp4"), {}, async () => {})).filename, "u.mp4");
|
|
});
|
|
|
|
test("modes read as words", () => {
|
|
assert.deepEqual(["LOW", "MEDIUM", "HIGH"].map(modeLabel), ["Fast", "Balanced", "Best"]);
|
|
});
|
|
|
|
test("a file that went through a step is never offered it again, whichever order the two steps ran in", () => {
|
|
const raw = m("a.mp4"), up = m("a_up.mp4", "funui_upscaled"), fi = m("a_x2.mp4", "funui_interpolated"), both = m("a_up_x2.mp4", "funui_interpolated");
|
|
assert.ok(!wentThrough(raw, "upscale") && !wentThrough(raw, "interpolate"));
|
|
assert.ok(wentThrough(up, "upscale") && !wentThrough(up, "interpolate"));
|
|
derived(up, both, "interpolate"); // upscaled first, then interpolated
|
|
assert.ok(wentThrough(both, "upscale") && wentThrough(both, "interpolate")); // so the upscale's "always" cannot take it again: no loop
|
|
assert.ok(wentThrough(fi, "interpolate") && !wentThrough(fi, "upscale")); // interpolated first: still upscalable once
|
|
});
|
|
|
|
test("the interpolated file replaces the render everywhere it played", () => {
|
|
const p = { scene_renders: { a: { media: m("x.mp4") } }, scene_ghosts: [] };
|
|
assert.equal(swap(p, m("x.mp4"), m("fi.mp4", "funui_interpolated")), 1);
|
|
});
|
|
|
|
test("a step that gave up is announced, so a step waiting behind it can go on without it", () => {
|
|
const heard = [];
|
|
onGaveUp((media, step) => heard.push([media.filename, step]));
|
|
gaveUp(m("r.mp4"), "upscale");
|
|
assert.deepEqual(heard, [["r.mp4", "upscale"]]);
|
|
});
|
|
|
|
test("Stop: a job being waited for can be stopped, ends as stopped (not failed, not restarted), and the Stop button hears about it", async () => {
|
|
const stopped = [], heard = [];
|
|
const off = onJobs(() => heard.push(jobsRunning()));
|
|
let polls = 0;
|
|
const read = async () => (++polls < 2 ? { state: "running" } : { state: "stopped" });
|
|
const waiting = waitJob(read, "j1", async () => { if (polls === 0) await stopJobs(); }, (id) => (stopped.push(id), { stopped: true }));
|
|
await assert.rejects(waiting, (err) => err instanceof JobStopped && err.stopped === true && err.message === "Stopped.");
|
|
assert.deepEqual(stopped, ["j1"]);
|
|
assert.deepEqual(heard, [1, 0]); // shown while it waits, hidden when it ends
|
|
assert.equal(jobsRunning(), 0);
|
|
off();
|
|
});
|
|
|
|
test("Stop: a stopped job that is read once more is still there (no 404 turned into 'ComfyUI restarted')", async () => {
|
|
await assert.rejects(waitJob(async () => ({ state: "stopped" }), "j2", async () => {}), (err) => err.stopped === true);
|
|
});
|
|
|
|
test("Stop: a chunk that fails while the stop is landing ends as stopped, not as a failure (nothing 'always' follows it)", async () => {
|
|
let polls = 0;
|
|
const read = async () => (++polls < 2 ? { state: "running" } : { state: "error", detail: "out of memory" });
|
|
await assert.rejects(waitJob(read, "j3", async () => { if (polls === 0) await stopJobs(); }, () => ({ stopped: true })), (err) => err.stopped === true);
|
|
// a stop request that failed, or found the job already over, does not turn a real failure into a stop
|
|
for (const stop of [() => { throw new Error("network"); }, () => ({ stopped: false })]) {
|
|
let n = 0;
|
|
await assert.rejects(waitJob(async () => (++n < 2 ? { state: "running" } : { state: "error", detail: "out of memory" }), "j5", async () => { if (n === 0) await stopJobs(); }, stop), /out of memory/);
|
|
}
|
|
await assert.rejects(waitJob(async () => ({ state: "error", detail: "boom" }), "j4", async () => {}), /boom/);
|
|
});
|