1165 lines
136 KiB
JavaScript
1165 lines
136 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import crypto from "node:crypto";
|
|
import { spawnSync } from "node:child_process";
|
|
import factory from "../../web/app/src/vendor/blender/web_engine.js";
|
|
import { readIndexedTask, verifyTaskIndex } from "./task-context-lib.mjs";
|
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
|
const task = process.argv[process.argv.indexOf("--task") + 1];
|
|
verifyTaskIndex();
|
|
const entry = readIndexedTask(task); assert.ok(entry); assert.ok(["active", "completed"].includes(entry.state));
|
|
const wasmBinary = fs.readFileSync(path.join(root, "web/app/src/vendor/blender/web_engine.wasm"));
|
|
const open = (engine, handle, input) => { const pointer = engine._malloc(input.byteLength); engine.HEAPU8.set(input, pointer); try { assert.equal(engine._web_engine_open_blend(handle, pointer, input.byteLength), 0, engine.UTF8ToString(engine._web_engine_last_error_message())); } finally { engine._free(pointer); } };
|
|
const output = (engine, handle, fn, owned = false) => { const data = engine._malloc(4); const length = engine._malloc(4); try { assert.equal(fn(handle, data, length), 0, engine.UTF8ToString(engine._web_engine_last_error_message())); const pointer = engine.HEAPU32[data >>> 2]; const size = engine.HEAPU32[length >>> 2]; const result = engine.HEAPU8.slice(pointer, pointer + size); if (owned) engine._web_engine_free_buffer(pointer); return result; } finally { engine._free(data); engine._free(length); } };
|
|
const snapshot = (engine, handle) => JSON.parse(new TextDecoder().decode(output(engine, handle, engine._web_engine_get_scene_snapshot)));
|
|
if (task === "M16-GAP-00002") {
|
|
const armatureFixture = path.join(root, "tests/files/web/generated/M16-GAP-00002-datablock-Armature.blend"); const armatureDesktopPath = path.join(root, "tests/golden/M16-GAP-00002/armature-desktop-report.json");
|
|
const armatureDesktop = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-armature-desktop.py"), "--", armatureFixture, armatureDesktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 }); assert.equal(armatureDesktop.status, 0, `${armatureDesktop.stdout ?? ""}\n${armatureDesktop.stderr ?? ""}`); const desktopEvidence = JSON.parse(fs.readFileSync(armatureDesktopPath, "utf8")); const armatureBytes = fs.readFileSync(armatureFixture); const armatureEngine = await factory({ wasmBinary }); const armatureHandle = armatureEngine._web_engine_create(); open(armatureEngine, armatureHandle, armatureBytes); const armatureSnapshot = snapshot(armatureEngine, armatureHandle); const armature = armatureSnapshot.armatures?.find((value) => value.id === "armature:RiggedArmature"); const armatureMesh = armatureSnapshot.meshes?.find((value) => value.id === "mesh:RiggedShapeMesh"); assert.ok(armature); assert.deepEqual(armature.bones.map((bone) => bone.name), ["Root", "Tip"]); assert.equal(armature.bones[1].parentId, armature.bones[0].id); assert.ok(armatureMesh?.skinWeights?.boneNames?.includes("Root")); const armatureSaved = output(armatureEngine, armatureHandle, armatureEngine._web_engine_save_blend, true); const armatureReopened = armatureEngine._web_engine_create(); open(armatureEngine, armatureReopened, armatureSaved); const armatureAfter = snapshot(armatureEngine, armatureReopened).armatures?.find((value) => value.id === "armature:RiggedArmature"); assert.deepEqual(armatureAfter, armature); const armatureReport = { schemaVersion: 1, task: "M16-GAP-00002", operation: "ARMATURE_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, armatureFixture).replaceAll(path.sep, "/"), sha256: desktopEvidence.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, armatureDesktopPath).replaceAll(path.sep, "/") }, wasm: { status: "EXACT", armatureId: armature.id, bones: armature.bones.map((bone) => ({ name: bone.name, parentId: bone.parentId })) }, saveReopen: "EXACT", nextTask: "M16-GAP-00003" }; const armatureReportPath = path.join(root, "tests/golden/M16-GAP-00002/armature-local-exact-report.json"); fs.mkdirSync(path.dirname(armatureReportPath), { recursive: true }); fs.writeFileSync(armatureReportPath, `${JSON.stringify(armatureReport, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} bones=${armature.bones.length} desktop=exact saveReopen=exact next=${armatureReport.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00003") {
|
|
const brushFixture = path.join(root, "tests/files/web/generated/M16-GAP-00003-datablock-Brush.blend"); const brushDesktopPath = path.join(root, "tests/golden/M16-GAP-00003/brush-desktop-report.json"); const brushDesktop = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-brush-desktop.py"), "--", brushFixture, brushDesktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 }); assert.equal(brushDesktop.status, 0, `${brushDesktop.stdout ?? ""}\n${brushDesktop.stderr ?? ""}`); const desktopEvidence = JSON.parse(fs.readFileSync(brushDesktopPath, "utf8")); const brushBytes = fs.readFileSync(brushFixture); const brushEngine = await factory({ wasmBinary }); const brushHandle = brushEngine._web_engine_create(); open(brushEngine, brushHandle, brushBytes); const brushSnapshot = snapshot(brushEngine, brushHandle); const brush = brushSnapshot.brushes?.find((value) => value.id === "brush:WebGapBrush"); assert.ok(brush); assert.equal(brush.name, "WebGapBrush"); assert.equal(brush.size, 42); assert.ok(Math.abs(brush.alpha - 0.65) < 1e-5); assert.ok(Math.abs(brush.hardness - 0.8) < 1e-5); assert.equal(brush.spacing, 17); assert.ok(Math.abs(brush.jitter - 0.2) < 1e-5); const brushSaved = output(brushEngine, brushHandle, brushEngine._web_engine_save_blend, true); const brushReopened = brushEngine._web_engine_create(); open(brushEngine, brushReopened, brushSaved); const brushAfter = snapshot(brushEngine, brushReopened).brushes?.find((value) => value.id === brush.id); assert.deepEqual(brushAfter, brush); const brushReport = { schemaVersion: 1, task: "M16-GAP-00003", operation: "BRUSH_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, brushFixture).replaceAll(path.sep, "/"), sha256: desktopEvidence.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, brushDesktopPath).replaceAll(path.sep, "/") }, wasm: { status: "EXACT", brushId: brush.id, values: { size: brush.size, alpha: brush.alpha, hardness: brush.hardness, spacing: brush.spacing, jitter: brush.jitter, sculptTool: brush.sculptTool } }, saveReopen: "EXACT", nextTask: "M16-GAP-00004" }; const brushReportPath = path.join(root, "tests/golden/M16-GAP-00003/brush-local-exact-report.json"); fs.mkdirSync(path.dirname(brushReportPath), { recursive: true }); fs.writeFileSync(brushReportPath, `${JSON.stringify(brushReport, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} brush=${brush.id} size=${brush.size} desktop=exact saveReopen=exact next=${brushReport.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00004" && !process.env.USE_LEGACY_CAMERA_BRANCH) {
|
|
const cameraFixture = path.join(root, "tests/files/web/generated/M16-GAP-00004-datablock-Camera.blend");
|
|
const cameraDesktopPath = path.join(root, "tests/golden/M16-GAP-00004/camera-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(cameraDesktopPath), { recursive: true });
|
|
const cameraDesktop = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-camera-desktop.py"), "--", cameraFixture, cameraDesktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(cameraDesktop.status, 0, `${cameraDesktop.stdout ?? ""}\n${cameraDesktop.stderr ?? ""}`);
|
|
const desktopEvidence = JSON.parse(fs.readFileSync(cameraDesktopPath, "utf8"));
|
|
const cameraBytes = fs.readFileSync(cameraFixture);
|
|
const cameraEngine = await factory({ wasmBinary });
|
|
const cameraHandle = cameraEngine._web_engine_create();
|
|
open(cameraEngine, cameraHandle, cameraBytes);
|
|
const camera = snapshot(cameraEngine, cameraHandle).cameras?.find((value) => value.id === "camera:WebGapCamera");
|
|
assert.ok(camera);
|
|
assert.equal(camera.projection, "PERSPECTIVE");
|
|
assert.ok(Math.abs(camera.lensMm - 52) < 1e-5);
|
|
assert.ok(Math.abs(camera.near - 0.1) < 1e-5);
|
|
assert.ok(Math.abs(camera.far - 2500) < 1e-5);
|
|
const cameraSaved = output(cameraEngine, cameraHandle, cameraEngine._web_engine_save_blend, true);
|
|
const cameraReopened = cameraEngine._web_engine_create();
|
|
open(cameraEngine, cameraReopened, cameraSaved);
|
|
const cameraAfter = snapshot(cameraEngine, cameraReopened).cameras?.find((value) => value.id === camera.id);
|
|
assert.deepEqual(cameraAfter, camera);
|
|
const cameraReport = { schemaVersion: 1, task: "M16-GAP-00004", operation: "CAMERA_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, cameraFixture).replaceAll(path.sep, "/"), sha256: desktopEvidence.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, cameraDesktopPath).replaceAll(path.sep, "/") }, wasm: { status: "EXACT", cameraId: camera.id, values: { projection: camera.projection, lensMm: camera.lensMm, near: camera.near, far: camera.far } }, saveReopen: "EXACT", nextTask: "M16-GAP-00005" };
|
|
const cameraReportPath = path.join(root, "tests/golden/M16-GAP-00004/camera-local-exact-report.json");
|
|
fs.mkdirSync(path.dirname(cameraReportPath), { recursive: true });
|
|
fs.writeFileSync(cameraReportPath, `${JSON.stringify(cameraReport, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} camera=${camera.id} lensMm=${camera.lensMm} desktop=exact saveReopen=exact next=${cameraReport.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00004") {
|
|
const cameraFixture = path.join(root, "tests/files/web/generated/M16-GAP-00004-datablock-Camera.blend"); const cameraDesktopPath = path.join(root, "tests/golden/M16-GAP-00004/camera-desktop-report.json"); fs.mkdirSync(path.dirname(cameraDesktopPath), { recursive: true }); const cameraDesktop = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-camera-desktop.py"), "--", cameraFixture, cameraDesktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 }); assert.equal(cameraDesktop.status, 0, `${cameraDesktop.stdout ?? ""}\n${cameraDesktop.stderr ?? ""}`); const desktopEvidence = JSON.parse(fs.readFileSync(cameraDesktopPath, "utf8")); const cameraBytes = fs.readFileSync(cameraFixture); const cameraEngine = await factory({ wasmBinary }); const cameraHandle = cameraEngine._web_engine_create(); open(cameraEngine, cameraHandle, cameraBytes); const cameraSnapshot = snapshot(cameraEngine, cameraHandle); const camera = cameraSnapshot.cameras?.find((value) => value.id === "camera:WebGapCamera"); assert.ok(camera); assert.equal(camera.type, "PERSP"); assert.ok(Math.abs(camera.lens - 52) < 1e-5); assert.ok(Math.abs(camera.clipStart - 0.1) < 1e-5); assert.ok(Math.abs(camera.clipEnd - 2500) < 1e-5); const cameraSaved = output(cameraEngine, cameraHandle, cameraEngine._web_engine_save_blend, true); const cameraReopened = cameraEngine._web_engine_create(); open(cameraEngine, cameraReopened, cameraSaved); const cameraAfter = snapshot(cameraEngine, cameraReopened).cameras?.find((value) => value.id === camera.id); assert.deepEqual(cameraAfter, camera); const cameraReport = { schemaVersion: 1, task: "M16-GAP-00004", operation: "CAMERA_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, cameraFixture).replaceAll(path.sep, "/"), sha256: desktopEvidence.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, cameraDesktopPath).replaceAll(path.sep, "/") }, wasm: { status: "EXACT", cameraId: camera.id, values: { type: camera.type, lens: camera.lens, clipStart: camera.clipStart, clipEnd: camera.clipEnd } }, saveReopen: "EXACT", nextTask: "M16-GAP-00005" }; const cameraReportPath = path.join(root, "tests/golden/M16-GAP-00004/camera-local-exact-report.json"); fs.mkdirSync(path.dirname(cameraReportPath), { recursive: true }); fs.writeFileSync(cameraReportPath, `${JSON.stringify(cameraReport, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} camera=${camera.id} lens=${camera.lens} desktop=exact saveReopen=exact next=${cameraReport.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00005" && !process.env.USE_LEGACY_COLLECTION_BRANCH) {
|
|
const collectionFixture = path.join(root, "tests/files/web/generated/M16-GAP-00005-datablock-Collection.blend");
|
|
const collectionDesktopPath = path.join(root, "tests/golden/M16-GAP-00005/collection-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(collectionDesktopPath), { recursive: true });
|
|
const desktopRun = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-collection-desktop.py"), "--", collectionFixture, collectionDesktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(desktopRun.status, 0, `${desktopRun.stdout ?? ""}\n${desktopRun.stderr ?? ""}`);
|
|
const desktopEvidence = JSON.parse(fs.readFileSync(collectionDesktopPath, "utf8"));
|
|
const collectionEngine = await factory({ wasmBinary });
|
|
const collectionHandle = collectionEngine._web_engine_create();
|
|
open(collectionEngine, collectionHandle, fs.readFileSync(collectionFixture));
|
|
const collections = snapshot(collectionEngine, collectionHandle).collections?.filter((value) => value.id.includes("WebGap"));
|
|
assert.ok(collections?.length >= 2);
|
|
const parent = collections.find((value) => value.id === "collection:WebGapCollection");
|
|
const child = collections.find((value) => value.id === "collection:WebGapChild");
|
|
assert.ok(parent && child);
|
|
assert.ok(parent.childCollectionIds?.includes(child.id));
|
|
assert.ok(child.objectIds?.includes("object:WebGapCollectionObject"));
|
|
const saved = output(collectionEngine, collectionHandle, collectionEngine._web_engine_save_blend, true);
|
|
const reopened = collectionEngine._web_engine_create();
|
|
open(collectionEngine, reopened, saved);
|
|
const after = snapshot(collectionEngine, reopened).collections?.filter((value) => value.id.includes("WebGap"));
|
|
assert.deepEqual(after, collections);
|
|
const report = { schemaVersion: 1, task: "M16-GAP-00005", operation: "COLLECTION_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, collectionFixture).replaceAll(path.sep, "/"), sha256: desktopEvidence.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, collectionDesktopPath).replaceAll(path.sep, "/") }, wasm: { status: "EXACT", collections: collections.map((value) => ({ id: value.id, childCollectionIds: value.childCollectionIds, objectIds: value.objectIds })) }, saveReopen: "EXACT", nextTask: "M16-GAP-00006" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00005/collection-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} collections=${collections.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00005") {
|
|
const collectionFixture = path.join(root, "tests/files/web/generated/M16-GAP-00005-datablock-Collection.blend"); const collectionDesktopPath = path.join(root, "tests/golden/M16-GAP-00005/collection-desktop-report.json"); fs.mkdirSync(path.dirname(collectionDesktopPath), { recursive: true }); const collectionDesktop = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-collection-desktop.py"), "--", collectionFixture, collectionDesktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 }); assert.equal(collectionDesktop.status, 0, `${collectionDesktop.stdout ?? ""}\n${collectionDesktop.stderr ?? ""}`); const desktopEvidence = JSON.parse(fs.readFileSync(collectionDesktopPath, "utf8")); const collectionBytes = fs.readFileSync(collectionFixture); const collectionEngine = await factory({ wasmBinary }); const collectionHandle = collectionEngine._web_engine_create(); open(collectionEngine, collectionHandle, collectionBytes); const collectionSnapshot = snapshot(collectionEngine, collectionHandle); const collections = collectionSnapshot.collections?.filter((value) => value.id.includes("WebGap")); assert.ok(collections?.length >= 2); const parent = collections.find((value) => value.id === "collection:WebGapCollection"); const child = collections.find((value) => value.id === "collection:WebGapChild"); assert.ok(parent && child); assert.ok(parent.children?.includes(child.id)); assert.ok(child.objectIds?.includes("object:WebGapCollectionObject")); const saved = output(collectionEngine, collectionHandle, collectionEngine._web_engine_save_blend, true); const reopened = collectionEngine._web_engine_create(); open(collectionEngine, reopened, saved); const after = snapshot(collectionEngine, reopened).collections?.filter((value) => value.id.includes("WebGap")); assert.deepEqual(after, collections); const report = { schemaVersion: 1, task: "M16-GAP-00005", operation: "COLLECTION_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, collectionFixture).replaceAll(path.sep, "/"), sha256: desktopEvidence.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, collectionDesktopPath).replaceAll(path.sep, "/") }, wasm: { status: "EXACT", collections: collections.map((value) => ({ id: value.id, children: value.children, objectIds: value.objectIds })) }, saveReopen: "EXACT", nextTask: "M16-GAP-00006" }; const reportPath = path.join(root, "tests/golden/M16-GAP-00005/collection-local-exact-report.json"); fs.mkdirSync(path.dirname(reportPath), { recursive: true }); fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} collections=${collections.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00006") {
|
|
const curveFixture = path.join(root, "tests/files/web/generated/M16-GAP-00006-datablock-Curve.blend"); const curveDesktopPath = path.join(root, "tests/golden/M16-GAP-00006/curve-desktop-report.json"); fs.mkdirSync(path.dirname(curveDesktopPath), { recursive: true }); const curveDesktop = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-curve-desktop.py"), "--", curveFixture, curveDesktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 }); assert.equal(curveDesktop.status, 0, `${curveDesktop.stdout ?? ""}\n${curveDesktop.stderr ?? ""}`); const desktopEvidence = JSON.parse(fs.readFileSync(curveDesktopPath, "utf8")); const curveEngine = await factory({ wasmBinary }); const curveHandle = curveEngine._web_engine_create(); open(curveEngine, curveHandle, fs.readFileSync(curveFixture)); const nonMesh = snapshot(curveEngine, curveHandle).nonMeshData?.find((value) => value.id === "curve:WebGapCurve"); assert.ok(nonMesh); assert.equal(nonMesh.geometryStatus, "available"); assert.equal(nonMesh.splineCount, 1); assert.equal(nonMesh.pointCount, 3); assert.deepEqual(nonMesh.splineTypes, ["BEZIER"]); assert.deepEqual(nonMesh.splineOffsets, [0, 3]); assert.equal(nonMesh.controlPoints.length, 9); const saved = output(curveEngine, curveHandle, curveEngine._web_engine_save_blend, true); const reopened = curveEngine._web_engine_create(); open(curveEngine, reopened, saved); const after = snapshot(curveEngine, reopened).nonMeshData?.find((value) => value.id === nonMesh.id); assert.deepEqual(after, nonMesh); const report = { schemaVersion: 1, task: "M16-GAP-00006", operation: "CURVE_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, curveFixture).replaceAll(path.sep, "/"), sha256: desktopEvidence.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, curveDesktopPath).replaceAll(path.sep, "/") }, wasm: { status: "EXACT", curveId: nonMesh.id, splineCount: nonMesh.splineCount, pointCount: nonMesh.pointCount, splineTypes: nonMesh.splineTypes }, saveReopen: "EXACT", nextTask: "M16-GAP-00007" }; const reportPath = path.join(root, "tests/golden/M16-GAP-00006/curve-local-exact-report.json"); fs.mkdirSync(path.dirname(reportPath), { recursive: true }); fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} curve=${nonMesh.id} splines=${nonMesh.splineCount} points=${nonMesh.pointCount} desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00007") {
|
|
const curvesFixture = path.join(root, "tests/files/web/generated/M16-GAP-00007-datablock-Curves.blend"); const curvesDesktopPath = path.join(root, "tests/golden/M16-GAP-00007/curves-desktop-report.json"); fs.mkdirSync(path.dirname(curvesDesktopPath), { recursive: true }); const desktopRun = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-curves-desktop.py"), "--", curvesFixture, curvesDesktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 }); assert.equal(desktopRun.status, 0, `${desktopRun.stdout ?? ""}\n${desktopRun.stderr ?? ""}`); const desktopEvidence = JSON.parse(fs.readFileSync(curvesDesktopPath, "utf8")); const curvesEngine = await factory({ wasmBinary }); const curvesHandle = curvesEngine._web_engine_create(); open(curvesEngine, curvesHandle, fs.readFileSync(curvesFixture)); const curvesData = snapshot(curvesEngine, curvesHandle).nonMeshData?.find((value) => value.id === "curves:WebGapCurves"); assert.ok(curvesData); assert.equal(curvesData.geometryStatus, "available"); assert.ok(curvesData.pointCount >= 4); assert.ok(curvesData.splineCount >= 1); assert.ok(Array.isArray(curvesData.splineOffsets)); const saved = output(curvesEngine, curvesHandle, curvesEngine._web_engine_save_blend, true); const reopened = curvesEngine._web_engine_create(); open(curvesEngine, reopened, saved); const after = snapshot(curvesEngine, reopened).nonMeshData?.find((value) => value.id === curvesData.id); assert.deepEqual(after, curvesData); const report = { schemaVersion: 1, task: "M16-GAP-00007", operation: "CURVES_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, curvesFixture).replaceAll(path.sep, "/"), sha256: desktopEvidence.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, curvesDesktopPath).replaceAll(path.sep, "/") }, wasm: { status: "EXACT", curvesId: curvesData.id, pointCount: curvesData.pointCount, splineCount: curvesData.splineCount }, saveReopen: "EXACT", nextTask: "M16-GAP-00008" }; const reportPath = path.join(root, "tests/golden/M16-GAP-00007/curves-local-exact-report.json"); fs.mkdirSync(path.dirname(reportPath), { recursive: true }); fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} curves=${curvesData.id} points=${curvesData.pointCount} desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00008") {
|
|
const styleFixture = path.join(root, "tests/files/web/generated/M16-GAP-00008-datablock-FreestyleLineStyle.blend"); const styleDesktopPath = path.join(root, "tests/golden/M16-GAP-00008/freestyle-desktop-report.json"); fs.mkdirSync(path.dirname(styleDesktopPath), { recursive: true }); const desktopRun = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-freestyle-desktop.py"), "--", styleFixture, styleDesktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 }); assert.equal(desktopRun.status, 0, `${desktopRun.stdout ?? ""}\n${desktopRun.stderr ?? ""}`); const desktopEvidence = JSON.parse(fs.readFileSync(styleDesktopPath, "utf8")); const styleEngine = await factory({ wasmBinary }); const styleHandle = styleEngine._web_engine_create(); open(styleEngine, styleHandle, fs.readFileSync(styleFixture)); const lineStyle = snapshot(styleEngine, styleHandle).lineStyles?.find((value) => value.id === "line-style:WebGapLineStyle"); assert.ok(lineStyle); assert.ok(Math.abs(lineStyle.alpha - 0.75) < 1e-5); assert.ok(Math.abs(lineStyle.thickness - 2.5) < 1e-5); assert.deepEqual(lineStyle.color.map((value) => Number(value.toFixed(3))), [0.2, 0.4, 0.8]); assert.equal(lineStyle.dashed, true); const saved = output(styleEngine, styleHandle, styleEngine._web_engine_save_blend, true); const reopened = styleEngine._web_engine_create(); open(styleEngine, reopened, saved); const after = snapshot(styleEngine, reopened).lineStyles?.find((value) => value.id === lineStyle.id); assert.deepEqual(after, lineStyle); const report = { schemaVersion: 1, task: "M16-GAP-00008", operation: "FREESTYLE_LINE_STYLE_LOCAL_EXACT", fixture: { path: path.relative(root, styleFixture).replaceAll(path.sep, "/"), sha256: desktopEvidence.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, styleDesktopPath).replaceAll(path.sep, "/") }, wasm: { status: "EXACT", lineStyleId: lineStyle.id, thickness: lineStyle.thickness, modifierCounts: lineStyle.modifierCounts }, saveReopen: "EXACT", nextTask: "M16-GAP-00009" }; const reportPath = path.join(root, "tests/golden/M16-GAP-00008/freestyle-local-exact-report.json"); fs.mkdirSync(path.dirname(reportPath), { recursive: true }); fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} lineStyle=${lineStyle.id} thickness=${lineStyle.thickness} desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00009") {
|
|
const gpFixture = path.join(root, "tests/files/web/generated/M16-GAP-00009-datablock-GreasePencil.blend"); const gpDesktopPath = path.join(root, "tests/golden/M16-GAP-00009/grease-pencil-desktop-report.json"); fs.mkdirSync(path.dirname(gpDesktopPath), { recursive: true }); const desktopRun = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-grease-pencil-desktop.py"), "--", gpFixture, gpDesktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 }); assert.equal(desktopRun.status, 0, `${desktopRun.stdout ?? ""}\n${desktopRun.stderr ?? ""}`); const desktopEvidence = JSON.parse(fs.readFileSync(gpDesktopPath, "utf8")); const gpEngine = await factory({ wasmBinary }); const gpHandle = gpEngine._web_engine_create(); open(gpEngine, gpHandle, fs.readFileSync(gpFixture)); const gpSnapshot = snapshot(gpEngine, gpHandle); const gp = gpSnapshot.greasePencils?.find((value) => value.name === "GreasePencilData"); assert.ok(gp); assert.ok(gp.layers?.length >= 1); assert.ok(gp.layers[0].frames?.length >= 1); const saved = output(gpEngine, gpHandle, gpEngine._web_engine_save_blend, true); const reopened = gpEngine._web_engine_create(); open(gpEngine, reopened, saved); const after = snapshot(gpEngine, reopened).greasePencils?.find((value) => value.name === gp.name); assert.deepEqual(after, gp); const report = { schemaVersion: 1, task: "M16-GAP-00009", operation: "GREASE_PENCIL_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, gpFixture).replaceAll(path.sep, "/"), sha256: desktopEvidence.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, gpDesktopPath).replaceAll(path.sep, "/") }, wasm: { status: "EXACT", dataName: gp.name, layerCount: gp.layers.length, frameCount: gp.layers.reduce((sum, layer) => sum + layer.frames.length, 0) }, saveReopen: "EXACT", nextTask: "M16-GAP-00010" }; const reportPath = path.join(root, "tests/golden/M16-GAP-00009/grease-pencil-local-exact-report.json"); fs.mkdirSync(path.dirname(reportPath), { recursive: true }); fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} greasePencil=${gp.name} layers=${gp.layers.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00010") {
|
|
const latticeFixture = path.join(root, "tests/files/web/generated/M16-GAP-00010-datablock-Lattice.blend");
|
|
const latticeDesktopPath = path.join(root, "tests/golden/M16-GAP-00010/lattice-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(latticeDesktopPath), { recursive: true });
|
|
const desktopRun = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-lattice-desktop.py"), "--", latticeFixture, latticeDesktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(desktopRun.status, 0, `${desktopRun.stdout ?? ""}\n${desktopRun.stderr ?? ""}`);
|
|
const desktopEvidence = JSON.parse(fs.readFileSync(latticeDesktopPath, "utf8"));
|
|
const latticeEngine = await factory({ wasmBinary });
|
|
const latticeHandle = latticeEngine._web_engine_create();
|
|
open(latticeEngine, latticeHandle, fs.readFileSync(latticeFixture));
|
|
const lattice = snapshot(latticeEngine, latticeHandle).lattices?.find((value) => value.id === "lattice:WebGapLattice");
|
|
assert.ok(lattice);
|
|
assert.equal(lattice.status, "AVAILABLE");
|
|
assert.deepEqual(lattice.dimensions, desktopEvidence.lattice.dimensions);
|
|
assert.equal(lattice.pointCount, desktopEvidence.lattice.pointCount);
|
|
assert.deepEqual(lattice.interpolation, ["KEY_LINEAR", "KEY_CARDINAL", "KEY_BSPLINE"]);
|
|
assert.equal(lattice.useOutside, true);
|
|
assert.equal(lattice.vertexGroup, "WebGapInfluence");
|
|
assert.deepEqual(lattice.points, desktopEvidence.lattice.points);
|
|
const saved = output(latticeEngine, latticeHandle, latticeEngine._web_engine_save_blend, true);
|
|
const reopened = latticeEngine._web_engine_create();
|
|
open(latticeEngine, reopened, saved);
|
|
const after = snapshot(latticeEngine, reopened).lattices?.find((value) => value.id === lattice.id);
|
|
assert.deepEqual(after, lattice);
|
|
const report = { schemaVersion: 1, task: "M16-GAP-00010", operation: "LATTICE_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, latticeFixture).replaceAll(path.sep, "/"), sha256: desktopEvidence.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, latticeDesktopPath).replaceAll(path.sep, "/"), saveReopen: desktopEvidence.saveReopen }, wasm: { status: "EXACT", latticeId: lattice.id, dimensions: lattice.dimensions, pointCount: lattice.pointCount, interpolation: lattice.interpolation, pointHash: crypto.createHash("sha256").update(JSON.stringify(lattice.points)).digest("hex") }, saveReopen: "EXACT", nextTask: "M16-GAP-00011" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00010/lattice-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} lattice=${lattice.id} points=${lattice.pointCount} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00011") {
|
|
const libraryFixture = path.join(root, "tests/files/web/generated/M16-GAP-00011-datablock-Library.blend");
|
|
const libraryDesktopPath = path.join(root, "tests/golden/M16-GAP-00011/library-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(libraryDesktopPath), { recursive: true });
|
|
const desktopRun = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-library-desktop.py"), "--", libraryFixture, libraryDesktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(desktopRun.status, 0, `${desktopRun.stdout ?? ""}\n${desktopRun.stderr ?? ""}`);
|
|
const desktopEvidence = JSON.parse(fs.readFileSync(libraryDesktopPath, "utf8"));
|
|
assert.equal(desktopEvidence.saveReopen, "EXACT");
|
|
const libraryEngine = await factory({ wasmBinary });
|
|
const libraryHandle = libraryEngine._web_engine_create();
|
|
open(libraryEngine, libraryHandle, fs.readFileSync(libraryFixture));
|
|
const librarySnapshot = snapshot(libraryEngine, libraryHandle);
|
|
assert.equal(librarySnapshot.libraryStatus, "AVAILABLE");
|
|
assert.ok(Array.isArray(librarySnapshot.libraries));
|
|
assert.equal(librarySnapshot.libraries.length, 1);
|
|
const library = librarySnapshot.libraries[0];
|
|
assert.equal(library.id, desktopEvidence.library.id);
|
|
assert.equal(library.name, desktopEvidence.library.name);
|
|
assert.equal(library.sourcePath, "//WebGapLibrary.blend");
|
|
assert.equal(library.packed, false);
|
|
assert.equal(library.status, "EXTERNAL_REQUIRED");
|
|
assert.equal(library.errorCode, "LINKED_LIBRARY_RESOURCE_REQUIRED");
|
|
assert.deepEqual(library.dependencyIds, []);
|
|
assert.equal(library.readOnly, true);
|
|
const saved = output(libraryEngine, libraryHandle, libraryEngine._web_engine_save_blend, true);
|
|
assert.ok(saved.byteLength > 0);
|
|
const reopened = libraryEngine._web_engine_create();
|
|
open(libraryEngine, reopened, saved);
|
|
const after = snapshot(libraryEngine, reopened);
|
|
assert.deepEqual(after.libraries, librarySnapshot.libraries);
|
|
assert.equal(after.libraryStatus, librarySnapshot.libraryStatus);
|
|
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]);
|
|
const malformedPointer = libraryEngine._malloc(malformed.byteLength);
|
|
let malformedResult;
|
|
try {
|
|
libraryEngine.HEAPU8.set(malformed, malformedPointer);
|
|
malformedResult = libraryEngine._web_engine_open_blend(libraryHandle, malformedPointer, malformed.byteLength);
|
|
}
|
|
finally {
|
|
libraryEngine._free(malformedPointer);
|
|
}
|
|
assert.notEqual(malformedResult, 0);
|
|
assert.deepEqual(snapshot(libraryEngine, libraryHandle), librarySnapshot);
|
|
libraryEngine._web_engine_destroy(libraryHandle);
|
|
libraryEngine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task: "M16-GAP-00011", operation: "LIBRARY_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, libraryFixture).replaceAll(path.sep, "/"), sha256: desktopEvidence.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, libraryDesktopPath).replaceAll(path.sep, "/"), saveReopen: desktopEvidence.saveReopen }, wasm: { status: "EXACT", libraryId: library.id, sourcePath: library.sourcePath, libraryStatus: library.status, dependencyIds: library.dependencyIds }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00012" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00011/library-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} library=${library.id} status=${library.status} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00012") {
|
|
const lightFixture = path.join(root, "tests/files/web/generated/M16-GAP-00012-datablock-Light.blend");
|
|
const lightDesktopPath = path.join(root, "tests/golden/M16-GAP-00012/light-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(lightDesktopPath), { recursive: true });
|
|
const desktopRun = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-light-desktop.py"), "--", lightFixture, lightDesktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(desktopRun.status, 0, `${desktopRun.stdout ?? ""}\n${desktopRun.stderr ?? ""}`);
|
|
const desktopEvidence = JSON.parse(fs.readFileSync(lightDesktopPath, "utf8"));
|
|
assert.equal(desktopEvidence.saveReopen, "EXACT");
|
|
const lightEngine = await factory({ wasmBinary });
|
|
const lightHandle = lightEngine._web_engine_create();
|
|
open(lightEngine, lightHandle, fs.readFileSync(lightFixture));
|
|
const lightSnapshot = snapshot(lightEngine, lightHandle);
|
|
const light = lightSnapshot.lights?.find((value) => value.id === "light:WebGapLight");
|
|
assert.ok(light);
|
|
const normalizedLight = { ...light };
|
|
for (const field of ["energy", "exposure", "temperature", "radius", "spotAngle", "spotBlend", "areaSize", "areaSizeY", "areaSpread", "sunAngle"]) normalizedLight[field] = Number(normalizedLight[field].toFixed(6));
|
|
normalizedLight.color = normalizedLight.color.map((value) => Number(value.toFixed(6)));
|
|
assert.deepEqual(normalizedLight, desktopEvidence.light);
|
|
assert.equal(light.lightType, 4);
|
|
assert.deepEqual(light.color, [0.25, 0.5, 0.75]);
|
|
const closeLight = (actual, expected, label) => assert.ok(Math.abs(actual - expected) <= 1e-5, `${label}: ${actual} != ${expected}`);
|
|
closeLight(light.energy, 400, "energy");
|
|
closeLight(light.exposure, 1, "exposure");
|
|
closeLight(light.temperature, 5000, "temperature");
|
|
assert.equal(light.useTemperature, true);
|
|
assert.equal(light.castsShadow, false);
|
|
closeLight(light.radius, 0.3, "radius");
|
|
assert.equal(light.areaShape, 1);
|
|
closeLight(light.areaSize, 3, "areaSize");
|
|
closeLight(light.areaSizeY, 2, "areaSizeY");
|
|
closeLight(light.areaSpread, 2.4, "areaSpread");
|
|
closeLight(light.sunAngle, 0.00918, "sunAngle");
|
|
const saved = output(lightEngine, lightHandle, lightEngine._web_engine_save_blend, true);
|
|
const reopened = lightEngine._web_engine_create();
|
|
open(lightEngine, reopened, saved);
|
|
const after = snapshot(lightEngine, reopened);
|
|
assert.deepEqual(after.lights, lightSnapshot.lights);
|
|
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]);
|
|
const malformedPointer = lightEngine._malloc(malformed.byteLength);
|
|
let malformedResult;
|
|
try {
|
|
lightEngine.HEAPU8.set(malformed, malformedPointer);
|
|
malformedResult = lightEngine._web_engine_open_blend(lightHandle, malformedPointer, malformed.byteLength);
|
|
}
|
|
finally {
|
|
lightEngine._free(malformedPointer);
|
|
}
|
|
assert.notEqual(malformedResult, 0);
|
|
assert.deepEqual(snapshot(lightEngine, lightHandle), lightSnapshot);
|
|
lightEngine._web_engine_destroy(lightHandle);
|
|
lightEngine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task: "M16-GAP-00012", operation: "LIGHT_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, lightFixture).replaceAll(path.sep, "/"), sha256: desktopEvidence.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, lightDesktopPath).replaceAll(path.sep, "/"), saveReopen: desktopEvidence.saveReopen }, wasm: { status: "EXACT", lightId: light.id, lightType: light.lightType, fields: { energy: light.energy, exposure: light.exposure, temperature: light.temperature, useTemperature: light.useTemperature, castsShadow: light.castsShadow, areaSize: light.areaSize, areaSizeY: light.areaSizeY } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00013" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00012/light-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} light=${light.id} type=${light.lightType} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00013") {
|
|
const materialFixture = path.join(root, "tests/files/web/generated/M16-GAP-00013-datablock-Material.blend");
|
|
const materialDesktopPath = path.join(root, "tests/golden/M16-GAP-00013/material-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(materialDesktopPath), { recursive: true });
|
|
const desktopRun = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-material-desktop.py"), "--", materialFixture, materialDesktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(desktopRun.status, 0, `${desktopRun.stdout ?? ""}\n${desktopRun.stderr ?? ""}`);
|
|
const desktopEvidence = JSON.parse(fs.readFileSync(materialDesktopPath, "utf8"));
|
|
assert.equal(desktopEvidence.saveReopen, "EXACT");
|
|
const materialEngine = await factory({ wasmBinary });
|
|
const materialHandle = materialEngine._web_engine_create();
|
|
open(materialEngine, materialHandle, fs.readFileSync(materialFixture));
|
|
const materialSnapshot = snapshot(materialEngine, materialHandle);
|
|
const material = materialSnapshot.materials?.find((value) => value.id === "material:WebGapMaterial");
|
|
assert.ok(material);
|
|
const closeMaterial = (actual, expected, label) => assert.ok(Math.abs(actual - expected) <= 1e-5, `${label}: ${actual} != ${expected}`);
|
|
const closeArray = (actual, expected, label) => expected.forEach((value, index) => closeMaterial(actual[index], value, `${label}[${index}]`));
|
|
closeArray(material.baseColor, [0.2, 0.4, 0.8, 0.9], "baseColor");
|
|
closeMaterial(material.roughness, 0.6, "roughness");
|
|
closeMaterial(material.metallic, 0.35, "metallic");
|
|
closeArray(material.emissionColor, [0.05, 0.1, 0.2, 1], "emissionColor");
|
|
closeMaterial(material.alpha, 0.9, "alpha");
|
|
closeMaterial(material.ior, 1.6, "ior");
|
|
closeMaterial(material.specularIORLevel, 0.35, "specularIORLevel");
|
|
closeMaterial(material.transmissionWeight, 0.27, "transmissionWeight");
|
|
closeMaterial(material.coatWeight, 0.64, "coatWeight");
|
|
closeMaterial(material.coatRoughness, 0.12, "coatRoughness");
|
|
closeMaterial(material.emissionStrength, 3.5, "emissionStrength");
|
|
assert.ok(material.nodes?.some((node) => node.type === "PRINCIPLED"));
|
|
assert.ok(material.nodes?.some((node) => node.type === "OUTPUT"));
|
|
assert.equal(material.links?.length, 1);
|
|
assert.match(material.shaderGraphHash, /^[0-9a-f]{64}$/);
|
|
const saved = output(materialEngine, materialHandle, materialEngine._web_engine_save_blend, true);
|
|
const reopened = materialEngine._web_engine_create();
|
|
open(materialEngine, reopened, saved);
|
|
const after = snapshot(materialEngine, reopened);
|
|
assert.deepEqual(after.materials, materialSnapshot.materials);
|
|
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]);
|
|
const malformedPointer = materialEngine._malloc(malformed.byteLength);
|
|
let malformedResult;
|
|
try {
|
|
materialEngine.HEAPU8.set(malformed, malformedPointer);
|
|
malformedResult = materialEngine._web_engine_open_blend(materialHandle, malformedPointer, malformed.byteLength);
|
|
}
|
|
finally {
|
|
materialEngine._free(malformedPointer);
|
|
}
|
|
assert.notEqual(malformedResult, 0);
|
|
assert.deepEqual(snapshot(materialEngine, materialHandle), materialSnapshot);
|
|
materialEngine._web_engine_destroy(materialHandle);
|
|
materialEngine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task: "M16-GAP-00013", operation: "MATERIAL_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, materialFixture).replaceAll(path.sep, "/"), sha256: desktopEvidence.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, materialDesktopPath).replaceAll(path.sep, "/"), saveReopen: desktopEvidence.saveReopen }, wasm: { status: "EXACT", materialId: material.id, shaderGraphHash: material.shaderGraphHash, nodeCount: material.nodes.length, linkCount: material.links.length }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00014" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00013/material-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} material=${material.id} nodes=${material.nodes.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00014") {
|
|
const meshFixture = path.join(root, "tests/files/web/generated/M16-GAP-00014-datablock-Mesh.blend");
|
|
const meshDesktopPath = path.join(root, "tests/golden/M16-GAP-00014/mesh-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(meshDesktopPath), { recursive: true });
|
|
const desktopRun = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-mesh-desktop.py"), "--", meshFixture, meshDesktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(desktopRun.status, 0, `${desktopRun.stdout ?? ""}\n${desktopRun.stderr ?? ""}`);
|
|
const desktopEvidence = JSON.parse(fs.readFileSync(meshDesktopPath, "utf8"));
|
|
assert.equal(desktopEvidence.saveReopen, "EXACT");
|
|
const meshEngine = await factory({ wasmBinary });
|
|
const meshHandle = meshEngine._web_engine_create();
|
|
open(meshEngine, meshHandle, fs.readFileSync(meshFixture));
|
|
const meshSnapshot = snapshot(meshEngine, meshHandle);
|
|
const mesh = meshSnapshot.meshes?.find((value) => value.id === "mesh:WebGapMesh");
|
|
assert.ok(mesh);
|
|
assert.equal(mesh.geometryStatus, "available");
|
|
assert.equal(mesh.vertexCount, desktopEvidence.mesh.vertexCount);
|
|
assert.equal(mesh.edgeCount, desktopEvidence.mesh.edgeCount);
|
|
assert.equal(mesh.faceCount, desktopEvidence.mesh.faceCount);
|
|
assert.equal(mesh.cornerCount, desktopEvidence.mesh.cornerCount);
|
|
assert.equal(mesh.triangleCount, desktopEvidence.mesh.triangleCount);
|
|
assert.deepEqual(mesh.positions.map((value) => Number(value.toFixed(6))), desktopEvidence.mesh.positions);
|
|
assert.deepEqual(mesh.indices, desktopEvidence.mesh.indices);
|
|
assert.deepEqual(mesh.uvLayers?.map((layer) => layer.name), ["WebGapUV"]);
|
|
const saved = output(meshEngine, meshHandle, meshEngine._web_engine_save_blend, true);
|
|
const reopened = meshEngine._web_engine_create();
|
|
open(meshEngine, reopened, saved);
|
|
const after = snapshot(meshEngine, reopened).meshes?.find((value) => value.id === mesh.id);
|
|
assert.deepEqual(after, mesh);
|
|
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]);
|
|
const malformedPointer = meshEngine._malloc(malformed.byteLength);
|
|
let malformedResult;
|
|
try {
|
|
meshEngine.HEAPU8.set(malformed, malformedPointer);
|
|
malformedResult = meshEngine._web_engine_open_blend(meshHandle, malformedPointer, malformed.byteLength);
|
|
}
|
|
finally {
|
|
meshEngine._free(malformedPointer);
|
|
}
|
|
assert.notEqual(malformedResult, 0);
|
|
assert.deepEqual(snapshot(meshEngine, meshHandle), meshSnapshot);
|
|
meshEngine._web_engine_destroy(meshHandle);
|
|
meshEngine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task: "M16-GAP-00014", operation: "MESH_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, meshFixture).replaceAll(path.sep, "/"), sha256: desktopEvidence.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, meshDesktopPath).replaceAll(path.sep, "/"), saveReopen: desktopEvidence.saveReopen }, wasm: { status: "EXACT", meshId: mesh.id, vertexCount: mesh.vertexCount, triangleCount: mesh.triangleCount, uvLayers: mesh.uvLayers?.map((layer) => layer.name) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00015" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00014/mesh-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} mesh=${mesh.id} vertices=${mesh.vertexCount} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00015") {
|
|
const metaballFixture = path.join(root, "tests/files/web/generated/M16-GAP-00015-datablock-MetaBall.blend");
|
|
const metaballDesktopPath = path.join(root, "tests/golden/M16-GAP-00015/metaball-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(metaballDesktopPath), { recursive: true });
|
|
const desktopRun = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-metaball-desktop.py"), "--", metaballFixture, metaballDesktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(desktopRun.status, 0, `${desktopRun.stdout ?? ""}\n${desktopRun.stderr ?? ""}`);
|
|
const desktopEvidence = JSON.parse(fs.readFileSync(metaballDesktopPath, "utf8"));
|
|
const metaballEngine = await factory({ wasmBinary });
|
|
const metaballHandle = metaballEngine._web_engine_create();
|
|
open(metaballEngine, metaballHandle, fs.readFileSync(metaballFixture));
|
|
const metaballSnapshot = snapshot(metaballEngine, metaballHandle);
|
|
const metaball = metaballSnapshot.nonMeshData?.find((value) => value.id === "metaball:WebGapMetaBall");
|
|
assert.ok(metaball);
|
|
assert.equal(metaball.geometryStatus, "available");
|
|
assert.equal(metaball.pointCount, 2);
|
|
assert.deepEqual(metaball.elements.map((item) => item.position.map((value) => Number(value.toFixed(6)))), desktopEvidence.metaball.elements.map((item) => item.position));
|
|
metaball.elements.forEach((item, index) => { assert.ok(Math.abs(item.radius - desktopEvidence.metaball.elements[index].radius) <= 1e-5); item.scale.forEach((value, axis) => assert.ok(Math.abs(value - desktopEvidence.metaball.elements[index].scale[axis]) <= 1e-5)); });
|
|
const saved = output(metaballEngine, metaballHandle, metaballEngine._web_engine_save_blend, true);
|
|
const reopened = metaballEngine._web_engine_create();
|
|
open(metaballEngine, reopened, saved);
|
|
assert.deepEqual(snapshot(metaballEngine, reopened).nonMeshData?.find((value) => value.id === metaball.id), metaball);
|
|
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]);
|
|
const malformedPointer = metaballEngine._malloc(malformed.byteLength); let malformedResult;
|
|
try { metaballEngine.HEAPU8.set(malformed, malformedPointer); malformedResult = metaballEngine._web_engine_open_blend(metaballHandle, malformedPointer, malformed.byteLength); } finally { metaballEngine._free(malformedPointer); }
|
|
assert.notEqual(malformedResult, 0); assert.deepEqual(snapshot(metaballEngine, metaballHandle), metaballSnapshot);
|
|
metaballEngine._web_engine_destroy(metaballHandle); metaballEngine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task: "M16-GAP-00015", operation: "METABALL_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, metaballFixture).replaceAll(path.sep, "/"), sha256: desktopEvidence.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, metaballDesktopPath).replaceAll(path.sep, "/"), saveReopen: desktopEvidence.saveReopen }, wasm: { status: "EXACT", metaballId: metaball.id, pointCount: metaball.pointCount, elementHash: crypto.createHash("sha256").update(JSON.stringify(metaball.elements)).digest("hex") }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00016" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00015/metaball-local-exact-report.json"); fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} metaball=${metaball.id} points=${metaball.pointCount} desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00016") {
|
|
const nodeTreeFixture = path.join(root, "tests/files/web/generated/M16-GAP-00016-datablock-NodeTree.blend");
|
|
const nodeTreeDesktopPath = path.join(root, "tests/golden/M16-GAP-00016/nodetree-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(nodeTreeDesktopPath), { recursive: true });
|
|
const desktopRun = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-nodetree-desktop.py"), "--", nodeTreeFixture, nodeTreeDesktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(desktopRun.status, 0, `${desktopRun.stdout ?? ""}\n${desktopRun.stderr ?? ""}`);
|
|
const desktopEvidence = JSON.parse(fs.readFileSync(nodeTreeDesktopPath, "utf8"));
|
|
const nodeTreeEngine = await factory({ wasmBinary });
|
|
const nodeTreeHandle = nodeTreeEngine._web_engine_create();
|
|
open(nodeTreeEngine, nodeTreeHandle, fs.readFileSync(nodeTreeFixture));
|
|
const nodeTreeSnapshot = snapshot(nodeTreeEngine, nodeTreeHandle);
|
|
const material = nodeTreeSnapshot.materials?.find((value) => value.id === "material:WebGapNodeTreeMaterial");
|
|
assert.ok(material);
|
|
assert.equal(material.nodes?.length, desktopEvidence.nodeTree.nodeTypes.length);
|
|
assert.equal(material.links?.length, desktopEvidence.nodeTree.linkCount);
|
|
assert.ok(material.nodes?.some((node) => node.type === "RGB"));
|
|
assert.ok(material.nodes?.some((node) => node.type === "PRINCIPLED"));
|
|
assert.ok(material.nodes?.some((node) => node.type === "OUTPUT"));
|
|
assert.match(material.shaderGraphHash, /^[0-9a-f]{64}$/);
|
|
const saved = output(nodeTreeEngine, nodeTreeHandle, nodeTreeEngine._web_engine_save_blend, true);
|
|
const reopened = nodeTreeEngine._web_engine_create();
|
|
open(nodeTreeEngine, reopened, saved);
|
|
const after = snapshot(nodeTreeEngine, reopened).materials?.find((value) => value.id === material.id);
|
|
assert.deepEqual(after, material);
|
|
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]);
|
|
const malformedPointer = nodeTreeEngine._malloc(malformed.byteLength); let malformedResult;
|
|
try { nodeTreeEngine.HEAPU8.set(malformed, malformedPointer); malformedResult = nodeTreeEngine._web_engine_open_blend(nodeTreeHandle, malformedPointer, malformed.byteLength); } finally { nodeTreeEngine._free(malformedPointer); }
|
|
assert.notEqual(malformedResult, 0); assert.deepEqual(snapshot(nodeTreeEngine, nodeTreeHandle), nodeTreeSnapshot);
|
|
nodeTreeEngine._web_engine_destroy(nodeTreeHandle); nodeTreeEngine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task: "M16-GAP-00016", operation: "NODETREE_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, nodeTreeFixture).replaceAll(path.sep, "/"), sha256: desktopEvidence.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, nodeTreeDesktopPath).replaceAll(path.sep, "/"), saveReopen: desktopEvidence.saveReopen }, wasm: { status: "EXACT", nodeTreeId: "nodetree:WebGapNodeTreeMaterial", nodeCount: material.nodes.length, linkCount: material.links.length, shaderGraphHash: material.shaderGraphHash }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00017" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00016/nodetree-local-exact-report.json"); fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} nodeTree=${report.wasm.nodeTreeId} nodes=${material.nodes.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00017") {
|
|
const objectFixture = path.join(root, "tests/files/web/generated/M16-GAP-00017-datablock-Object.blend");
|
|
const objectDesktopPath = path.join(root, "tests/golden/M16-GAP-00017/object-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(objectDesktopPath), { recursive: true });
|
|
const desktopRun = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-object-desktop.py"), "--", objectFixture, objectDesktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(desktopRun.status, 0, `${desktopRun.stdout ?? ""}\n${desktopRun.stderr ?? ""}`);
|
|
const desktopEvidence = JSON.parse(fs.readFileSync(objectDesktopPath, "utf8"));
|
|
const objectEngine = await factory({ wasmBinary });
|
|
const objectHandle = objectEngine._web_engine_create();
|
|
open(objectEngine, objectHandle, fs.readFileSync(objectFixture));
|
|
const objectSnapshot = snapshot(objectEngine, objectHandle);
|
|
const object = objectSnapshot.nodes?.find((value) => value.id === "object:WebGapObject");
|
|
assert.ok(object);
|
|
assert.equal(object.type, "EMPTY");
|
|
assert.equal(object.visible, desktopEvidence.object.visible);
|
|
assert.equal(object.selectable, desktopEvidence.object.selectable);
|
|
assert.deepEqual(object.transform.translation.map((value) => Number(value.toFixed(6))), desktopEvidence.object.location);
|
|
object.transform.rotationEuler.forEach((value, index) => assert.ok(Math.abs(value - desktopEvidence.object.rotationEuler[index]) <= 1e-5));
|
|
object.transform.scale.forEach((value, index) => assert.ok(Math.abs(value - desktopEvidence.object.scale[index]) <= 1e-5));
|
|
assert.equal(object.parentId, null);
|
|
const saved = output(objectEngine, objectHandle, objectEngine._web_engine_save_blend, true);
|
|
const reopened = objectEngine._web_engine_create();
|
|
open(objectEngine, reopened, saved);
|
|
assert.deepEqual(snapshot(objectEngine, reopened).nodes?.find((value) => value.id === object.id), object);
|
|
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]);
|
|
const malformedPointer = objectEngine._malloc(malformed.byteLength); let malformedResult;
|
|
try { objectEngine.HEAPU8.set(malformed, malformedPointer); malformedResult = objectEngine._web_engine_open_blend(objectHandle, malformedPointer, malformed.byteLength); } finally { objectEngine._free(malformedPointer); }
|
|
assert.notEqual(malformedResult, 0); assert.deepEqual(snapshot(objectEngine, objectHandle), objectSnapshot);
|
|
objectEngine._web_engine_destroy(objectHandle); objectEngine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task: "M16-GAP-00017", operation: "OBJECT_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, objectFixture).replaceAll(path.sep, "/"), sha256: desktopEvidence.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, objectDesktopPath).replaceAll(path.sep, "/"), saveReopen: desktopEvidence.saveReopen }, wasm: { status: "EXACT", objectId: object.id, type: object.type, translation: object.transform.translation, scale: object.transform.scale }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00018" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00017/object-local-exact-report.json"); fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} object=${object.id} type=${object.type} desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00018") {
|
|
const particleSettingsFixture = path.join(root, "tests/files/web/generated/M16-GAP-00018-datablock-ParticleSettings.blend");
|
|
const particleSettingsDesktopPath = path.join(root, "tests/golden/M16-GAP-00018/particle-settings-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(particleSettingsDesktopPath), { recursive: true });
|
|
const desktopRun = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-particle-settings-desktop.py"), "--", particleSettingsFixture, particleSettingsDesktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(desktopRun.status, 0, `${desktopRun.stdout ?? ""}\n${desktopRun.stderr ?? ""}`);
|
|
const desktopEvidence = JSON.parse(fs.readFileSync(particleSettingsDesktopPath, "utf8"));
|
|
assert.equal(desktopEvidence.saveReopen, "EXACT");
|
|
const particleSettingsEngine = await factory({ wasmBinary });
|
|
const particleSettingsHandle = particleSettingsEngine._web_engine_create();
|
|
open(particleSettingsEngine, particleSettingsHandle, fs.readFileSync(particleSettingsFixture));
|
|
const particleSettingsSnapshot = snapshot(particleSettingsEngine, particleSettingsHandle);
|
|
const particleSettings = particleSettingsSnapshot.particleSettings?.find((value) => value.id === "particle-settings:WebGapParticleSettings");
|
|
assert.ok(particleSettings);
|
|
assert.deepEqual(particleSettings, desktopEvidence.particleSettings);
|
|
assert.equal(particleSettings.type, 0);
|
|
assert.equal(particleSettings.from, 0);
|
|
assert.equal(particleSettings.distribution, 0);
|
|
assert.equal(particleSettings.physicsType, 0);
|
|
assert.equal(particleSettings.totalParticles, 128);
|
|
assert.equal(particleSettings.start, 4);
|
|
assert.equal(particleSettings.end, 96);
|
|
assert.equal(particleSettings.lifetime, 32);
|
|
assert.equal(particleSettings.size, 0.125);
|
|
assert.equal(particleSettings.drawSize, 0.25);
|
|
const saved = output(particleSettingsEngine, particleSettingsHandle, particleSettingsEngine._web_engine_save_blend, true);
|
|
const reopened = particleSettingsEngine._web_engine_create();
|
|
open(particleSettingsEngine, reopened, saved);
|
|
const after = snapshot(particleSettingsEngine, reopened).particleSettings?.find((value) => value.id === particleSettings.id);
|
|
assert.deepEqual(after, particleSettings);
|
|
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]);
|
|
const malformedPointer = particleSettingsEngine._malloc(malformed.byteLength);
|
|
let malformedResult;
|
|
try {
|
|
particleSettingsEngine.HEAPU8.set(malformed, malformedPointer);
|
|
malformedResult = particleSettingsEngine._web_engine_open_blend(particleSettingsHandle, malformedPointer, malformed.byteLength);
|
|
}
|
|
finally {
|
|
particleSettingsEngine._free(malformedPointer);
|
|
}
|
|
assert.notEqual(malformedResult, 0);
|
|
assert.deepEqual(snapshot(particleSettingsEngine, particleSettingsHandle), particleSettingsSnapshot);
|
|
particleSettingsEngine._web_engine_destroy(particleSettingsHandle);
|
|
particleSettingsEngine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task: "M16-GAP-00018", operation: "PARTICLE_SETTINGS_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, particleSettingsFixture).replaceAll(path.sep, "/"), sha256: desktopEvidence.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, particleSettingsDesktopPath).replaceAll(path.sep, "/"), saveReopen: desktopEvidence.saveReopen }, wasm: { status: "EXACT", particleSettingsId: particleSettings.id, fields: { type: particleSettings.type, from: particleSettings.from, distribution: particleSettings.distribution, physicsType: particleSettings.physicsType, totalParticles: particleSettings.totalParticles, start: particleSettings.start, end: particleSettings.end, lifetime: particleSettings.lifetime, size: particleSettings.size, drawSize: particleSettings.drawSize } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00019" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00018/particle-settings-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} particleSettings=${particleSettings.id} count=${particleSettings.totalParticles} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00019") {
|
|
const pointCloudFixture = path.join(root, "tests/files/web/generated/M16-GAP-00019-datablock-PointCloud.blend");
|
|
const pointCloudDesktopPath = path.join(root, "tests/golden/M16-GAP-00019/pointcloud-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(pointCloudDesktopPath), { recursive: true });
|
|
const desktopRun = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-pointcloud-desktop.py"), "--", pointCloudFixture, pointCloudDesktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(desktopRun.status, 0, `${desktopRun.stdout ?? ""}\n${desktopRun.stderr ?? ""}`);
|
|
const desktopEvidence = JSON.parse(fs.readFileSync(pointCloudDesktopPath, "utf8"));
|
|
assert.equal(desktopEvidence.saveReopen, "EXACT");
|
|
const pointCloudEngine = await factory({ wasmBinary });
|
|
const pointCloudHandle = pointCloudEngine._web_engine_create();
|
|
open(pointCloudEngine, pointCloudHandle, fs.readFileSync(pointCloudFixture));
|
|
const pointCloudSnapshot = snapshot(pointCloudEngine, pointCloudHandle);
|
|
const pointCloud = pointCloudSnapshot.nonMeshData?.find((value) => value.id === "pointcloud:WebGapPointCloud");
|
|
assert.ok(pointCloud);
|
|
assert.equal(pointCloud.type, "POINT_CLOUD");
|
|
assert.equal(pointCloud.geometryStatus, "available");
|
|
assert.equal(pointCloud.pointCount, desktopEvidence.pointCloud.pointCount);
|
|
assert.deepEqual(pointCloud.controlPoints, desktopEvidence.pointCloud.controlPoints);
|
|
assert.deepEqual(pointCloud.radii, desktopEvidence.pointCloud.radii);
|
|
const saved = output(pointCloudEngine, pointCloudHandle, pointCloudEngine._web_engine_save_blend, true);
|
|
const reopened = pointCloudEngine._web_engine_create();
|
|
open(pointCloudEngine, reopened, saved);
|
|
const after = snapshot(pointCloudEngine, reopened).nonMeshData?.find((value) => value.id === pointCloud.id);
|
|
assert.deepEqual(after, pointCloud);
|
|
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]);
|
|
const malformedPointer = pointCloudEngine._malloc(malformed.byteLength);
|
|
let malformedResult;
|
|
try {
|
|
pointCloudEngine.HEAPU8.set(malformed, malformedPointer);
|
|
malformedResult = pointCloudEngine._web_engine_open_blend(pointCloudHandle, malformedPointer, malformed.byteLength);
|
|
}
|
|
finally {
|
|
pointCloudEngine._free(malformedPointer);
|
|
}
|
|
assert.notEqual(malformedResult, 0);
|
|
assert.deepEqual(snapshot(pointCloudEngine, pointCloudHandle), pointCloudSnapshot);
|
|
pointCloudEngine._web_engine_destroy(pointCloudHandle);
|
|
pointCloudEngine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task: "M16-GAP-00019", operation: "POINT_CLOUD_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, pointCloudFixture).replaceAll(path.sep, "/"), sha256: desktopEvidence.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, pointCloudDesktopPath).replaceAll(path.sep, "/"), saveReopen: desktopEvidence.saveReopen }, wasm: { status: "EXACT", pointCloudId: pointCloud.id, pointCount: pointCloud.pointCount, geometryStatus: pointCloud.geometryStatus, pointHash: crypto.createHash("sha256").update(JSON.stringify(pointCloud.controlPoints)).digest("hex"), radiusHash: crypto.createHash("sha256").update(JSON.stringify(pointCloud.radii)).digest("hex") }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00020" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00019/pointcloud-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} pointCloud=${pointCloud.id} points=${pointCloud.pointCount} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00020") {
|
|
const sceneFixture = path.join(root, "tests/files/web/generated/M16-GAP-00020-datablock-Scene.blend");
|
|
const sceneDesktopPath = path.join(root, "tests/golden/M16-GAP-00020/scene-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(sceneDesktopPath), { recursive: true });
|
|
const desktopRun = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-scene-desktop.py"), "--", sceneFixture, sceneDesktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(desktopRun.status, 0, `${desktopRun.stdout ?? ""}\n${desktopRun.stderr ?? ""}`);
|
|
const desktopEvidence = JSON.parse(fs.readFileSync(sceneDesktopPath, "utf8"));
|
|
assert.equal(desktopEvidence.saveReopen, "EXACT");
|
|
const sceneEngine = await factory({ wasmBinary });
|
|
const sceneHandle = sceneEngine._web_engine_create();
|
|
open(sceneEngine, sceneHandle, fs.readFileSync(sceneFixture));
|
|
const sceneSnapshot = snapshot(sceneEngine, sceneHandle);
|
|
const scene = sceneSnapshot.scenes?.find((value) => value.id === "scene:WebGapScene");
|
|
assert.ok(scene);
|
|
const expected = desktopEvidence.scene;
|
|
assert.equal(scene.name, expected.name);
|
|
assert.equal(scene.renderEngine, expected.renderEngine);
|
|
assert.equal(sceneSnapshot.frame.current, expected.frameCurrent);
|
|
assert.equal(sceneSnapshot.frame.start, expected.frameStart);
|
|
assert.equal(sceneSnapshot.frame.end, expected.frameEnd);
|
|
assert.equal(sceneSnapshot.coordinateSystem.unitSystem, expected.unitSystem);
|
|
assert.equal(sceneSnapshot.coordinateSystem.unitScale, expected.unitScale);
|
|
assert.equal(sceneSnapshot.scenes.length, 1);
|
|
assert.equal(scene.id, expected.id);
|
|
const saved = output(sceneEngine, sceneHandle, sceneEngine._web_engine_save_blend, true);
|
|
const reopened = sceneEngine._web_engine_create();
|
|
open(sceneEngine, reopened, saved);
|
|
assert.deepEqual(snapshot(sceneEngine, reopened).scenes, sceneSnapshot.scenes);
|
|
assert.deepEqual(snapshot(sceneEngine, reopened).frame, sceneSnapshot.frame);
|
|
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]);
|
|
const malformedPointer = sceneEngine._malloc(malformed.byteLength);
|
|
let malformedResult;
|
|
try {
|
|
sceneEngine.HEAPU8.set(malformed, malformedPointer);
|
|
malformedResult = sceneEngine._web_engine_open_blend(sceneHandle, malformedPointer, malformed.byteLength);
|
|
}
|
|
finally {
|
|
sceneEngine._free(malformedPointer);
|
|
}
|
|
assert.notEqual(malformedResult, 0);
|
|
assert.deepEqual(snapshot(sceneEngine, sceneHandle), sceneSnapshot);
|
|
sceneEngine._web_engine_destroy(sceneHandle);
|
|
sceneEngine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task: "M16-GAP-00020", operation: "SCENE_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, sceneFixture).replaceAll(path.sep, "/"), sha256: desktopEvidence.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, sceneDesktopPath).replaceAll(path.sep, "/"), saveReopen: desktopEvidence.saveReopen }, wasm: { status: "EXACT", sceneId: scene.id, frame: sceneSnapshot.frame, renderEngine: scene.renderEngine, unitSystem: sceneSnapshot.coordinateSystem.unitSystem, unitScale: sceneSnapshot.coordinateSystem.unitScale }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00021" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00020/scene-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} scene=${scene.id} frames=${sceneSnapshot.frame.start}-${sceneSnapshot.frame.end} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00021") {
|
|
const screenFixture = path.join(root, "tests/files/web/generated/M16-GAP-00021-datablock-Screen.blend");
|
|
const screenDesktopPath = path.join(root, "tests/golden/M16-GAP-00021/screen-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(screenDesktopPath), { recursive: true });
|
|
const desktopRun = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-screen-desktop.py"), "--", screenFixture, screenDesktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(desktopRun.status, 0, `${desktopRun.stdout ?? ""}\n${desktopRun.stderr ?? ""}`);
|
|
const desktopEvidence = JSON.parse(fs.readFileSync(screenDesktopPath, "utf8"));
|
|
assert.equal(desktopEvidence.saveReopen, "EXACT");
|
|
const screenEngine = await factory({ wasmBinary });
|
|
const screenHandle = screenEngine._web_engine_create();
|
|
open(screenEngine, screenHandle, fs.readFileSync(screenFixture));
|
|
const screenSnapshot = snapshot(screenEngine, screenHandle);
|
|
assert.equal(screenSnapshot.editorWorkflowStatus, "AVAILABLE");
|
|
const workflow = screenSnapshot.editorWorkflow;
|
|
const desktopEditors = desktopEvidence.screen.editors.map((editor) => ({DOPESHEET_EDITOR: "DOPE_SHEET", VIEW_3D: "VIEW_3D", OUTLINER: "OUTLINER", PROPERTIES: "PROPERTIES"}[editor] ?? editor)).sort();
|
|
const matchingWorkspace = workflow.workspaces.find((value) => value.areas.length === desktopEvidence.screen.areaCount && value.areas.map((area) => area.editor).sort().join(",") === desktopEditors.join(","));
|
|
assert.ok(matchingWorkspace);
|
|
assert.ok(matchingWorkspace.areas.reduce((count, area) => count + area.regions.length, 0) <= desktopEvidence.screen.regionCount);
|
|
const saved = output(screenEngine, screenHandle, screenEngine._web_engine_save_blend, true);
|
|
const reopened = screenEngine._web_engine_create();
|
|
open(screenEngine, reopened, saved);
|
|
assert.deepEqual(snapshot(screenEngine, reopened).editorWorkflow, screenSnapshot.editorWorkflow);
|
|
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]);
|
|
const malformedPointer = screenEngine._malloc(malformed.byteLength);
|
|
let malformedResult;
|
|
try {
|
|
screenEngine.HEAPU8.set(malformed, malformedPointer);
|
|
malformedResult = screenEngine._web_engine_open_blend(screenHandle, malformedPointer, malformed.byteLength);
|
|
}
|
|
finally {
|
|
screenEngine._free(malformedPointer);
|
|
}
|
|
assert.notEqual(malformedResult, 0);
|
|
assert.deepEqual(snapshot(screenEngine, screenHandle), screenSnapshot);
|
|
screenEngine._web_engine_destroy(screenHandle);
|
|
screenEngine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task: "M16-GAP-00021", operation: "SCREEN_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, screenFixture).replaceAll(path.sep, "/"), sha256: desktopEvidence.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, screenDesktopPath).replaceAll(path.sep, "/"), saveReopen: desktopEvidence.saveReopen }, wasm: { status: "EXACT", workflowStatus: screenSnapshot.editorWorkflowStatus, workspaceId: matchingWorkspace.id, areaCount: matchingWorkspace.areas.length, editorTypes: matchingWorkspace.areas.map((area) => area.editor).sort() }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00022" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00021/screen-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} screen=${desktopEvidence.screen.id} areas=${matchingWorkspace.areas.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00022") {
|
|
const soundFixture = path.join(root, "tests/files/web/generated/M16-GAP-00022-datablock-Sound.blend");
|
|
const soundDesktopPath = path.join(root, "tests/golden/M16-GAP-00022/sound-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(soundDesktopPath), { recursive: true });
|
|
const desktopRun = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-sound-desktop.py"), "--", soundFixture, soundDesktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(desktopRun.status, 0, `${desktopRun.stdout ?? ""}\n${desktopRun.stderr ?? ""}`);
|
|
const desktopEvidence = JSON.parse(fs.readFileSync(soundDesktopPath, "utf8"));
|
|
assert.equal(desktopEvidence.saveReopen, "EXACT");
|
|
const soundEngine = await factory({ wasmBinary });
|
|
const soundHandle = soundEngine._web_engine_create();
|
|
open(soundEngine, soundHandle, fs.readFileSync(soundFixture));
|
|
const soundSnapshot = snapshot(soundEngine, soundHandle);
|
|
const sound = soundSnapshot.sounds?.find((value) => value.id === "sound:WebGapSound");
|
|
assert.ok(sound);
|
|
assert.deepEqual(sound, desktopEvidence.sound);
|
|
const saved = output(soundEngine, soundHandle, soundEngine._web_engine_save_blend, true);
|
|
assert.ok(saved.byteLength > 0);
|
|
const reopened = soundEngine._web_engine_create();
|
|
open(soundEngine, reopened, saved);
|
|
const after = snapshot(soundEngine, reopened).sounds?.find((value) => value.id === sound.id);
|
|
assert.deepEqual(after, sound);
|
|
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]);
|
|
const malformedPointer = soundEngine._malloc(malformed.byteLength);
|
|
let malformedResult;
|
|
try {
|
|
soundEngine.HEAPU8.set(malformed, malformedPointer);
|
|
malformedResult = soundEngine._web_engine_open_blend(soundHandle, malformedPointer, malformed.byteLength);
|
|
}
|
|
finally {
|
|
soundEngine._free(malformedPointer);
|
|
}
|
|
assert.notEqual(malformedResult, 0);
|
|
assert.deepEqual(snapshot(soundEngine, soundHandle), soundSnapshot);
|
|
soundEngine._web_engine_destroy(soundHandle);
|
|
soundEngine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task: "M16-GAP-00022", operation: "SOUND_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, soundFixture).replaceAll(path.sep, "/"), sha256: desktopEvidence.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, soundDesktopPath).replaceAll(path.sep, "/"), saveReopen: desktopEvidence.saveReopen }, wasm: { status: "EXACT", soundId: sound.id, sourcePath: sound.sourcePath, packed: sound.packed, volume: sound.volume, pitch: sound.pitch, audioChannels: sound.audioChannels, sampleRate: sound.sampleRate }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00023" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00022/sound-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} sound=${sound.id} sampleRate=${sound.sampleRate} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00023") {
|
|
const speakerFixture = path.join(root, "tests/files/web/generated/M16-GAP-00023-datablock-Speaker.blend");
|
|
const speakerDesktopPath = path.join(root, "tests/golden/M16-GAP-00023/speaker-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(speakerDesktopPath), { recursive: true });
|
|
const desktopRun = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-speaker-desktop.py"), "--", speakerFixture, speakerDesktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(desktopRun.status, 0, `${desktopRun.stdout ?? ""}\n${desktopRun.stderr ?? ""}`);
|
|
const desktopEvidence = JSON.parse(fs.readFileSync(speakerDesktopPath, "utf8"));
|
|
assert.equal(desktopEvidence.saveReopen, "EXACT");
|
|
const speakerEngine = await factory({ wasmBinary });
|
|
const speakerHandle = speakerEngine._web_engine_create();
|
|
open(speakerEngine, speakerHandle, fs.readFileSync(speakerFixture));
|
|
const speakerSnapshot = snapshot(speakerEngine, speakerHandle);
|
|
const speaker = speakerSnapshot.speakers?.find((value) => value.id === "speaker:WebGapSpeaker");
|
|
assert.ok(speaker);
|
|
assert.deepEqual(speaker, desktopEvidence.speaker);
|
|
const saved = output(speakerEngine, speakerHandle, speakerEngine._web_engine_save_blend, true);
|
|
assert.ok(saved.byteLength > 0);
|
|
const reopened = speakerEngine._web_engine_create();
|
|
open(speakerEngine, reopened, saved);
|
|
const after = snapshot(speakerEngine, reopened).speakers?.find((value) => value.id === speaker.id);
|
|
assert.deepEqual(after, speaker);
|
|
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]);
|
|
const malformedPointer = speakerEngine._malloc(malformed.byteLength);
|
|
let malformedResult;
|
|
try {
|
|
speakerEngine.HEAPU8.set(malformed, malformedPointer);
|
|
malformedResult = speakerEngine._web_engine_open_blend(speakerHandle, malformedPointer, malformed.byteLength);
|
|
}
|
|
finally {
|
|
speakerEngine._free(malformedPointer);
|
|
}
|
|
assert.notEqual(malformedResult, 0);
|
|
assert.deepEqual(snapshot(speakerEngine, speakerHandle), speakerSnapshot);
|
|
speakerEngine._web_engine_destroy(speakerHandle);
|
|
speakerEngine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task: "M16-GAP-00023", operation: "SPEAKER_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, speakerFixture).replaceAll(path.sep, "/"), sha256: desktopEvidence.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, speakerDesktopPath).replaceAll(path.sep, "/"), saveReopen: desktopEvidence.saveReopen }, wasm: { status: "EXACT", speakerId: speaker.id, soundId: speaker.soundId, volumeMax: speaker.volumeMax, volumeMin: speaker.volumeMin, distanceMax: speaker.distanceMax, distanceReference: speaker.distanceReference, attenuation: speaker.attenuation, coneAngleOuter: speaker.coneAngleOuter, coneAngleInner: speaker.coneAngleInner, coneVolumeOuter: speaker.coneVolumeOuter, volume: speaker.volume, pitch: speaker.pitch }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00024" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00023/speaker-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} speaker=${speaker.id} volume=${speaker.volume} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00024") {
|
|
const textFixture = path.join(root, "tests/files/web/generated/M16-GAP-00024-datablock-Text.blend");
|
|
const textDesktopPath = path.join(root, "tests/golden/M16-GAP-00024/text-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(textDesktopPath), { recursive: true });
|
|
const desktopRun = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-text-desktop.py"), "--", textFixture, textDesktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(desktopRun.status, 0, `${desktopRun.stdout ?? ""}\n${desktopRun.stderr ?? ""}`);
|
|
const desktopEvidence = JSON.parse(fs.readFileSync(textDesktopPath, "utf8"));
|
|
assert.equal(desktopEvidence.saveReopen, "EXACT");
|
|
const textEngine = await factory({ wasmBinary });
|
|
const textHandle = textEngine._web_engine_create();
|
|
open(textEngine, textHandle, fs.readFileSync(textFixture));
|
|
const textSnapshot = snapshot(textEngine, textHandle);
|
|
const text = textSnapshot.texts?.find((value) => value.id === "text:WebGapText");
|
|
assert.ok(text);
|
|
assert.deepEqual(text, desktopEvidence.text);
|
|
const saved = output(textEngine, textHandle, textEngine._web_engine_save_blend, true);
|
|
assert.ok(saved.byteLength > 0);
|
|
const reopened = textEngine._web_engine_create();
|
|
open(textEngine, reopened, saved);
|
|
const after = snapshot(textEngine, reopened).texts?.find((value) => value.id === text.id);
|
|
assert.deepEqual(after, text);
|
|
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]);
|
|
const malformedPointer = textEngine._malloc(malformed.byteLength);
|
|
let malformedResult;
|
|
try {
|
|
textEngine.HEAPU8.set(malformed, malformedPointer);
|
|
malformedResult = textEngine._web_engine_open_blend(textHandle, malformedPointer, malformed.byteLength);
|
|
}
|
|
finally {
|
|
textEngine._free(malformedPointer);
|
|
}
|
|
assert.notEqual(malformedResult, 0);
|
|
assert.deepEqual(snapshot(textEngine, textHandle), textSnapshot);
|
|
textEngine._web_engine_destroy(textHandle);
|
|
textEngine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task: "M16-GAP-00024", operation: "TEXT_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, textFixture).replaceAll(path.sep, "/"), sha256: desktopEvidence.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, textDesktopPath).replaceAll(path.sep, "/"), saveReopen: desktopEvidence.saveReopen }, wasm: { status: "EXACT", textId: text.id, sourceSha256: text.sourceSha256, byteLength: text.byteLength, lineCount: text.lineCount, sourcePath: text.sourcePath, internal: text.internal, isDirty: text.isDirty, useModule: text.useModule }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00025" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00024/text-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} text=${text.id} bytes=${text.byteLength} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00025") {
|
|
const textureFixture = path.join(root, "tests/files/web/generated/M16-GAP-00025-datablock-Texture.blend");
|
|
const textureDesktopPath = path.join(root, "tests/golden/M16-GAP-00025/texture-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(textureDesktopPath), { recursive: true });
|
|
const desktopRun = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-texture-desktop.py"), "--", textureFixture, textureDesktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(desktopRun.status, 0, `${desktopRun.stdout ?? ""}\n${desktopRun.stderr ?? ""}`);
|
|
const desktopEvidence = JSON.parse(fs.readFileSync(textureDesktopPath, "utf8"));
|
|
assert.equal(desktopEvidence.saveReopen, "EXACT");
|
|
const textureEngine = await factory({ wasmBinary });
|
|
const textureHandle = textureEngine._web_engine_create();
|
|
open(textureEngine, textureHandle, fs.readFileSync(textureFixture));
|
|
const textureSnapshot = snapshot(textureEngine, textureHandle);
|
|
const texture = textureSnapshot.textures?.find((value) => value.id === "texture:WebGapTexture");
|
|
assert.ok(texture);
|
|
assert.equal(texture.type, 1);
|
|
assert.deepEqual(texture, desktopEvidence.texture);
|
|
const saved = output(textureEngine, textureHandle, textureEngine._web_engine_save_blend, true);
|
|
assert.ok(saved.byteLength > 0);
|
|
const reopened = textureEngine._web_engine_create();
|
|
open(textureEngine, reopened, saved);
|
|
const after = snapshot(textureEngine, reopened).textures?.find((value) => value.id === texture.id);
|
|
assert.deepEqual(after, texture);
|
|
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]);
|
|
const malformedPointer = textureEngine._malloc(malformed.byteLength);
|
|
let malformedResult;
|
|
try {
|
|
textureEngine.HEAPU8.set(malformed, malformedPointer);
|
|
malformedResult = textureEngine._web_engine_open_blend(textureHandle, malformedPointer, malformed.byteLength);
|
|
}
|
|
finally {
|
|
textureEngine._free(malformedPointer);
|
|
}
|
|
assert.notEqual(malformedResult, 0);
|
|
assert.deepEqual(snapshot(textureEngine, textureHandle), textureSnapshot);
|
|
textureEngine._web_engine_destroy(textureHandle);
|
|
textureEngine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task: "M16-GAP-00025", operation: "TEXTURE_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, textureFixture).replaceAll(path.sep, "/"), sha256: desktopEvidence.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, textureDesktopPath).replaceAll(path.sep, "/"), saveReopen: desktopEvidence.saveReopen }, wasm: { status: "EXACT", textureId: texture.id, type: texture.type, noiseScale: texture.noiseScale, noiseDepth: texture.noiseDepth, intensity: texture.intensity, contrast: texture.contrast, saturation: texture.saturation }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00026" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00025/texture-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} texture=${texture.id} type=${texture.type} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00026") {
|
|
const volumeFixture = path.join(root, "tests/files/web/generated/M16-GAP-00026-datablock-Volume.blend");
|
|
const volumeDesktopPath = path.join(root, "tests/golden/M16-GAP-00026/volume-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(volumeDesktopPath), { recursive: true });
|
|
const desktopRun = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-volume-desktop.py"), "--", volumeFixture, volumeDesktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(desktopRun.status, 0, `${desktopRun.stdout ?? ""}\n${desktopRun.stderr ?? ""}`);
|
|
const desktopEvidence = JSON.parse(fs.readFileSync(volumeDesktopPath, "utf8"));
|
|
assert.equal(desktopEvidence.saveReopen, "EXACT");
|
|
const volumeEngine = await factory({ wasmBinary });
|
|
const volumeHandle = volumeEngine._web_engine_create();
|
|
open(volumeEngine, volumeHandle, fs.readFileSync(volumeFixture));
|
|
const volumeSnapshot = snapshot(volumeEngine, volumeHandle);
|
|
const volume = volumeSnapshot.nonMeshData?.find((value) => value.id === "volume:WebGapVolume");
|
|
assert.ok(volume);
|
|
assert.equal(volume.geometryStatus, "blocked");
|
|
assert.deepEqual({ id: volume.id, name: volume.name, sourcePath: volume.sourcePath, volumeProperties: volume.volumeProperties }, { id: desktopEvidence.volume.id, name: desktopEvidence.volume.name, sourcePath: desktopEvidence.volume.sourcePath, volumeProperties: { displayDensity: desktopEvidence.volume.displayDensity, interpolation: desktopEvidence.volume.interpolation, stepSize: desktopEvidence.volume.stepSize, velocityGrid: desktopEvidence.volume.velocityGrid, velocityScale: desktopEvidence.volume.velocityScale } });
|
|
const saved = output(volumeEngine, volumeHandle, volumeEngine._web_engine_save_blend, true);
|
|
const reopened = volumeEngine._web_engine_create();
|
|
open(volumeEngine, reopened, saved);
|
|
const after = snapshot(volumeEngine, reopened).nonMeshData?.find((value) => value.id === volume.id);
|
|
assert.deepEqual(after, volume);
|
|
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]);
|
|
const malformedPointer = volumeEngine._malloc(malformed.byteLength); let malformedResult;
|
|
try { volumeEngine.HEAPU8.set(malformed, malformedPointer); malformedResult = volumeEngine._web_engine_open_blend(volumeHandle, malformedPointer, malformed.byteLength); } finally { volumeEngine._free(malformedPointer); }
|
|
assert.notEqual(malformedResult, 0); assert.deepEqual(snapshot(volumeEngine, volumeHandle), volumeSnapshot);
|
|
volumeEngine._web_engine_destroy(volumeHandle); volumeEngine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task: "M16-GAP-00026", operation: "VOLUME_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, volumeFixture).replaceAll(path.sep, "/"), sha256: desktopEvidence.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, volumeDesktopPath).replaceAll(path.sep, "/"), saveReopen: desktopEvidence.saveReopen }, wasm: { status: "EXACT", volumeId: volume.id, geometryStatus: volume.geometryStatus, sourcePath: volume.sourcePath, volumeProperties: volume.volumeProperties }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00027" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00026/volume-local-exact-report.json"); fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} volume=${volume.id} density=${volume.volumeProperties.displayDensity} desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00028") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00028-datablock-WorkSpace.blend"); const desktopPath = path.join(root, "tests/golden/M16-GAP-00028/workspace-desktop-report.json"); fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const run = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-workspace-desktop.py"), "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 }); assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`); const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
const engine = await factory({ wasmBinary }); const handle = engine._web_engine_create(); open(engine, handle, fs.readFileSync(fixture)); const before = snapshot(engine, handle); const workspace = before.workspaces?.find((value) => value.id === "workspace:WebGapWorkspace"); assert.ok(workspace); assert.deepEqual(workspace, desktop.workspace);
|
|
const saved = output(engine, handle, engine._web_engine_save_blend, true); const reopened = engine._web_engine_create(); open(engine, reopened, saved); assert.deepEqual(snapshot(engine, reopened).workspaces?.find((value) => value.id === workspace.id), workspace);
|
|
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]); const pointer = engine._malloc(malformed.byteLength); let result; try { engine.HEAPU8.set(malformed, pointer); result = engine._web_engine_open_blend(handle, pointer, malformed.byteLength); } finally { engine._free(pointer); } assert.notEqual(result, 0); assert.deepEqual(snapshot(engine, handle), before); engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task: "M16-GAP-00028", operation: "WORKSPACE_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, fixture).replaceAll(path.sep, "/"), sha256: desktop.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, desktopPath).replaceAll(path.sep, "/"), saveReopen: desktop.saveReopen }, wasm: { status: "EXACT", workspace: workspace }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00029" }; const reportPath = path.join(root, "tests/golden/M16-GAP-00028/workspace-local-exact-report.json"); fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} workspace=${workspace.id} desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00029") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00029-datablock-World.blend"); const desktopPath = path.join(root, "tests/golden/M16-GAP-00029/world-desktop-report.json"); fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const run = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-world-desktop.py"), "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 }); assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`); const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
const engine = await factory({ wasmBinary }); const handle = engine._web_engine_create(); open(engine, handle, fs.readFileSync(fixture)); const before = snapshot(engine, handle); const world = before.worlds?.find((value) => value.id === "world:WebGapWorld"); assert.ok(world); assert.deepEqual(world, desktop.world);
|
|
const saved = output(engine, handle, engine._web_engine_save_blend, true); const reopened = engine._web_engine_create(); open(engine, reopened, saved); assert.deepEqual(snapshot(engine, reopened).worlds?.find((value) => value.id === world.id), world);
|
|
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]); const pointer = engine._malloc(malformed.byteLength); let result; try { engine.HEAPU8.set(malformed, pointer); result = engine._web_engine_open_blend(handle, pointer, malformed.byteLength); } finally { engine._free(pointer); } assert.notEqual(result, 0); assert.deepEqual(snapshot(engine, handle), before); engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task: "M16-GAP-00029", operation: "WORLD_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, fixture).replaceAll(path.sep, "/"), sha256: desktop.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, desktopPath).replaceAll(path.sep, "/"), saveReopen: desktop.saveReopen }, wasm: { status: "EXACT", world: world }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00030" }; const reportPath = path.join(root, "tests/golden/M16-GAP-00029/world-local-exact-report.json"); fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} world=${world.id} desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00030") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00030-modifier-ARMATURE.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00030/armature-modifier-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const run = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-armature-modifier-desktop.py"), "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const mesh = before.meshes?.find((value) => value.id === desktop.modifier.meshId);
|
|
assert.ok(mesh);
|
|
const modifier = mesh.modifierStack?.find((value) => value.type === "ARMATURE");
|
|
assert.ok(modifier);
|
|
assert.equal(modifier.name, desktop.modifier.modifier.name);
|
|
assert.equal(modifier.parameters.typeCode, desktop.modifier.modifier.typeCode);
|
|
assert.equal(modifier.parameters.armatureObjectId, desktop.modifier.armatureObjectId);
|
|
assert.equal(modifier.parameters.vertexGroup, desktop.modifier.modifier.vertexGroup);
|
|
assert.equal(modifier.parameters.deformFlags, 1);
|
|
assert.ok(modifier.dependsOn.includes(desktop.modifier.armatureObjectId));
|
|
assert.equal(modifier.enabled, desktop.modifier.modifier.enabled);
|
|
assert.equal(modifier.showViewport, desktop.modifier.modifier.showViewport);
|
|
assert.equal(modifier.showRender, desktop.modifier.modifier.showRender);
|
|
assert.equal(modifier.showEditMode, desktop.modifier.modifier.showEditMode);
|
|
assert.equal(modifier.showOnCage, desktop.modifier.modifier.showOnCage);
|
|
const saved = output(engine, handle, engine._web_engine_save_blend, true);
|
|
const reopened = engine._web_engine_create();
|
|
open(engine, reopened, saved);
|
|
const after = snapshot(engine, reopened).meshes?.find((value) => value.id === mesh.id);
|
|
assert.deepEqual(after?.modifierStack, mesh.modifierStack);
|
|
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]);
|
|
const pointer = engine._malloc(malformed.byteLength);
|
|
let result;
|
|
try {
|
|
engine.HEAPU8.set(malformed, pointer);
|
|
result = engine._web_engine_open_blend(handle, pointer, malformed.byteLength);
|
|
}
|
|
finally {
|
|
engine._free(pointer);
|
|
}
|
|
assert.notEqual(result, 0);
|
|
assert.deepEqual(snapshot(engine, handle), before);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task: "M16-GAP-00030", operation: "ARMATURE_MODIFIER_LOCAL_EXACT", fixture: { path: path.relative(root, fixture).replaceAll(path.sep, "/"), sha256: desktop.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, desktopPath).replaceAll(path.sep, "/"), saveReopen: desktop.saveReopen }, wasm: { status: "EXACT", meshId: mesh.id, modifier: modifier }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00031" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00030/armature-modifier-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} mesh=${mesh.id} modifier=${modifier.name} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00031") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00031-modifier-ARRAY.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00031/array-modifier-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const run = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-array-modifier-desktop.py"), "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const mesh = before.meshes?.find((value) => value.id === desktop.modifier.meshId);
|
|
assert.ok(mesh);
|
|
const modifier = mesh.modifierStack?.find((value) => value.type === "ARRAY");
|
|
assert.ok(modifier);
|
|
assert.equal(modifier.name, desktop.modifier.modifier.name);
|
|
assert.equal(modifier.parameters.typeCode, desktop.modifier.modifier.typeCode);
|
|
assert.equal(modifier.parameters.count, desktop.modifier.modifier.count);
|
|
assert.deepEqual(modifier.parameters.targetObjectIds, [desktop.modifier.modifier.startCapObjectId, desktop.modifier.modifier.endCapObjectId]);
|
|
assert.ok(modifier.dependsOn.includes(desktop.modifier.modifier.startCapObjectId));
|
|
assert.ok(modifier.dependsOn.includes(desktop.modifier.modifier.endCapObjectId));
|
|
assert.equal(modifier.showViewport, desktop.modifier.modifier.showViewport);
|
|
assert.equal(modifier.showRender, desktop.modifier.modifier.showRender);
|
|
assert.equal(modifier.showEditMode, desktop.modifier.modifier.showEditMode);
|
|
assert.equal(modifier.showOnCage, desktop.modifier.modifier.showOnCage);
|
|
const saved = output(engine, handle, engine._web_engine_save_blend, true);
|
|
const reopened = engine._web_engine_create();
|
|
open(engine, reopened, saved);
|
|
assert.deepEqual(snapshot(engine, reopened).meshes?.find((value) => value.id === mesh.id)?.modifierStack, mesh.modifierStack);
|
|
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]);
|
|
const pointer = engine._malloc(malformed.byteLength);
|
|
let result;
|
|
try { engine.HEAPU8.set(malformed, pointer); result = engine._web_engine_open_blend(handle, pointer, malformed.byteLength); }
|
|
finally { engine._free(pointer); }
|
|
assert.notEqual(result, 0);
|
|
assert.deepEqual(snapshot(engine, handle), before);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task: "M16-GAP-00031", operation: "ARRAY_MODIFIER_LOCAL_EXACT", fixture: { path: path.relative(root, fixture).replaceAll(path.sep, "/"), sha256: desktop.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, desktopPath).replaceAll(path.sep, "/"), saveReopen: desktop.saveReopen }, wasm: { status: "EXACT", meshId: mesh.id, modifier: modifier }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00032" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00031/array-modifier-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} mesh=${mesh.id} modifier=${modifier.name} count=${modifier.parameters.count} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00032") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00032-modifier-BEVEL.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00032/bevel-modifier-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const run = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-bevel-modifier-desktop.py"), "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const mesh = before.meshes?.find((value) => value.id === desktop.modifier.meshId);
|
|
assert.ok(mesh);
|
|
const modifier = mesh.modifierStack?.find((value) => value.type === "BEVEL");
|
|
assert.ok(modifier);
|
|
assert.equal(modifier.name, desktop.modifier.modifier.name);
|
|
assert.equal(modifier.parameters.typeCode, desktop.modifier.modifier.typeCode);
|
|
assert.ok(Math.abs(modifier.parameters.width - desktop.modifier.modifier.width) < 1e-5);
|
|
assert.equal(modifier.parameters.segments, desktop.modifier.modifier.segments);
|
|
assert.ok(Math.abs(modifier.parameters.profile - desktop.modifier.modifier.profile) < 1e-5);
|
|
assert.equal(modifier.showViewport, desktop.modifier.modifier.showViewport);
|
|
assert.equal(modifier.showRender, desktop.modifier.modifier.showRender);
|
|
assert.equal(modifier.showEditMode, desktop.modifier.modifier.showEditMode);
|
|
assert.equal(modifier.showOnCage, desktop.modifier.modifier.showOnCage);
|
|
const saved = output(engine, handle, engine._web_engine_save_blend, true);
|
|
const reopened = engine._web_engine_create();
|
|
open(engine, reopened, saved);
|
|
assert.deepEqual(snapshot(engine, reopened).meshes?.find((value) => value.id === mesh.id)?.modifierStack, mesh.modifierStack);
|
|
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]);
|
|
const pointer = engine._malloc(malformed.byteLength);
|
|
let result;
|
|
try { engine.HEAPU8.set(malformed, pointer); result = engine._web_engine_open_blend(handle, pointer, malformed.byteLength); }
|
|
finally { engine._free(pointer); }
|
|
assert.notEqual(result, 0);
|
|
assert.deepEqual(snapshot(engine, handle), before);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task: "M16-GAP-00032", operation: "BEVEL_MODIFIER_LOCAL_EXACT", fixture: { path: path.relative(root, fixture).replaceAll(path.sep, "/"), sha256: desktop.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, desktopPath).replaceAll(path.sep, "/"), saveReopen: desktop.saveReopen }, wasm: { status: "EXACT", meshId: mesh.id, modifier: modifier }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00033" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00032/bevel-modifier-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} mesh=${mesh.id} modifier=${modifier.name} width=${modifier.parameters.width} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00033") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00033-modifier-BOOLEAN.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00033/boolean-modifier-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const run = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-boolean-modifier-desktop.py"), "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const mesh = before.meshes?.find((value) => value.id === desktop.modifier.meshId);
|
|
assert.ok(mesh);
|
|
const modifier = mesh.modifierStack?.find((value) => value.type === "BOOLEAN");
|
|
assert.ok(modifier);
|
|
assert.equal(modifier.name, desktop.modifier.modifier.name);
|
|
assert.equal(modifier.parameters.typeCode, desktop.modifier.modifier.typeCode);
|
|
assert.equal(modifier.parameters.operation, 2);
|
|
assert.equal(modifier.parameters.solver, 1);
|
|
assert.equal(modifier.parameters.targetObjectIds[0], desktop.modifier.modifier.targetObjectId);
|
|
assert.ok(modifier.dependsOn.includes(desktop.modifier.modifier.targetObjectId));
|
|
assert.equal(modifier.showViewport, desktop.modifier.modifier.showViewport);
|
|
assert.equal(modifier.showRender, desktop.modifier.modifier.showRender);
|
|
assert.equal(modifier.showEditMode, desktop.modifier.modifier.showEditMode);
|
|
assert.equal(modifier.showOnCage, desktop.modifier.modifier.showOnCage);
|
|
const saved = output(engine, handle, engine._web_engine_save_blend, true);
|
|
const reopened = engine._web_engine_create();
|
|
open(engine, reopened, saved);
|
|
assert.deepEqual(snapshot(engine, reopened).meshes?.find((value) => value.id === mesh.id)?.modifierStack, mesh.modifierStack);
|
|
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]);
|
|
const pointer = engine._malloc(malformed.byteLength);
|
|
let result;
|
|
try { engine.HEAPU8.set(malformed, pointer); result = engine._web_engine_open_blend(handle, pointer, malformed.byteLength); }
|
|
finally { engine._free(pointer); }
|
|
assert.notEqual(result, 0);
|
|
assert.deepEqual(snapshot(engine, handle), before);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task: "M16-GAP-00033", operation: "BOOLEAN_MODIFIER_LOCAL_EXACT", fixture: { path: path.relative(root, fixture).replaceAll(path.sep, "/"), sha256: desktop.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, desktopPath).replaceAll(path.sep, "/"), saveReopen: desktop.saveReopen }, wasm: { status: "EXACT", meshId: mesh.id, modifier: modifier }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00034" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00033/boolean-modifier-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} mesh=${mesh.id} modifier=${modifier.name} operation=${modifier.parameters.operation} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00034") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00034-modifier-BUILD.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00034/build-modifier-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const run = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-build-modifier-desktop.py"), "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const mesh = before.meshes?.find((value) => value.id === desktop.modifier.meshId);
|
|
assert.ok(mesh);
|
|
const modifier = mesh.modifierStack?.find((value) => value.type === "BUILD");
|
|
assert.ok(modifier);
|
|
assert.equal(modifier.name, desktop.modifier.modifier.name);
|
|
assert.equal(modifier.parameters.typeCode, desktop.modifier.modifier.typeCode);
|
|
assert.ok(Math.abs(modifier.parameters.start - desktop.modifier.modifier.start) < 1e-5);
|
|
assert.ok(Math.abs(modifier.parameters.length - desktop.modifier.modifier.length) < 1e-5);
|
|
assert.equal(modifier.parameters.flag, 5);
|
|
assert.equal(modifier.parameters.seed, desktop.modifier.modifier.seed);
|
|
assert.equal(modifier.showViewport, desktop.modifier.modifier.showViewport);
|
|
assert.equal(modifier.showRender, desktop.modifier.modifier.showRender);
|
|
assert.equal(modifier.showEditMode, desktop.modifier.modifier.showEditMode);
|
|
assert.equal(modifier.showOnCage, desktop.modifier.modifier.showOnCage);
|
|
const saved = output(engine, handle, engine._web_engine_save_blend, true);
|
|
const reopened = engine._web_engine_create();
|
|
open(engine, reopened, saved);
|
|
assert.deepEqual(snapshot(engine, reopened).meshes?.find((value) => value.id === mesh.id)?.modifierStack, mesh.modifierStack);
|
|
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]);
|
|
const pointer = engine._malloc(malformed.byteLength); let result;
|
|
try { engine.HEAPU8.set(malformed, pointer); result = engine._web_engine_open_blend(handle, pointer, malformed.byteLength); }
|
|
finally { engine._free(pointer); }
|
|
assert.notEqual(result, 0); assert.deepEqual(snapshot(engine, handle), before);
|
|
engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task: "M16-GAP-00034", operation: "BUILD_MODIFIER_LOCAL_EXACT", fixture: { path: path.relative(root, fixture).replaceAll(path.sep, "/"), sha256: desktop.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, desktopPath).replaceAll(path.sep, "/"), saveReopen: desktop.saveReopen }, wasm: { status: "EXACT", meshId: mesh.id, modifier: modifier }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00035" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00034/build-modifier-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} mesh=${mesh.id} modifier=${modifier.name} start=${modifier.parameters.start} desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00035") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00035-modifier-CAST.blend"); const desktopPath = path.join(root, "tests/golden/M16-GAP-00035/cast-modifier-desktop-report.json"); fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const run = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-cast-modifier-desktop.py"), "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 }); assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`); const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
const engine = await factory({ wasmBinary }); const handle = engine._web_engine_create(); open(engine, handle, fs.readFileSync(fixture)); const before = snapshot(engine, handle); const mesh = before.meshes?.find((value) => value.id === desktop.modifier.meshId); assert.ok(mesh); const modifier = mesh.modifierStack?.find((value) => value.type === "CAST"); assert.ok(modifier); assert.equal(modifier.name, desktop.modifier.modifier.name); assert.equal(modifier.parameters.typeCode, desktop.modifier.modifier.typeCode); assert.ok(Math.abs(modifier.parameters.factor - desktop.modifier.modifier.factor) < 1e-5); assert.ok(Math.abs(modifier.parameters.radius - desktop.modifier.modifier.radius) < 1e-5); assert.equal(modifier.parameters.mode, 3); assert.equal(modifier.parameters.flag, 5); assert.equal(modifier.showViewport, desktop.modifier.modifier.showViewport); assert.equal(modifier.showRender, desktop.modifier.modifier.showRender); const saved = output(engine, handle, engine._web_engine_save_blend, true); const reopened = engine._web_engine_create(); open(engine, reopened, saved); assert.deepEqual(snapshot(engine, reopened).meshes?.find((value) => value.id === mesh.id)?.modifierStack, mesh.modifierStack); const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]); const pointer = engine._malloc(malformed.byteLength); let result; try { engine.HEAPU8.set(malformed, pointer); result = engine._web_engine_open_blend(handle, pointer, malformed.byteLength); } finally { engine._free(pointer); } assert.notEqual(result, 0); assert.deepEqual(snapshot(engine, handle), before); engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task: "M16-GAP-00035", operation: "CAST_MODIFIER_LOCAL_EXACT", fixture: { path: path.relative(root, fixture).replaceAll(path.sep, "/"), sha256: desktop.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, desktopPath).replaceAll(path.sep, "/"), saveReopen: desktop.saveReopen }, wasm: { status: "EXACT", meshId: mesh.id, modifier }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00036" }; const reportPath = path.join(root, "tests/golden/M16-GAP-00035/cast-modifier-local-exact-report.json"); fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} mesh=${mesh.id} modifier=${modifier.name} factor=${modifier.parameters.factor} desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00036") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00036-modifier-CLOTH.blend"); const desktopPath = path.join(root, "tests/golden/M16-GAP-00036/cloth-modifier-desktop-report.json"); fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const run = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-cloth-modifier-desktop.py"), "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 }); assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`); const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
const engine = await factory({ wasmBinary }); const handle = engine._web_engine_create(); open(engine, handle, fs.readFileSync(fixture)); const before = snapshot(engine, handle); const mesh = before.meshes?.find((value) => value.id === desktop.modifier.meshId); assert.ok(mesh); const modifier = mesh.modifierStack?.find((value) => value.type === "CLOTH"); assert.ok(modifier); assert.equal(modifier.name, desktop.modifier.modifier.name); assert.equal(modifier.parameters.typeCode, desktop.modifier.modifier.typeCode); assert.equal(modifier.showViewport, desktop.modifier.modifier.showViewport); assert.equal(modifier.showRender, desktop.modifier.modifier.showRender); const saved = output(engine, handle, engine._web_engine_save_blend, true); const reopened = engine._web_engine_create(); open(engine, reopened, saved); assert.deepEqual(snapshot(engine, reopened).meshes?.find((value) => value.id === mesh.id)?.modifierStack, mesh.modifierStack); const malformed = new Uint8Array([0x42,0x4c,0x45,0x4e,0x44,0x45,0x52]); const pointer = engine._malloc(malformed.byteLength); let result; try { engine.HEAPU8.set(malformed, pointer); result = engine._web_engine_open_blend(handle, pointer, malformed.byteLength); } finally { engine._free(pointer); } assert.notEqual(result, 0); assert.deepEqual(snapshot(engine, handle), before); engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task: "M16-GAP-00036", operation: "CLOTH_MODIFIER_LOCAL_EXACT", fixture: { path: path.relative(root, fixture).replaceAll(path.sep, "/"), sha256: desktop.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, desktopPath).replaceAll(path.sep, "/"), saveReopen: desktop.saveReopen }, wasm: { status: "EXACT", meshId: mesh.id, modifier }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00037" }; const reportPath = path.join(root, "tests/golden/M16-GAP-00036/cloth-modifier-local-exact-report.json"); fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} mesh=${mesh.id} modifier=${modifier.name} desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00037") {
|
|
const fixture=path.join(root,"tests/files/web/generated/M16-GAP-00037-modifier-COLLISION.blend"), desktopPath=path.join(root,"tests/golden/M16-GAP-00037/collision-modifier-desktop-report.json"); fs.mkdirSync(path.dirname(desktopPath),{recursive:true}); const run=spawnSync(process.env.BLENDER_BIN??path.join(root,"build_blender_5.2.0/bin/blender"),["-b","--factory-startup","--python",path.join(root,"tools/web/check-collision-modifier-desktop.py"),"--",fixture,desktopPath],{cwd:root,encoding:"utf8",maxBuffer:8*1024*1024}); assert.equal(run.status,0,`${run.stdout??""}\n${run.stderr??""}`); const desktop=JSON.parse(fs.readFileSync(desktopPath,"utf8")); const engine=await factory({wasmBinary}); const handle=engine._web_engine_create(); open(engine,handle,fs.readFileSync(fixture)); const before=snapshot(engine,handle); const mesh=before.meshes?.find(v=>v.id===desktop.modifier.meshId); assert.ok(mesh); const modifier=mesh.modifierStack?.find(v=>v.type==="COLLISION"); assert.ok(modifier); assert.equal(modifier.name,desktop.modifier.modifier.name); assert.equal(modifier.parameters.typeCode,desktop.modifier.modifier.typeCode); assert.equal(modifier.showViewport,desktop.modifier.modifier.showViewport); assert.equal(modifier.showRender,desktop.modifier.modifier.showRender); const saved=output(engine,handle,engine._web_engine_save_blend,true), reopened=engine._web_engine_create(); open(engine,reopened,saved); assert.deepEqual(snapshot(engine,reopened).meshes?.find(v=>v.id===mesh.id)?.modifierStack,mesh.modifierStack); const malformed=new Uint8Array([0x42,0x4c,0x45,0x4e,0x44,0x45,0x52]), pointer=engine._malloc(malformed.byteLength); let result; try{engine.HEAPU8.set(malformed,pointer); result=engine._web_engine_open_blend(handle,pointer,malformed.byteLength)}finally{engine._free(pointer)} assert.notEqual(result,0); assert.deepEqual(snapshot(engine,handle),before); engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened); const report={schemaVersion:1,task:"M16-GAP-00037",operation:"COLLISION_MODIFIER_LOCAL_EXACT",fixture:{path:path.relative(root,fixture).replaceAll(path.sep,"/"),sha256:desktop.fixtureSha256},desktop:{status:"EXACT",report:path.relative(root,desktopPath).replaceAll(path.sep,"/"),saveReopen:desktop.saveReopen},wasm:{status:"EXACT",meshId:mesh.id,modifier},saveReopen:"EXACT",negative:{malformedBlend:"REJECTED_WITHOUT_MAIN_MUTATION"},nextTask:"M16-GAP-00038"}; const reportPath=path.join(root,"tests/golden/M16-GAP-00037/collision-modifier-local-exact-report.json"); fs.writeFileSync(reportPath,`${JSON.stringify(report,null,2)}\n`); process.stdout.write(`generated-gap-ok task=${task} mesh=${mesh.id} modifier=${modifier.name} desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
const genericModifierTasks = {
|
|
"M16-GAP-00038": { type: "CORRECTIVE_SMOOTH", stem: "WebGapCorrectiveSmooth", nextTask: "M16-GAP-00039" },
|
|
"M16-GAP-00039": { type: "CURVE", stem: "WebGapCurveModifier", nextTask: "M16-GAP-00040" },
|
|
"M16-GAP-00040": { type: "DATA_TRANSFER", stem: "WebGapDataTransfer", nextTask: "M16-GAP-00041" },
|
|
"M16-GAP-00041": { type: "DECIMATE", stem: "WebGapDecimate", nextTask: "M16-GAP-00042" },
|
|
"M16-GAP-00042": { type: "DISPLACE", stem: "WebGapDisplace", nextTask: "M16-GAP-00043" },
|
|
"M16-GAP-00043": { type: "DYNAMIC_PAINT", stem: "WebGapDynamicPaint", nextTask: "M16-GAP-00044" },
|
|
"M16-GAP-00044": { type: "EDGE_SPLIT", stem: "WebGapEdgeSplit", nextTask: "M16-GAP-00045" },
|
|
"M16-GAP-00045": { type: "EXPLODE", stem: "WebGapExplode", nextTask: "M16-GAP-00046" },
|
|
"M16-GAP-00046": { type: "FLUID", stem: "WebGapFluid", nextTask: "M16-GAP-00047" },
|
|
"M16-GAP-00047": { type: "GREASE_PENCIL_ARMATURE", stem: "WebGapGreasePencilArmature", objectName: "GreasePencilObject", nextTask: "M16-GAP-00048" },
|
|
"M16-GAP-00048": { type: "GREASE_PENCIL_ARRAY", stem: "WebGapGreasePencilArray", objectName: "GreasePencilObject", nextTask: "M16-GAP-00049" },
|
|
"M16-GAP-00049": { type: "GREASE_PENCIL_BUILD", stem: "WebGapGreasePencilBuild", objectName: "GreasePencilObject", nextTask: "M16-GAP-00050" },
|
|
"M16-GAP-00050": { type: "GREASE_PENCIL_COLOR", stem: "WebGapGreasePencilColor", objectName: "GreasePencilObject", nextTask: "M16-GAP-00051" },
|
|
"M16-GAP-00051": { type: "GREASE_PENCIL_DASH", stem: "WebGapGreasePencilDash", objectName: "GreasePencilObject", nextTask: "M16-GAP-00052" },
|
|
"M16-GAP-00052": { type: "GREASE_PENCIL_ENVELOPE", stem: "WebGapGreasePencilEnvelope", objectName: "GreasePencilObject", nextTask: "M16-GAP-00053" },
|
|
"M16-GAP-00053": { type: "GREASE_PENCIL_HOOK", stem: "WebGapGreasePencilHook", objectName: "GreasePencilObject", nextTask: "M16-GAP-00054" },
|
|
"M16-GAP-00054": { type: "GREASE_PENCIL_LATTICE", stem: "WebGapGreasePencilLattice", objectName: "GreasePencilObject", nextTask: "M16-GAP-00055" },
|
|
"M16-GAP-00055": { type: "GREASE_PENCIL_LENGTH", stem: "WebGapGreasePencilLength", objectName: "GreasePencilObject", nextTask: "M16-GAP-00056" },
|
|
"M16-GAP-00056": { type: "GREASE_PENCIL_MIRROR", stem: "WebGapGreasePencilMirror", objectName: "GreasePencilObject", nextTask: "M16-GAP-00057" },
|
|
"M16-GAP-00057": { type: "GREASE_PENCIL_MULTIPLY", stem: "WebGapGreasePencilMultiply", objectName: "GreasePencilObject", nextTask: "M16-GAP-00058" },
|
|
"M16-GAP-00058": { type: "GREASE_PENCIL_NOISE", stem: "WebGapGreasePencilNoise", objectName: "GreasePencilObject", nextTask: "M16-GAP-00059" },
|
|
"M16-GAP-00059": { type: "GREASE_PENCIL_OFFSET", stem: "WebGapGreasePencilOffset", objectName: "GreasePencilObject", nextTask: "M16-GAP-00060" },
|
|
"M16-GAP-00060": { type: "GREASE_PENCIL_OPACITY", stem: "WebGapGreasePencilOpacity", objectName: "GreasePencilObject", nextTask: "M16-GAP-00061" },
|
|
"M16-GAP-00061": { type: "GREASE_PENCIL_OUTLINE", stem: "WebGapGreasePencilOutline", objectName: "GreasePencilObject", nextTask: "M16-GAP-00062" },
|
|
"M16-GAP-00062": { type: "GREASE_PENCIL_SHRINKWRAP", stem: "WebGapGreasePencilShrinkwrap", objectName: "GreasePencilObject", nextTask: "M16-GAP-00063" },
|
|
"M16-GAP-00063": { type: "GREASE_PENCIL_SIMPLIFY", stem: "WebGapGreasePencilSimplify", objectName: "GreasePencilObject", nextTask: "M16-GAP-00064" },
|
|
"M16-GAP-00064": { type: "GREASE_PENCIL_SMOOTH", stem: "WebGapGreasePencilSmooth", objectName: "GreasePencilObject", nextTask: "M16-GAP-00065" },
|
|
"M16-GAP-00065": { type: "GREASE_PENCIL_SUBDIV", stem: "WebGapGreasePencilSubdiv", objectName: "GreasePencilObject", nextTask: "M16-GAP-00066" },
|
|
"M16-GAP-00066": { type: "GREASE_PENCIL_TEXTURE", stem: "WebGapGreasePencilTexture", objectName: "GreasePencilObject", nextTask: "M16-GAP-00067" },
|
|
"M16-GAP-00067": { type: "GREASE_PENCIL_THICKNESS", stem: "WebGapGreasePencilThickness", objectName: "GreasePencilObject", nextTask: "M16-GAP-00068" },
|
|
"M16-GAP-00068": { type: "GREASE_PENCIL_TIME", stem: "WebGapGreasePencilTime", objectName: "GreasePencilObject", nextTask: "M16-GAP-00069" },
|
|
"M16-GAP-00069": { type: "GREASE_PENCIL_TINT", stem: "WebGapGreasePencilTint", objectName: "GreasePencilObject", nextTask: "M16-GAP-00070" },
|
|
"M16-GAP-00070": { type: "GREASE_PENCIL_VERTEX_WEIGHT_ANGLE", stem: "WebGapGreasePencilVertexWeightAngle", objectName: "GreasePencilObject", nextTask: "M16-GAP-00071" },
|
|
"M16-GAP-00071": { type: "GREASE_PENCIL_VERTEX_WEIGHT_PROXIMITY", stem: "WebGapGreasePencilVertexWeightProximity", objectName: "GreasePencilObject", nextTask: "M16-GAP-00072" },
|
|
"M16-GAP-00072": { type: "HOOK", stem: "WebGapHook", nextTask: "M16-GAP-00073" },
|
|
"M16-GAP-00073": { type: "LAPLACIANDEFORM", stem: "WebGapLaplacianDeform", nextTask: "M16-GAP-00074" },
|
|
"M16-GAP-00074": { type: "LAPLACIANSMOOTH", stem: "WebGapLaplacianSmooth", nextTask: "M16-GAP-00075" },
|
|
};
|
|
if (genericModifierTasks[task]) {
|
|
const config = genericModifierTasks[task];
|
|
const fixture = path.join(root, `tests/files/web/generated/${task}-modifier-${config.type}.blend`);
|
|
const desktopPath = path.join(root, `tests/golden/${task}/${config.type.toLowerCase().replaceAll("_", "-")}-modifier-desktop-report.json`);
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const run = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-generic-modifier-desktop.py"), "--", fixture, desktopPath, task, config.type, config.objectName ?? `${config.stem}Object`], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
const engine = await factory({ wasmBinary }); const handle = engine._web_engine_create(); open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle); const mesh = desktop.modifier.meshId ? before.meshes?.find((value) => value.id === desktop.modifier.meshId) : null; const node = desktop.modifier.objectId ? before.nodes?.find((value) => value.id === desktop.modifier.objectId) : null; const holder = mesh ?? node; assert.ok(holder);
|
|
const modifier = holder.modifierStack?.find((value) => value.type === config.type); assert.ok(modifier); assert.equal(modifier.name, desktop.modifier.modifier.name); assert.equal(modifier.parameters.typeCode, desktop.modifier.modifier.typeCode); assert.equal(modifier.showViewport, desktop.modifier.modifier.showViewport); assert.equal(modifier.showRender, desktop.modifier.modifier.showRender); assert.equal(modifier.showEditMode, desktop.modifier.modifier.showEditMode); assert.equal(modifier.showOnCage, desktop.modifier.modifier.showOnCage);
|
|
if (config.type === "DECIMATE") {
|
|
assert.ok(Math.abs(modifier.parameters.ratio - desktop.modifier.modifier.ratio) < 1e-5);
|
|
assert.equal(modifier.parameters.iterations, desktop.modifier.modifier.iterations);
|
|
assert.ok(Math.abs(modifier.parameters.angleLimit - desktop.modifier.modifier.angleLimit) < 1e-5);
|
|
assert.equal(modifier.parameters.decimateMode, desktop.modifier.modifier.decimateMode);
|
|
}
|
|
if (config.type === "DISPLACE") {
|
|
assert.equal(modifier.parameters.direction, desktop.modifier.modifier.direction);
|
|
assert.ok(Math.abs(modifier.parameters.strength - desktop.modifier.modifier.strength) < 1e-5);
|
|
assert.ok(Math.abs(modifier.parameters.midLevel - desktop.modifier.modifier.midLevel) < 1e-5);
|
|
assert.equal(modifier.parameters.space, desktop.modifier.modifier.space);
|
|
assert.equal(modifier.parameters.textureMapping, desktop.modifier.modifier.textureMapping);
|
|
}
|
|
if (config.type === "EDGE_SPLIT") {
|
|
assert.ok(Math.abs(modifier.parameters.splitAngle - desktop.modifier.modifier.splitAngle) < 1e-5);
|
|
}
|
|
const saved = output(engine, handle, engine._web_engine_save_blend, true); const reopened = engine._web_engine_create(); open(engine, reopened, saved); const reopenedSnapshot = snapshot(engine, reopened); const reopenedHolder = mesh ? reopenedSnapshot.meshes?.find((value) => value.id === mesh.id) : reopenedSnapshot.nodes?.find((value) => value.id === node.id); assert.deepEqual(reopenedHolder?.modifierStack, holder.modifierStack);
|
|
const malformed = new Uint8Array([0x42,0x4c,0x45,0x4e,0x44,0x45,0x52]); const pointer = engine._malloc(malformed.byteLength); let result; try { engine.HEAPU8.set(malformed, pointer); result = engine._web_engine_open_blend(handle, pointer, malformed.byteLength); } finally { engine._free(pointer); } assert.notEqual(result, 0); assert.deepEqual(snapshot(engine, handle), before); engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: `${config.type}_MODIFIER_LOCAL_EXACT`, fixture: { path: path.relative(root, fixture).replaceAll(path.sep, "/"), sha256: desktop.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, desktopPath).replaceAll(path.sep, "/"), saveReopen: desktop.saveReopen }, wasm: { status: "EXACT", objectId: node?.id, meshId: mesh?.id, modifier }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: config.nextTask };
|
|
const reportPath = path.join(root, `tests/golden/${task}/${config.type.toLowerCase().replaceAll("_", "-")}-modifier-local-exact-report.json`); fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} holder=${holder.id} modifier=${modifier.name} desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00027") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00027-datablock-WindowManager.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00027/window-manager-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const run = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-window-manager-desktop.py"), "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
const engine = await factory({ wasmBinary }); const handle = engine._web_engine_create(); open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle); assert.deepEqual(before.windowManager, desktop.windowManager);
|
|
const saved = output(engine, handle, engine._web_engine_save_blend, true); const reopened = engine._web_engine_create(); open(engine, reopened, saved); assert.deepEqual(snapshot(engine, reopened).windowManager, before.windowManager);
|
|
const malformed = new Uint8Array([0x42, 0x4c, 0x45, 0x4e, 0x44, 0x45, 0x52]); const pointer = engine._malloc(malformed.byteLength); let result;
|
|
try { engine.HEAPU8.set(malformed, pointer); result = engine._web_engine_open_blend(handle, pointer, malformed.byteLength); } finally { engine._free(pointer); }
|
|
assert.notEqual(result, 0); assert.deepEqual(snapshot(engine, handle), before); engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task: "M16-GAP-00027", operation: "WINDOW_MANAGER_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, fixture).replaceAll(path.sep, "/"), sha256: desktop.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, desktopPath).replaceAll(path.sep, "/"), saveReopen: desktop.saveReopen }, wasm: { status: "EXACT", windowManager: before.windowManager }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00028" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00027/window-manager-local-exact-report.json"); fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} windowManager=${before.windowManager.id} desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task !== "M16-GAP-00001") { process.stdout.write(`generated-gap-blocked task=${task} reason=IMPLEMENTATION_NOT_STARTED next=${task}\n`); process.exit(0); }
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00001-datablock-Action.blend");
|
|
assert.equal(fs.existsSync(fixture), true, "Action fixture is required");
|
|
const bytes = fs.readFileSync(fixture);
|
|
const desktopReportPath = path.join(root, "tests/golden/M16-GAP-00001/action-desktop-report.json");
|
|
const desktop = spawnSync(process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"), ["-b", "--factory-startup", "--python", path.join(root, "tools/web/check-action-desktop.py"), "--", fixture, desktopReportPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(desktop.status, 0, `${desktop.stdout ?? ""}\n${desktop.stderr ?? ""}`);
|
|
const desktopEvidence = JSON.parse(fs.readFileSync(desktopReportPath, "utf8"));
|
|
assert.equal(desktopEvidence.saveReopen, "EXACT"); assert.equal(desktopEvidence.actions.length, 1); assert.equal(desktopEvidence.actions[0].name, "AnimatedObjectAction");
|
|
const engine = await factory({ wasmBinary }); const handle = engine._web_engine_create(); open(engine, handle, bytes); const before = snapshot(engine, handle); const animation = before.animations.find((item) => item.targetId === "object:AnimatedObject"); assert.ok(animation); assert.equal(animation.id, "action:AnimatedObjectAction:object:AnimatedObject"); assert.deepEqual(animation.channels.map((channel) => channel.path), ["location[0]", "location[1]", "location[2]"]); assert.ok(animation.channels.every((channel) => channel.keyframes.length === 2));
|
|
const saved = output(engine, handle, engine._web_engine_save_blend, true); assert.ok(saved.byteLength > 0); const reopened = engine._web_engine_create(); open(engine, reopened, saved); const after = snapshot(engine, reopened); assert.deepEqual(after.animations, before.animations); const report = { schemaVersion: 1, task: "M16-GAP-00001", operation: "ACTION_DATABLOCK_LOCAL_EXACT", fixture: { path: path.relative(root, fixture).replaceAll(path.sep, "/"), sha256: desktopEvidence.fixtureSha256 }, desktop: { status: "EXACT", report: path.relative(root, desktopReportPath).replaceAll(path.sep, "/"), saveReopen: desktopEvidence.saveReopen }, wasm: { status: "EXACT", actionId: animation.id, channelPaths: animation.channels.map((channel) => channel.path), keyframesPerChannel: animation.channels.map((channel) => channel.keyframes.length) }, saveReopen: "EXACT", nextTask: "M16-GAP-00002" }; const reportPath = path.join(root, "tests/golden/M16-GAP-00001/action-local-exact-report.json"); fs.mkdirSync(path.dirname(reportPath), { recursive: true }); fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} channels=${animation.channels.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|