import assert from "node:assert/strict"; 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)); async function openFixture(name) { const engine = await factory({ wasmBinary: wasmBinary.slice() }); const handle = engine._web_engine_create(); const blend = fs.readFileSync(new URL(`tests/files/web/${name}`, root)); const input = engine._malloc(blend.byteLength); engine.HEAPU8.set(blend, input); assert.equal(engine._web_engine_open_blend(handle, input, blend.byteLength), 0, `${name} open`); engine._free(input); const dataOut = engine._malloc(4); const lengthOut = engine._malloc(4); assert.equal(engine._web_engine_get_scene_snapshot(handle, dataOut, lengthOut), 0); const pointer = engine.HEAPU32[dataOut >>> 2]; const length = engine.HEAPU32[lengthOut >>> 2]; const snapshot = JSON.parse(new TextDecoder().decode(engine.HEAPU8.slice(pointer, pointer + length))); engine._free(dataOut); engine._free(lengthOut); return { engine, handle, snapshot }; } function packedAsset(engine, handle, assetId) { const encoded = new TextEncoder().encode(assetId); const id = engine._malloc(encoded.byteLength); const dataOut = engine._malloc(4); const lengthOut = engine._malloc(4); engine.HEAPU8.set(encoded, id); try { assert.equal(engine._web_engine_get_packed_asset(handle, id, encoded.byteLength, dataOut, lengthOut), 0, assetId); const pointer = engine.HEAPU32[dataOut >>> 2]; const length = engine.HEAPU32[lengthOut >>> 2]; return engine.HEAPU8.slice(pointer, pointer + length); } finally { engine._free(id); engine._free(dataOut); engine._free(lengthOut); } } const matrix = await openFixture("image_resource_matrix.blend"); try { const byName = new Map(matrix.snapshot.images.map((image) => [image.name, image])); const generated = byName.get("GeneratedTexture"); assert.equal(generated?.sourceKind, "GENERATED"); assert.equal(generated?.assetStatus, "GENERATED"); assert.equal(generated?.width, 12); assert.equal(generated?.height, 6); const udim = byName.get("UDIMTexture"); assert.equal(udim?.sourceKind, "TILED"); assert.equal(udim?.assetStatus, "PACKED"); assert.deepEqual(udim?.tiles.map((tile) => tile.number), [1001, 1002]); for (const tile of udim.tiles) { assert.equal(tile.packed, true); assert.equal(tile.validSignature, true); assert.deepEqual([...packedAsset(matrix.engine, matrix.handle, tile.assetId).slice(0, 8)], [137, 80, 78, 71, 13, 10, 26, 10]); } const linked = matrix.snapshot.libraries.find((library) => /image_resource_library\.blend$/.test(library.sourcePath)); assert.ok(linked, "linked image library resource is missing"); assert.equal(linked.packed, false); assert.equal(linked.status, "EXTERNAL_REQUIRED"); assert.equal(linked.errorCode, "LINKED_LIBRARY_RESOURCE_REQUIRED"); const missing = byName.get("MissingExternalTexture"); assert.equal(missing?.sourceKind, "FILE"); assert.equal(missing?.assetStatus, "EXTERNAL"); assert.match(missing?.sourcePath, /missing\/not-present\.png$/); } finally { matrix.engine._web_engine_destroy(matrix.handle); } const corrupt = await openFixture("corrupt_packed_image.blend"); try { const image = corrupt.snapshot.images.find((candidate) => candidate.name === "CorruptPackedTexture"); assert.equal(image?.packed, true); assert.equal(image?.assetStatus, "CORRUPT"); assert.equal(image?.errorCode, "PACKED_IMAGE_SIGNATURE_INVALID"); } finally { corrupt.engine._web_engine_destroy(corrupt.handle); } process.stdout.write("image-resources-ok udimTiles=2 generated=1 linked=1 corrupt=1 external=1\n");