112 lines
5.5 KiB
JavaScript
112 lines
5.5 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import crypto from "node:crypto";
|
|
import fs from "node:fs";
|
|
import factory from "../../web/app/src/vendor/blender/web_engine.js";
|
|
|
|
const root = new URL("../../", import.meta.url);
|
|
const wasmBinary = fs.readFileSync(new URL("web/app/src/vendor/blender/web_engine.wasm", root));
|
|
const expected = JSON.parse(fs.readFileSync(new URL("tests/golden/M10-01/geometry-node-main-reader.json", root), "utf8"));
|
|
const fixture = fs.readFileSync(new URL(expected.fixture, root));
|
|
const desktopGoldenBytes = fs.readFileSync(new URL(expected.desktopGolden, root));
|
|
const desktopGolden = JSON.parse(desktopGoldenBytes.toString("utf8"));
|
|
|
|
assert.equal(crypto.createHash("sha256").update(fixture).digest("hex"), expected.fixtureSha256);
|
|
assert.equal(crypto.createHash("sha256").update(desktopGoldenBytes).digest("hex"), expected.desktopGoldenSha256);
|
|
|
|
function open(engine, handle, bytes) {
|
|
const pointer = engine._malloc(bytes.byteLength);
|
|
try {
|
|
engine.HEAPU8.set(bytes, pointer);
|
|
assert.equal(engine._web_engine_open_blend(handle, pointer, bytes.byteLength), 0,
|
|
engine.UTF8ToString(engine._web_engine_last_error_message()));
|
|
}
|
|
finally { engine._free(pointer); }
|
|
}
|
|
|
|
function output(engine, handle, fn, owned = false) {
|
|
const dataOut = engine._malloc(4);
|
|
const lengthOut = engine._malloc(4);
|
|
try {
|
|
assert.equal(fn(handle, dataOut, lengthOut), 0, engine.UTF8ToString(engine._web_engine_last_error_message()));
|
|
const pointer = engine.HEAPU32[dataOut >>> 2];
|
|
const length = engine.HEAPU32[lengthOut >>> 2];
|
|
const bytes = engine.HEAPU8.slice(pointer, pointer + length);
|
|
if (owned) engine._web_engine_free_buffer(pointer);
|
|
return bytes;
|
|
}
|
|
finally { engine._free(dataOut); engine._free(lengthOut); }
|
|
}
|
|
|
|
function snapshot(engine, handle) {
|
|
return JSON.parse(new TextDecoder().decode(output(engine, handle, engine._web_engine_get_scene_snapshot)));
|
|
}
|
|
|
|
function socketDefault(graph, key) {
|
|
const separator = key.indexOf(":");
|
|
const nodeType = key.slice(0, separator);
|
|
const socketName = key.slice(separator + 1);
|
|
const node = graph.nodes.find((candidate) => candidate.type === nodeType);
|
|
assert.ok(node, `missing node ${nodeType} in ${graph.name}`);
|
|
const socket = node.sockets.find((candidate) => candidate.name === socketName);
|
|
assert.ok(socket, `missing socket ${nodeType}:${socketName}`);
|
|
return socket.defaultValue;
|
|
}
|
|
|
|
function assertGraphs(scene) {
|
|
const graphs = scene.geometryNodeGraphs;
|
|
assert.equal(graphs?.length, expected.graphs.length);
|
|
const desktopByName = new Map(desktopGolden.nodeGroups.map((graph) => [graph.name, graph]));
|
|
const expectedByName = new Map(expected.graphs.map((graph) => [graph.name, graph]));
|
|
for (const graph of graphs) {
|
|
const manifest = expectedByName.get(graph.name);
|
|
const desktop = desktopByName.get(graph.name);
|
|
assert.ok(manifest && desktop, `unexpected graph ${graph.name}`);
|
|
assert.equal(graph.schemaVersion, 1);
|
|
assert.equal(graph.id, `node-group:${graph.name}`);
|
|
assert.match(graph.graphHash, /^[0-9a-f]{64}$/);
|
|
assert.deepEqual(graph.nodes.map((node) => node.type), manifest.nodeTypes);
|
|
const nodeKey = (node) => `${node.name}\u0000${node.type}`;
|
|
assert.deepEqual(
|
|
[...graph.nodes].map((node) => ({ name: node.name, type: node.type })).sort((a, b) => nodeKey(a).localeCompare(nodeKey(b))),
|
|
[...desktop.nodes].sort((a, b) => nodeKey(a).localeCompare(nodeKey(b))),
|
|
);
|
|
assert.equal(graph.links.length, manifest.linkCount);
|
|
assert.equal(graph.links.length, desktop.links);
|
|
assert.deepEqual(graph.interfaceInputs.map((socket) => [socket.direction, socket.dataType]), [["INPUT", "GEOMETRY"]]);
|
|
assert.deepEqual(graph.interfaceOutputs.map((socket) => [socket.direction, socket.dataType]), [["OUTPUT", "GEOMETRY"]]);
|
|
assert.equal(new Set(graph.nodes.map((node) => node.id)).size, graph.nodes.length);
|
|
const nodeById = new Map(graph.nodes.map((node) => [node.id, node]));
|
|
for (const node of graph.nodes) {
|
|
assert.match(node.id, /^geometry-node:[1-9][0-9]*$/);
|
|
assert.equal(node.type.includes("Undefined["), false);
|
|
assert.equal(node.sockets.some((socket) => socket.id.endsWith(":__extend__")), false);
|
|
assert.equal(new Set(node.sockets.map((socket) => socket.id)).size, node.sockets.length);
|
|
}
|
|
for (const link of graph.links) {
|
|
const fromNode = nodeById.get(link.fromNodeId);
|
|
const toNode = nodeById.get(link.toNodeId);
|
|
assert.ok(fromNode?.sockets.some((socket) => socket.id === link.fromSocketId && socket.direction === "OUTPUT"));
|
|
assert.ok(toNode?.sockets.some((socket) => socket.id === link.toSocketId && socket.direction === "INPUT"));
|
|
}
|
|
for (const [key, value] of Object.entries(manifest.defaults)) {
|
|
assert.deepEqual(socketDefault(graph, key), value);
|
|
}
|
|
}
|
|
return graphs;
|
|
}
|
|
|
|
const engine = await factory({ wasmBinary: wasmBinary.slice() });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fixture);
|
|
const initialGraphs = assertGraphs(snapshot(engine, handle));
|
|
const saved = output(engine, handle, engine._web_engine_save_blend, true);
|
|
engine._web_engine_destroy(handle);
|
|
|
|
const reopened = engine._web_engine_create();
|
|
open(engine, reopened, saved);
|
|
const reopenedGraphs = assertGraphs(snapshot(engine, reopened));
|
|
assert.deepEqual(reopenedGraphs, initialGraphs);
|
|
engine._web_engine_destroy(reopened);
|
|
|
|
process.stdout.write("geometry-node-main-reader-ok graphs=3 nodes=10 links=7 defaults=9 stable-id=passed graph-hash=passed desktop-golden=passed save-reopen=passed simulation-preserved=passed\n");
|