Files
workinf_Blender_Wasm/web/tests/unit/nla.test.mjs
mes123456 0fe8d2bb56
Some checks are pending
M6 deployable RC / quick (push) Waiting to run
M6 deployable RC / chromium (push) Blocked by required conditions
M6 deployable RC / release (push) Blocked by required conditions
Advance M8-M11 parity workflows
2026-08-17 04:37:07 -04:00

92 lines
4.0 KiB
JavaScript

import assert from "node:assert/strict";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { pathToFileURL } from "node:url";
import test from "node:test";
import ts from "typescript";
const root = path.resolve(import.meta.dirname, "../../..");
const temporary = fs.mkdtempSync(path.join(os.tmpdir(), "nla-unit-"));
function transpile(sourceName, outputName, replacements = []) {
const sourcePath = path.join(root, "web/protocol", sourceName);
const transpiled = ts.transpileModule(fs.readFileSync(sourcePath, "utf8"), {
compilerOptions: { module: ts.ModuleKind.ES2022, target: ts.ScriptTarget.ES2022 },
fileName: sourcePath,
reportDiagnostics: true,
});
assert.deepEqual(transpiled.diagnostics, []);
const output = replacements.reduce((source, [from, to]) => source.replaceAll(from, to), transpiled.outputText);
fs.writeFileSync(path.join(temporary, outputName), output);
}
transpile("capability-gates.ts", "capability-gates.mjs");
transpile("nla.ts", "nla.mjs", [['from "./capability-gates"', 'from "./capability-gates.mjs"']]);
const nla = await import(pathToFileURL(path.join(temporary, "nla.mjs")));
const actionId = "action:Move:object:Cube";
const context = {
actionIds: new Set([actionId]),
actionChannelPaths: new Map([[actionId, new Set(["location[0]"])]]),
ownerId: "object:Cube",
};
function tracks() {
return [{
schemaVersion: 1,
id: "track:Move",
ownerId: "object:Cube",
name: "Move",
muted: false,
solo: false,
selected: true,
strips: [
{ id: "strip:A", actionId, frameStart: 20, frameEnd: 40, actionFrameStart: 1, actionFrameEnd: 11, scale: 2, repeat: 1, blendIn: 0, blendOut: 0, influence: 1, blendMode: "REPLACE", extrapolation: "NOTHING", muted: false, selected: true, stripType: "CLIP" },
{ id: "strip:B", actionId, frameStart: 45, frameEnd: 65, actionFrameStart: 1, actionFrameEnd: 11, scale: 1, repeat: 2, blendIn: 0, blendOut: 0, influence: 1, blendMode: "REPLACE", extrapolation: "NOTHING", muted: false, selected: true, stripType: "CLIP" },
],
}];
}
test("M10-12 moves one NLA strip without mutating the source stack", () => {
const source = tracks();
const before = structuredClone(source);
const moved = nla.moveNlaStrip(source, {
type: "moveNLAStrip",
objectId: "object:Cube",
trackId: "track:Move",
stripId: "strip:A",
frameStart: 5,
baseRevision: 7,
}, context);
assert.deepEqual(source, before);
assert.deepEqual(moved[0].strips.map((strip) => [strip.id, strip.frameStart, strip.frameEnd]), [
["strip:A", 5, 25],
["strip:B", 45, 65],
]);
});
test("M10-12 rejects overlap, unknown identities and invalid operator budgets", () => {
const command = { type: "moveNLAStrip", objectId: "object:Cube", trackId: "track:Move", stripId: "strip:A", frameStart: 30, baseRevision: 7 };
assert.throws(() => nla.moveNlaStrip(tracks(), command, context), { code: "NLA_INVALID_STACK" });
assert.throws(() => nla.moveNlaStrip(tracks(), { ...command, trackId: "track:missing" }, context), { code: "NLA_INVALID_STACK" });
assert.throws(() => nla.moveNlaStrip(tracks(), { ...command, frameStart: 1_000_001 }, context), { code: "NLA_INVALID_STACK" });
});
test("M10-15 rejects NLA topology that exceeds the browser memory budget", () => {
const oversized = new Array(nla.NLA_STACK_BUDGET.maxTracks + 1).fill(tracks()[0]);
assert.throws(() => nla.parseNlaTracks(oversized), { code: "NLA_BUDGET_EXCEEDED" });
const tooManyStrips = [{
...tracks()[0],
strips: new Array(nla.NLA_STACK_BUDGET.maxStripsPerTrack + 1).fill(tracks()[0].strips[0]),
}];
assert.throws(() => nla.parseNlaTracks(tooManyStrips), { code: "NLA_BUDGET_EXCEEDED" });
});
test("M10-15 rejects undeclared NLA input and recovers on the next valid stack", () => {
const malicious = tracks();
malicious[0].strips[0].proxySuccess = true;
assert.throws(() => nla.parseNlaTracks(malicious), { code: "NLA_INVALID_STACK" });
assert.equal(nla.gateNlaTracks(tracks(), context).status, "READY");
});