8544 lines
809 KiB
JavaScript
8544 lines
809 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" },
|
|
"M16-GAP-00075": { type: "LATTICE", stem: "WebGapLatticeModifier", nextTask: "M16-GAP-00076" },
|
|
"M16-GAP-00076": { type: "LINEART", stem: "WebGapLineart", objectName: "GreasePencilObject", nextTask: "M16-GAP-00077" },
|
|
"M16-GAP-00077": { type: "MASK", stem: "WebGapMask", nextTask: "M16-GAP-00078" },
|
|
"M16-GAP-00078": { type: "MESH_CACHE", stem: "WebGapMeshCache", nextTask: "M16-GAP-00079" },
|
|
"M16-GAP-00079": { type: "MESH_DEFORM", stem: "WebGapMeshDeform", nextTask: "M16-GAP-00080" },
|
|
"M16-GAP-00080": { type: "MESH_SEQUENCE_CACHE", stem: "WebGapMeshSequenceCache", nextTask: "M16-GAP-00081" },
|
|
"M16-GAP-00081": { type: "MESH_TO_VOLUME", stem: "WebGapMeshToVolume", nextTask: "M16-GAP-00082" },
|
|
"M16-GAP-00082": { type: "MIRROR", stem: "WebGapMirror", nextTask: "M16-GAP-00083" },
|
|
"M16-GAP-00083": { type: "MULTIRES", stem: "WebGapMultires", nextTask: "M16-GAP-00084" },
|
|
"M16-GAP-00084": { type: "NODES", stem: "WebGapNodes", nextTask: "M16-GAP-00085" },
|
|
"M16-GAP-00085": { type: "NORMAL_EDIT", stem: "WebGapNormalEdit", nextTask: "M16-GAP-00086" },
|
|
"M16-GAP-00086": { type: "OCEAN", stem: "WebGapOcean", nextTask: "M16-GAP-00087" },
|
|
"M16-GAP-00087": { type: "PARTICLE_INSTANCE", stem: "WebGapParticleInstance", nextTask: "M16-GAP-00088" },
|
|
"M16-GAP-00088": { type: "PARTICLE_SYSTEM", stem: "WebGapParticleSystem", nextTask: "M16-GAP-00089" },
|
|
"M16-GAP-00089": { type: "REMESH", stem: "WebGapRemesh", nextTask: "M16-GAP-00090" },
|
|
"M16-GAP-00090": { type: "SCREW", stem: "WebGapScrew", nextTask: "M16-GAP-00091" },
|
|
"M16-GAP-00091": { type: "SHRINKWRAP", stem: "WebGapShrinkwrap", nextTask: "M16-GAP-00092" },
|
|
"M16-GAP-00092": { type: "SIMPLE_DEFORM", stem: "WebGapSimpleDeform", nextTask: "M16-GAP-00093" },
|
|
"M16-GAP-00093": { type: "SKIN", stem: "WebGapSkin", nextTask: "M16-GAP-00094" },
|
|
"M16-GAP-00094": { type: "SMOOTH", stem: "WebGapSmooth", nextTask: "M16-GAP-00095" },
|
|
"M16-GAP-00095": { type: "SOFT_BODY", stem: "WebGapSoftBody", nextTask: "M16-GAP-00096" },
|
|
"M16-GAP-00096": { type: "SOLIDIFY", stem: "WebGapSolidify", nextTask: "M16-GAP-00097" },
|
|
"M16-GAP-00097": { type: "SUBSURF", stem: "WebGapSubsurf", nextTask: "M16-GAP-00098" },
|
|
"M16-GAP-00098": { type: "SURFACE", stem: "WebGapSurface", nextTask: "M16-GAP-00099" },
|
|
"M16-GAP-00099": { type: "SURFACE_DEFORM", stem: "WebGapSurfaceDeform", nextTask: "M16-GAP-00100" },
|
|
"M16-GAP-00100": { type: "TRIANGULATE", stem: "WebGapTriangulate", nextTask: "M16-GAP-00101" },
|
|
"M16-GAP-00101": { type: "UV_PROJECT", stem: "WebGapUvProject", nextTask: "M16-GAP-00102" },
|
|
"M16-GAP-00102": { type: "UV_WARP", stem: "WebGapUvWarp", nextTask: "M16-GAP-00103" },
|
|
"M16-GAP-00103": { type: "VERTEX_WEIGHT_EDIT", stem: "WebGapVertexWeightEdit", nextTask: "M16-GAP-00104" },
|
|
"M16-GAP-00104": { type: "VERTEX_WEIGHT_MIX", stem: "WebGapVertexWeightMix", nextTask: "M16-GAP-00105" },
|
|
"M16-GAP-00105": { type: "VERTEX_WEIGHT_PROXIMITY", stem: "WebGapVertexWeightProximity", nextTask: "M16-GAP-00106" },
|
|
"M16-GAP-00106": { type: "VOLUME_DISPLACE", stem: "WebGapVolumeDisplace", nextTask: "M16-GAP-00107" },
|
|
"M16-GAP-00107": { type: "VOLUME_TO_MESH", stem: "WebGapVolumeToMesh", nextTask: "M16-GAP-00108" },
|
|
"M16-GAP-00108": { type: "WARP", stem: "WebGapWarp", nextTask: "M16-GAP-00109" },
|
|
"M16-GAP-00109": { type: "WAVE", stem: "WebGapWave", nextTask: "M16-GAP-00110" },
|
|
"M16-GAP-00110": { type: "WEIGHTED_NORMAL", stem: "WebGapWeightedNormal", nextTask: "M16-GAP-00111" },
|
|
"M16-GAP-00111": { type: "WELD", stem: "WebGapWeld", nextTask: "M16-GAP-00112" },
|
|
"M16-GAP-00112": { type: "WIREFRAME", stem: "WebGapWireframe", nextTask: "M16-GAP-00113" },
|
|
};
|
|
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);
|
|
}
|
|
if (config.type === "SMOOTH") {
|
|
assert.ok(Math.abs(modifier.parameters.factor - desktop.modifier.modifier.factor) < 1e-5);
|
|
assert.equal(modifier.parameters.repeat, desktop.modifier.modifier.iterations);
|
|
assert.equal(modifier.parameters.vertexGroup, desktop.modifier.modifier.vertexGroup);
|
|
assert.equal(modifier.parameters.smoothFlags, desktop.modifier.modifier.smoothFlags);
|
|
}
|
|
if (config.type === "SOLIDIFY") {
|
|
assert.ok(Math.abs(modifier.parameters.thickness - desktop.modifier.modifier.thickness) < 1e-5);
|
|
assert.ok(Math.abs(modifier.parameters.offset - desktop.modifier.modifier.offset) < 1e-5);
|
|
}
|
|
if (config.type === "SUBSURF") {
|
|
assert.equal(modifier.parameters.subdivisionType, desktop.modifier.modifier.subdivisionType);
|
|
assert.equal(modifier.parameters.levels, desktop.modifier.modifier.levels);
|
|
assert.equal(modifier.parameters.renderLevels, desktop.modifier.modifier.renderLevels);
|
|
}
|
|
if (config.type === "TRIANGULATE") {
|
|
assert.equal(modifier.parameters.quadMethod, desktop.modifier.modifier.quadMethod);
|
|
assert.equal(modifier.parameters.ngonMethod, desktop.modifier.modifier.ngonMethod);
|
|
}
|
|
if (config.type === "WAVE") {
|
|
assert.ok(Math.abs(modifier.parameters.height - desktop.modifier.modifier.height) < 1e-5);
|
|
assert.ok(Math.abs(modifier.parameters.width - desktop.modifier.modifier.width) < 1e-5);
|
|
assert.ok(Math.abs(modifier.parameters.speed - desktop.modifier.modifier.speed) < 1e-5);
|
|
}
|
|
if (config.type === "WEIGHTED_NORMAL") {
|
|
assert.equal(modifier.parameters.weight, desktop.modifier.modifier.weight);
|
|
}
|
|
if (config.type === "WELD") {
|
|
assert.ok(Math.abs(modifier.parameters.mergeThreshold - desktop.modifier.modifier.mergeThreshold) < 1e-5);
|
|
}
|
|
if (config.type === "WIREFRAME") {
|
|
assert.ok(Math.abs(modifier.parameters.thickness - desktop.modifier.modifier.thickness) < 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-00113") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00113-operator-action.bake_keys.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00113/action-bake-keys-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-action-bake-keys-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapBakeKeysObjectAction:object:WebGapBakeKeysObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
for (const [index, channel] of animation.channels.entries()) {
|
|
const expected = desktop.action.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.frame), expected.frames);
|
|
}
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.length), [5, 5, 5]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_BAKE_KEYS_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", actionId: animation.id, channelPaths: animation.channels.map((channel) => channel.path), keyframesPerChannel: animation.channels.map((channel) => channel.keyframes.length) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00114" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00113/action-bake-keys-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} channels=${animation.channels.length} bakedFrames=5 desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00114") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00114-operator-action.clean.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00114/action-clean-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-action-clean-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapCleanObjectAction:object:WebGapCleanObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
for (const [index, channel] of animation.channels.entries()) {
|
|
const expected = desktop.action.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.frame), expected.frames);
|
|
}
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.length), [1, 1, 1]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_CLEAN_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", actionId: animation.id, threshold: desktop.threshold, keyframesPerChannel: animation.channels.map((channel) => channel.keyframes.length) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00115" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00114/action-clean-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} channels=${animation.channels.length} remainingFrames=1 desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00115") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00115-operator-action.clickselect.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00115/action-clickselect-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-action-clickselect-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapClickSelectObjectAction:object:WebGapClickSelectObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
for (const [index, channel] of animation.channels.entries()) {
|
|
const expected = desktop.action.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.frame), expected.frames);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.selected), expected.selected);
|
|
}
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.filter((value) => value.selected).map((value) => value.frame)), [[3], [3], [3]]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_CLICKSELECT_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", actionId: animation.id, selectedFrames: animation.channels.map((channel) => channel.keyframes.filter((value) => value.selected).map((value) => value.frame)) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00116" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00115/action-clickselect-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} selectedFrame=3 desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00116") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00116-operator-action.copy.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00116/action-copy-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-action-copy-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "NONE");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapCopyObjectAction:object:WebGapCopyObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
for (const [index, channel] of animation.channels.entries()) {
|
|
const expected = desktop.action.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.frame), expected.frames);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.selected), expected.selected);
|
|
}
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.filter((value) => value.selected).map((value) => value.frame)), [[3], [3], [3]]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_COPY_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, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", actionId: animation.id, copiedFrames: [3], selectedFrames: animation.channels.map((channel) => channel.keyframes.filter((value) => value.selected).map((value) => value.frame)) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00117" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00116/action-copy-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} copiedFrame=3 mainMutation=none desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00117") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00117-operator-action.delete.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00117/action-delete-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-action-delete-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapDeleteObjectAction:object:WebGapDeleteObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
for (const [index, channel] of animation.channels.entries()) {
|
|
const expected = desktop.action.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.frame), expected.frames);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.selected), expected.selected);
|
|
}
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)), [[1, 5], [1, 5], [1, 5]]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_DELETE_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", actionId: animation.id, deletedFrames: [3], remainingFrames: animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00118" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00117/action-delete-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} deletedFrame=3 remainingFrames=1,5 desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00118") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00118-operator-action.duplicate.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00118/action-duplicate-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-action-duplicate-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapDuplicateObjectAction:object:WebGapDuplicateObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
for (const [index, channel] of animation.channels.entries()) {
|
|
const expected = desktop.action.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.frame), expected.frames);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.selected), expected.selected);
|
|
}
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)), [[1, 3, 3, 5], [1, 3, 3, 5], [1, 3, 3, 5]]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_DUPLICATE_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", actionId: animation.id, duplicatedFrames: [3], keyframesPerChannel: animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00119" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00118/action-duplicate-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} duplicatedFrame=3 frames=1,3,3,5 desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00119") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00119-operator-action.duplicate_move.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00119/action-duplicate-move-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-action-duplicate-move-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapDuplicateMoveObjectAction:object:WebGapDuplicateMoveObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
for (const [index, channel] of animation.channels.entries()) {
|
|
const expected = desktop.action.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.frame), expected.frames);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.selected), expected.selected);
|
|
}
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)), [[1, 3, 5], [1, 3, 5], [1, 3, 5]]);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.filter((value) => value.selected).map((value) => value.frame)), [[5], [5], [5]]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_DUPLICATE_MOVE_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", actionId: animation.id, sourceFrame: 3, movedFrame: 5, timeOffset: 2, selectedFrames: animation.channels.map((channel) => channel.keyframes.filter((value) => value.selected).map((value) => value.frame)) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00120" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00119/action-duplicate-move-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} sourceFrame=3 movedFrame=5 offset=2 desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00120") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00120-operator-action.easing_type.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00120/action-easing-type-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-action-easing-type-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapEasingTypeObjectAction:object:WebGapEasingTypeObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
for (const [index, channel] of animation.channels.entries()) {
|
|
const expected = desktop.action.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.frame), expected.frames);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.easing), expected.easing);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.selected), expected.selected);
|
|
}
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.easing)), [["AUTO", "EASE_IN_OUT", "AUTO"], ["AUTO", "EASE_IN_OUT", "AUTO"], ["AUTO", "EASE_IN_OUT", "AUTO"]]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_EASING_TYPE_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", actionId: animation.id, easingType: "EASE_IN_OUT", easingPerChannel: animation.channels.map((channel) => channel.keyframes.map((value) => value.easing)) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00121" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00120/action-easing-type-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} easing=EASE_IN_OUT frame=3 desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00121") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00121-operator-action.extrapolation_type.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00121/action-extrapolation-type-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-action-extrapolation-type-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapExtrapolationTypeObjectAction:object:WebGapExtrapolationTypeObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
for (const [index, channel] of animation.channels.entries()) {
|
|
const expected = desktop.action.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.equal(channel.extrapolation, expected.extrapolation);
|
|
}
|
|
assert.deepEqual(animation.channels.map((channel) => channel.extrapolation), ["LINEAR", "LINEAR", "LINEAR"]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_EXTRAPOLATION_TYPE_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", actionId: animation.id, extrapolationType: "LINEAR", extrapolationPerChannel: animation.channels.map((channel) => channel.extrapolation) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00122" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00121/action-extrapolation-type-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} extrapolation=LINEAR desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00122") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00122-operator-action.frame_jump.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00122/action-frame-jump-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-action-frame-jump-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
assert.equal(before.frame.current, desktop.jumpedToFrame);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapFrameJumpObjectAction:object:WebGapFrameJumpObject");
|
|
assert.ok(animation);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.filter((value) => value.selected).map((value) => value.frame)), [[5], [5], [5]]);
|
|
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).frame, before.frame);
|
|
assert.deepEqual(snapshot(engine, reopened).animations, before.animations);
|
|
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).frame, before.frame);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_FRAME_JUMP_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", actionId: animation.id, jumpedToFrame: before.frame.current, selectedFrames: animation.channels.map((channel) => channel.keyframes.filter((value) => value.selected).map((value) => value.frame)) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00123" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00122/action-frame-jump-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} currentFrame=${before.frame.current} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00123") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00123-operator-action.handle_type.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00123/action-handle-type-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-action-handle-type-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapHandleTypeObjectAction:object:WebGapHandleTypeObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
for (const [index, channel] of animation.channels.entries()) {
|
|
const expected = desktop.action.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.frame), expected.frames);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.handleType), expected.handleType);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.selected), expected.selected);
|
|
}
|
|
const handleTypes = animation.channels.map((channel) => channel.keyframes.map((value) => value.handleType));
|
|
assert.deepEqual(handleTypes, [["VECTOR", "VECTOR", "VECTOR"], ["VECTOR", "VECTOR", "VECTOR"], ["VECTOR", "VECTOR", "VECTOR"]]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_HANDLE_TYPE_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", actionId: animation.id, handleType: "VECTOR", handleTypes }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00124" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00123/action-handle-type-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} handle=VECTOR channels=${animation.channels.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00124") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00124-operator-action.interpolation_type.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00124/action-interpolation-type-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-action-interpolation-type-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapInterpolationTypeObjectAction:object:WebGapInterpolationTypeObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
for (const [index, channel] of animation.channels.entries()) {
|
|
const expected = desktop.action.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.frame), expected.frames);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.interpolation), expected.interpolation);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.selected), expected.selected);
|
|
}
|
|
const interpolationPerChannel = animation.channels.map((channel) => channel.keyframes.map((value) => value.interpolation));
|
|
assert.deepEqual(interpolationPerChannel, [["BEZIER", "LINEAR", "BEZIER"], ["BEZIER", "LINEAR", "BEZIER"], ["BEZIER", "LINEAR", "BEZIER"]]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_INTERPOLATION_TYPE_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", actionId: animation.id, interpolationType: "LINEAR", interpolationPerChannel }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00125" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00124/action-interpolation-type-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} interpolation=LINEAR frame=3 desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00125") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00125-operator-action.keyframe_insert.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00125/action-keyframe-insert-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-action-keyframe-insert-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapKeyframeInsertObjectAction:object:WebGapKeyframeInsertObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
for (const [index, channel] of animation.channels.entries()) {
|
|
const expected = desktop.action.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.frame), expected.frames);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.value[0]), expected.values);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.selected), expected.selected);
|
|
}
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)), [[1, 3, 5], [1, 3, 5], [1, 3, 5]]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_KEYFRAME_INSERT_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", actionId: animation.id, insertedFrame: 3, framesPerChannel: animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00126" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00125/action-keyframe-insert-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} insertedFrame=3 channels=${animation.channels.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00126") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00126-operator-action.keyframe_type.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00126/action-keyframe-type-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-action-keyframe-type-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapKeyframeTypeObjectAction:object:WebGapKeyframeTypeObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
for (const [index, channel] of animation.channels.entries()) {
|
|
const expected = desktop.action.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.frame), expected.frames);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.keyframeType), expected.keyframeType);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.selected), expected.selected);
|
|
}
|
|
const keyframeTypes = animation.channels.map((channel) => channel.keyframes.map((value) => value.keyframeType));
|
|
assert.deepEqual(keyframeTypes, [["KEYFRAME", "BREAKDOWN", "KEYFRAME"], ["KEYFRAME", "BREAKDOWN", "KEYFRAME"], ["KEYFRAME", "BREAKDOWN", "KEYFRAME"]]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_KEYFRAME_TYPE_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", actionId: animation.id, keyframeType: "BREAKDOWN", keyframeTypes }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00127" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00126/action-keyframe-type-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} keyframeType=BREAKDOWN frame=3 desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00127") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00127-operator-action.markers_make_local.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00127/action-markers-make-local-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-action-markers-make-local-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapMarkersMakeLocalObjectAction:object:WebGapMarkersMakeLocalObject");
|
|
assert.ok(animation);
|
|
assert.deepEqual(animation.markers, desktop.action.markers);
|
|
assert.deepEqual(animation.markers, [{ name: "LocalActionMarker", frame: 3 }]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_MARKERS_MAKE_LOCAL_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", actionId: animation.id, markers: animation.markers }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00128" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00127/action-markers-make-local-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} markers=1 frame=3 desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00128") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00128-operator-action.mirror.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00128/action-mirror-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-action-mirror-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapMirrorObjectAction:object:WebGapMirrorObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
for (const [index, channel] of animation.channels.entries()) {
|
|
const expected = desktop.action.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.frame), expected.frames);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.value[0]), expected.values);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.selected), expected.selected);
|
|
}
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.value[0])), [[-1, -2, -4], [-2, -4, -8], [-3, -6, -12]]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_MIRROR_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", actionId: animation.id, mirrorType: "XAXIS", valuesPerChannel: animation.channels.map((channel) => channel.keyframes.map((value) => value.value[0])) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00129" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00128/action-mirror-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} mirror=XAXIS desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00129") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00129-operator-action.new.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00129/action-new-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-action-new-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === `action:${desktop.action.name}:object:WebGapActionNewObject`);
|
|
assert.ok(animation);
|
|
assert.equal(animation.name, desktop.action.name);
|
|
assert.equal(animation.channels.length, desktop.action.channels);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_NEW_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", actionId: animation.id, actionName: animation.name, channels: animation.channels.length }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00130" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00129/action-new-local-exact-report.json");
|
|
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`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00130") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00130-operator-action.paste.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00130/action-paste-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-action-paste-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapPasteObjectAction:object:WebGapPasteObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
for (const [index, channel] of animation.channels.entries()) {
|
|
const expected = desktop.action.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.frame), expected.frames);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.value[0]), expected.values);
|
|
}
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)), [[1, 3, 5, 7], [1, 3, 5, 7], [1, 3, 5, 7]]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_PASTE_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", actionId: animation.id, pastedFrame: 7, framesPerChannel: animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00131" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00130/action-paste-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} pastedFrame=7 channels=${animation.channels.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00131") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00131-operator-action.previewrange_set.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00131/action-previewrange-set-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-action-previewrange-set-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const scene = before.scenes?.find((value) => value.id === before.sceneId) ?? before.scenes?.[0];
|
|
assert.ok(scene);
|
|
assert.deepEqual(scene.previewRange, { start: 1, end: 5 });
|
|
assert.deepEqual(scene.previewRange, desktop.previewRange);
|
|
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).scenes?.find((value) => value.id === before.sceneId) ?? snapshot(engine, reopened).scenes?.[0]).previewRange, scene.previewRange);
|
|
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).scenes?.find((value) => value.id === before.sceneId) ?? snapshot(engine, handle).scenes?.[0]).previewRange, scene.previewRange);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_PREVIEWRANGE_SET_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", sceneId: scene.id, previewRange: scene.previewRange }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00132" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00131/action-previewrange-set-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} scene=${scene.id} preview=1-5 desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00132") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00132-operator-action.push_down.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00132/action-push-down-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-action-push-down-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const action = before.animations?.find((value) => value.id === "action:WebGapPushDownObjectAction:object:WebGapPushDownObject");
|
|
assert.ok(action);
|
|
const tracks = before.nlaTracks?.filter((value) => value.ownerId === "object:WebGapPushDownObject") ?? [];
|
|
assert.equal(tracks.length, desktop.nla.tracks.length);
|
|
assert.equal(tracks.length, 1);
|
|
assert.equal(tracks[0].strips.length, 1);
|
|
assert.equal(tracks[0].strips[0].actionId, action.id);
|
|
assert.equal(before.animations.some((value) => value.id === action.id), true);
|
|
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).nlaTracks, before.nlaTracks);
|
|
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).nlaTracks, before.nlaTracks);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_PUSH_DOWN_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", actionId: action.id, nlaTracks: tracks }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00133" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00132/action-push-down-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${action.id} nlaTracks=1 strips=1 desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00133") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00133-operator-action.select_all.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00133/action-select-all-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-action-select-all-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapSelectAllObjectAction:object:WebGapSelectAllObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
for (const [index, channel] of animation.channels.entries()) {
|
|
const expected = desktop.action.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.frame), expected.frames);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.selected), expected.selected);
|
|
}
|
|
assert.ok(animation.channels.every((channel) => channel.keyframes.every((value) => value.selected === true)));
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_SELECT_ALL_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", actionId: animation.id, selected: true, channels: animation.channels.length }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00134" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00133/action-select-all-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} selected=all channels=${animation.channels.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00134") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00134-operator-action.select_box.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00134/action-select-box-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-action-select-box-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapSelectBoxObjectAction:object:WebGapSelectBoxObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
for (const [index, channel] of animation.channels.entries()) {
|
|
const expected = desktop.action.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.frame), expected.frames);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.selected), expected.selected);
|
|
}
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.filter((value) => value.selected).map((value) => value.frame)), [[3], [3], [3]]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_SELECT_BOX_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", actionId: animation.id, selectedFrame: 3, selectedPerChannel: animation.channels.map((channel) => channel.keyframes.filter((value) => value.selected).map((value) => value.frame)) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00135" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00134/action-select-box-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} selectedFrame=3 channels=${animation.channels.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00135") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00135-operator-action.select_by_type.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00135/action-select-by-type-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-action-select-by-type-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapSelectByTypeObjectAction:object:WebGapSelectByTypeObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
for (const [index, channel] of animation.channels.entries()) {
|
|
const expected = desktop.action.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.keyframeType), expected.keyframeType);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.selected), expected.selected);
|
|
}
|
|
assert.ok(animation.channels.every((channel) => channel.keyframes[1].selected && channel.keyframes[1].keyframeType === "BREAKDOWN"));
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_SELECT_BY_TYPE_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", actionId: animation.id, selectedType: "BREAKDOWN", selectedPerChannel: animation.channels.map((channel) => channel.keyframes.filter((value) => value.selected).map((value) => value.frame)) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00136" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00135/action-select-by-type-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} type=BREAKDOWN selectedFrame=3 desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00136") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00136-operator-action.select_circle.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00136/action-select-circle-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-action-select-circle-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapSelectCircleObjectAction:object:WebGapSelectCircleObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
for (const [index, channel] of animation.channels.entries()) {
|
|
const expected = desktop.action.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.frame), expected.frames);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.selected), expected.selected);
|
|
}
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.filter((value) => value.selected).map((value) => value.frame)), [[3], [3], [3]]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_SELECT_CIRCLE_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", actionId: animation.id, selectedFrame: 3, selectedPerChannel: animation.channels.map((channel) => channel.keyframes.filter((value) => value.selected).map((value) => value.frame)) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00137" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00136/action-select-circle-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} selectedFrame=3 channels=${animation.channels.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00137") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00137-operator-action.select_column.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00137/action-select-column-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-action-select-column-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapSelectColumnObjectAction:object:WebGapSelectColumnObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
for (const [index, channel] of animation.channels.entries()) {
|
|
const expected = desktop.action.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.frame), expected.frames);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.selected), expected.selected);
|
|
}
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.filter((value) => value.selected).map((value) => value.frame)), [[3], [3], [3]]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_SELECT_COLUMN_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", actionId: animation.id, selectedFrame: 3, selectedPerChannel: animation.channels.map((channel) => channel.keyframes.filter((value) => value.selected).map((value) => value.frame)) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00138" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00137/action-select-column-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} selectedFrame=3 channels=${animation.channels.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00138") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00138-operator-action.select_lasso.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00138/action-select-lasso-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-action-select-lasso-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapSelectLassoObjectAction:object:WebGapSelectLassoObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
for (const [index, channel] of animation.channels.entries()) {
|
|
const expected = desktop.action.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.frame), expected.frames);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.selected), expected.selected);
|
|
}
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.filter((value) => value.selected).map((value) => value.frame)), [[3], [3], [3]]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_SELECT_LASSO_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", actionId: animation.id, selectedFrame: 3, selectedPerChannel: animation.channels.map((channel) => channel.keyframes.filter((value) => value.selected).map((value) => value.frame)) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00139" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00138/action-select-lasso-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} selectedFrame=3 channels=${animation.channels.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00139") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00139-operator-action.select_leftright.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00139/action-select-leftright-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-action-select-leftright-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mode, "LEFT");
|
|
assert.deepEqual(desktop.selectedFrames, [1, 3]);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapSelectLeftRightObjectAction:object:WebGapSelectLeftRightObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
for (const [index, channel] of animation.channels.entries()) {
|
|
const expected = desktop.action.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.frame), expected.frames);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.selected), expected.selected);
|
|
}
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.filter((value) => value.selected).map((value) => value.frame)), [[1, 3], [1, 3], [1, 3]]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_SELECT_LEFTRIGHT_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", actionId: animation.id, mode: "LEFT", selectedFrames: [1, 3], selectedPerChannel: animation.channels.map((channel) => channel.keyframes.filter((value) => value.selected).map((value) => value.frame)) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00140" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00139/action-select-leftright-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} mode=left selectedFrames=1,3 channels=${animation.channels.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00140") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00140-operator-action.select_less.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00140/action-select-less-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-action-select-less-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.selectedFrame, 3);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapSelectLessObjectAction:object:WebGapSelectLessObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
for (const [index, channel] of animation.channels.entries()) {
|
|
const expected = desktop.action.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.frame), expected.frames);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.selected), expected.selected);
|
|
}
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.filter((value) => value.selected).map((value) => value.frame)), [[3], [3], [3]]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_SELECT_LESS_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", actionId: animation.id, selectedFrame: 3, selectedPerChannel: animation.channels.map((channel) => channel.keyframes.filter((value) => value.selected).map((value) => value.frame)) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00141" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00140/action-select-less-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} selectedFrame=3 channels=${animation.channels.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00141") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00141-operator-action.select_linked.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00141/action-select-linked-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-action-select-linked-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.selectedChannel, "location[0]");
|
|
assert.deepEqual(desktop.selectedFrames, [1, 3, 5]);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapSelectLinkedObjectAction:object:WebGapSelectLinkedObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
for (const [index, channel] of animation.channels.entries()) {
|
|
const expected = desktop.action.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.frame), expected.frames);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.selected), expected.selected);
|
|
}
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.filter((value) => value.selected).map((value) => value.frame)), [[1, 3, 5], [1, 3, 5], [1, 3, 5]]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_SELECT_LINKED_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", actionId: animation.id, selectedChannel: "location[0]", selectedFrames: [1, 3, 5], selectedPerChannel: animation.channels.map((channel) => channel.keyframes.filter((value) => value.selected).map((value) => value.frame)) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00142" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00141/action-select-linked-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} selectedChannel=location[0] selectedFrames=1,3,5 channels=${animation.channels.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00142") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00142-operator-action.select_more.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00142/action-select-more-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-action-select-more-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.selectedChannel, "location[0]");
|
|
assert.deepEqual(desktop.selectedPerChannel, [[1, 3, 5], [1, 3, 5], [1, 3, 5]]);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapSelectMoreObjectAction:object:WebGapSelectMoreObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
for (const [index, channel] of animation.channels.entries()) {
|
|
const expected = desktop.action.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.frame), expected.frames);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.selected), expected.selected);
|
|
}
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.filter((value) => value.selected).map((value) => value.frame)), [[1, 3, 5], [1, 3, 5], [1, 3, 5]]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_SELECT_MORE_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", actionId: animation.id, selectedChannel: "location[0]", selectedPerChannel: [[1, 3, 5], [1, 3, 5], [1, 3, 5]] }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00143" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00142/action-select-more-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} selectedChannel=location[0] selectedPerChannel=1,3,5|1,3,5|1,3,5 channels=${animation.channels.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00143") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00143-operator-action.snap.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00143/action-snap-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-action-snap-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.type, "CFRA");
|
|
assert.equal(desktop.currentFrame, 4);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapSnapObjectAction:object:WebGapSnapObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
for (const [index, channel] of animation.channels.entries()) {
|
|
const expected = desktop.action.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.frame), expected.frames);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.selected), expected.selected);
|
|
}
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)), [[1, 4, 5], [1, 4, 5], [1, 4, 5]]);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.filter((value) => value.selected).map((value) => value.frame)), [[4], [4], [4]]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_SNAP_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", actionId: animation.id, type: "CFRA", currentFrame: 4, framesPerChannel: [[1, 4, 5], [1, 4, 5], [1, 4, 5]], selectedPerChannel: [[4], [4], [4]] }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00144" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00143/action-snap-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} type=CFRA currentFrame=4 channels=${animation.channels.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00144") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00144-operator-action.stash.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00144/action-stash-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-action-stash-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.nla.activeAction, null);
|
|
assert.equal(desktop.nla.tracks.length, 1);
|
|
assert.equal(desktop.nla.tracks[0].strips.length, 1);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const action = before.animations?.find((value) => value.id === "action:WebGapStashObjectAction:object:WebGapStashObject");
|
|
assert.ok(action);
|
|
const tracks = before.nlaTracks?.filter((value) => value.ownerId === "object:WebGapStashObject") ?? [];
|
|
assert.equal(tracks.length, desktop.nla.tracks.length);
|
|
assert.equal(tracks.length, 1);
|
|
assert.equal(tracks[0].strips.length, 1);
|
|
assert.equal(tracks[0].strips[0].actionId, action.id);
|
|
assert.equal(before.animations.some((value) => value.id === action.id), true);
|
|
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).nlaTracks, before.nlaTracks);
|
|
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).nlaTracks, before.nlaTracks);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_STASH_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", actionId: action.id, nlaTracks: tracks }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00145" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00144/action-stash-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${action.id} nlaTracks=1 strips=1 desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00145") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00145-operator-action.stash_and_create.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00145/action-stash-and-create-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-action-stash-and-create-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.nla.activeAction, "Action");
|
|
assert.equal(desktop.nla.tracks.length, 1);
|
|
assert.equal(desktop.nla.tracks[0].strips.length, 1);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const stashed = before.animations?.find((value) => value.id === "action:WebGapStashCreateObjectAction:object:WebGapStashCreateObject");
|
|
assert.ok(stashed);
|
|
const tracks = before.nlaTracks?.filter((value) => value.ownerId === "object:WebGapStashCreateObject") ?? [];
|
|
assert.equal(tracks.length, 1);
|
|
assert.equal(tracks[0].strips.length, 1);
|
|
assert.equal(tracks[0].strips[0].actionId, stashed.id);
|
|
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).nlaTracks, before.nlaTracks);
|
|
assert.deepEqual(snapshot(engine, reopened).animations, before.animations);
|
|
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).nlaTracks, before.nlaTracks);
|
|
engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_STASH_AND_CREATE_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", activeActionId: null, stashedActionId: stashed.id, nlaTracks: tracks }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00146" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00145/action-stash-and-create-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} activeAction=empty stashedAction=${stashed.id} nlaTracks=1 strips=1 desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00146") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00146-operator-action.unlink.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00146/action-unlink-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-action-unlink-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.action.activeAction, null);
|
|
assert.equal(desktop.action.unlinkedAction.name, "WebGapActionUnlinkObjectAction");
|
|
assert.equal(desktop.action.unlinkedAction.users, 1);
|
|
assert.equal(desktop.action.unlinkedAction.useFakeUser, true);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const action = before.animations?.find((value) => value.unlinked === true);
|
|
assert.ok(action);
|
|
assert.equal(action.name, desktop.action.unlinkedAction.name);
|
|
assert.equal(action.targetId, "unlinked");
|
|
assert.equal(action.channels.length, desktop.action.unlinkedAction.channels.length);
|
|
for (const [index, channel] of action.channels.entries()) {
|
|
const expected = desktop.action.unlinkedAction.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.equal(Number(channel.path.match(/\[(\d+)\]$/)?.[1] ?? 0), expected.index);
|
|
assert.deepEqual(channel.keyframes.map((keyframe) => keyframe.frame), expected.frames);
|
|
}
|
|
assert.deepEqual(action.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.frame)), [[1, 3, 5], [1, 3, 5], [1, 3, 5]]);
|
|
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).animations, before.animations);
|
|
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: "ACTION_UNLINK_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", actionId: action.id, targetId: action.targetId, unlinked: action.unlinked, keyframesPerChannel: action.channels.map((channel) => channel.keyframes.length) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00147" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00146/action-unlink-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${action.id} unlinked=true channels=${action.channels.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00147") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00147-operator-action.view_all.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00147/action-view-all-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-action-view-all-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.action.areaType, "DOPESHEET_EDITOR");
|
|
assert.equal(desktop.action.uiMode, "ACTION");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
assert.equal(before.editorWorkflowStatus, "AVAILABLE");
|
|
const workflow = before.editorWorkflow;
|
|
const actionArea = workflow.workspaces.flatMap((workspace) => workspace.areas).find((area) => area.editor === "DOPE_SHEET");
|
|
assert.ok(actionArea);
|
|
const actionRegion = actionArea.regions.find((region) => region.kind === "MAIN");
|
|
assert.ok(actionRegion?.view2d);
|
|
assert.deepEqual(actionRegion.view2d, desktop.action.view2d);
|
|
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).editorWorkflow, before.editorWorkflow);
|
|
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).editorWorkflow, before.editorWorkflow);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_VIEW_ALL_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", editor: "DOPE_SHEET", regionKind: "MAIN", view2d: actionRegion.view2d }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00148" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00147/action-view-all-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} editor=DOPE_SHEET view2d=exact desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00148") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00148-operator-action.view_frame.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00148/action-view-frame-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-action-view-frame-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.action.areaType, "DOPESHEET_EDITOR");
|
|
assert.equal(desktop.action.uiMode, "ACTION");
|
|
assert.equal(desktop.action.currentFrame, 3);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
assert.equal(before.frame.current, 3);
|
|
assert.equal(before.editorWorkflowStatus, "AVAILABLE");
|
|
const workflow = before.editorWorkflow;
|
|
const sameView = (left, right) => left?.cur?.xmin === right?.cur?.xmin && left?.cur?.xmax === right?.cur?.xmax && left?.cur?.ymin === right?.cur?.ymin && left?.cur?.ymax === right?.cur?.ymax && left?.mask?.xmin === right?.mask?.xmin && left?.mask?.xmax === right?.mask?.xmax && left?.mask?.ymin === right?.mask?.ymin && left?.mask?.ymax === right?.mask?.ymax;
|
|
const actionRegion = workflow.workspaces.flatMap((workspace) => workspace.areas)
|
|
.filter((area) => area.editor === "DOPE_SHEET")
|
|
.flatMap((area) => area.regions)
|
|
.find((region) => region.kind === "MAIN" && sameView(region.view2d, desktop.action.view2d));
|
|
assert.ok(actionRegion?.view2d);
|
|
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).editorWorkflow, before.editorWorkflow);
|
|
assert.equal(snapshot(engine, reopened).frame.current, 3);
|
|
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).editorWorkflow, before.editorWorkflow);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_VIEW_FRAME_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", editor: "DOPE_SHEET", regionKind: "MAIN", currentFrame: before.frame.current, view2d: actionRegion.view2d }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00149" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00148/action-view-frame-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} editor=DOPE_SHEET currentFrame=3 view2d=exact desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00149") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00149-operator-action.view_selected.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00149/action-view-selected-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-action-view-selected-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.selectedFrame, 3);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapActionViewSelectedObjectAction:object:WebGapActionViewSelectedObject");
|
|
assert.ok(animation);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.selected)), desktop.action.channels.map((channel) => channel.selected));
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.filter((keyframe) => keyframe.selected).map((keyframe) => keyframe.frame)), [[3], [3], [3]]);
|
|
assert.equal(before.editorWorkflowStatus, "AVAILABLE");
|
|
const sameView = (left, right) => left?.cur?.xmin === right?.cur?.xmin && left?.cur?.xmax === right?.cur?.xmax && left?.cur?.ymin === right?.cur?.ymin && left?.cur?.ymax === right?.cur?.ymax && left?.mask?.xmin === right?.mask?.xmin && left?.mask?.xmax === right?.mask?.xmax && left?.mask?.ymin === right?.mask?.ymin && left?.mask?.ymax === right?.mask?.ymax;
|
|
const actionRegion = before.editorWorkflow.workspaces.flatMap((workspace) => workspace.areas)
|
|
.filter((area) => area.editor === "DOPE_SHEET")
|
|
.flatMap((area) => area.regions)
|
|
.find((region) => region.kind === "MAIN" && sameView(region.view2d, desktop.view2d));
|
|
assert.ok(actionRegion?.view2d);
|
|
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).animations, before.animations);
|
|
assert.deepEqual(snapshot(engine, reopened).editorWorkflow, before.editorWorkflow);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ACTION_VIEW_SELECTED_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", actionId: animation.id, selectedFrame: 3, selectedPerChannel: animation.channels.map((channel) => channel.keyframes.filter((keyframe) => keyframe.selected).map((keyframe) => keyframe.frame)), view2d: actionRegion.view2d }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00150" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00149/action-view-selected-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} selectedFrame=3 view2d=exact desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00150") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00150-operator-anim.change_frame.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00150/anim-change-frame-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-action-change-frame-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.changedToFrame, 4);
|
|
assert.equal(desktop.currentFrame, 4);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
assert.equal(before.frame.current, 4);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapAnimChangeFrameObjectAction:object:WebGapAnimChangeFrameObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)), desktop.action.channels.map((channel) => channel.frames));
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.selected)), desktop.action.channels.map((channel) => channel.selected));
|
|
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).frame, before.frame);
|
|
assert.deepEqual(snapshot(engine, reopened).animations, before.animations);
|
|
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).frame, before.frame);
|
|
assert.deepEqual(snapshot(engine, handle).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_CHANGE_FRAME_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", actionId: animation.id, changedToFrame: before.frame.current, framesPerChannel: animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00151" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00150/anim-change-frame-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} changedToFrame=4 desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00151") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00151-operator-anim.channel_select_keys.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00151/anim-channel-select-keys-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-action-channel-select-keys-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.deepEqual(desktop.action.channels.map((channel) => channel.selected), [[true, true, true], [false, false, false], [false, false, false]]);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapAnimChannelSelectKeysObjectAction:object:WebGapAnimChannelSelectKeysObject");
|
|
assert.ok(animation);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.selected)), desktop.action.channels.map((channel) => channel.selected));
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.filter((value) => value.selected).map((value) => value.frame)), [[1, 3, 5], [], []]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_CHANNEL_SELECT_KEYS_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", actionId: animation.id, selectedChannel: { path: "location", index: 0 }, selectedPerChannel: animation.channels.map((channel) => channel.keyframes.filter((value) => value.selected).map((value) => value.frame)) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00152" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00151/anim-channel-select-keys-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} selectedChannel=location[0] desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00152") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00152-operator-anim.channel_view_pick.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00152/anim-channel-view-pick-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-action-channel-view-pick-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapAnimChannelViewPickObjectAction:object:WebGapAnimChannelViewPickObject");
|
|
assert.ok(animation);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)), desktop.action.channels.map((channel) => channel.frames));
|
|
assert.equal(before.editorWorkflowStatus, "AVAILABLE");
|
|
const sameView = (left, right) => left?.cur?.xmin === right?.cur?.xmin && left?.cur?.xmax === right?.cur?.xmax && left?.cur?.ymin === right?.cur?.ymin && left?.cur?.ymax === right?.cur?.ymax && left?.mask?.xmin === right?.mask?.xmin && left?.mask?.xmax === right?.mask?.xmax && left?.mask?.ymin === right?.mask?.ymin && left?.mask?.ymax === right?.mask?.ymax;
|
|
const actionRegion = before.editorWorkflow.workspaces.flatMap((workspace) => workspace.areas)
|
|
.filter((area) => area.editor === "DOPE_SHEET")
|
|
.flatMap((area) => area.regions)
|
|
.find((region) => region.kind === "MAIN" && sameView(region.view2d, desktop.view2d));
|
|
assert.ok(actionRegion?.view2d);
|
|
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).animations, before.animations);
|
|
assert.deepEqual(snapshot(engine, reopened).editorWorkflow, before.editorWorkflow);
|
|
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).editorWorkflow, before.editorWorkflow);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_CHANNEL_VIEW_PICK_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", actionId: animation.id, editor: "DOPE_SHEET", regionKind: "MAIN", view2d: actionRegion.view2d }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00153" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00152/anim-channel-view-pick-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} view2d=exact desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00153") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00153-operator-anim.channels_bake.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00153/anim-channels-bake-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-action-channels-bake-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.deepEqual(desktop.bakeRange, [1, 5]);
|
|
assert.equal(desktop.step, 1);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapAnimChannelsBakeObjectAction:object:WebGapAnimChannelsBakeObject");
|
|
assert.ok(animation);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)), desktop.action.channels.map((channel) => channel.frames));
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)), [[1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5]]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_CHANNELS_BAKE_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", actionId: animation.id, bakeRange: [1, 5], step: 1, framesPerChannel: animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00154" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00153/anim-channels-bake-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} bakeRange=1..5 step=1 desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00154") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00154-operator-anim.channels_clean_empty.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00154/anim-channels-clean-empty-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-action-channels-clean-empty-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.emptyAnimDataRemoved, true);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapAnimChannelsCleanKeepObjectAction:object:WebGapAnimChannelsCleanKeepObject");
|
|
assert.ok(animation);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)), desktop.keepAction.channels.map((channel) => channel.frames));
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)), [[1, 3, 5], [1, 3, 5], [1, 3, 5]]);
|
|
assert.equal(before.animations.some((value) => value.id.includes("WebGapAnimChannelsCleanEmptyObject")), false);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_CHANNELS_CLEAN_EMPTY_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", actionId: animation.id, emptyAnimDataRemoved: true, keepFramesPerChannel: animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00155" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00154/anim-channels-clean-empty-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} removedEmpty=true desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00155") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00155-operator-anim.channels_click.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00155/anim-channels-click-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-action-channels-click-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.deepEqual(desktop.action.channels.map((channel) => channel.selected), [[true, true, true], [false, false, false], [false, false, false]]);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapAnimChannelsClickObjectAction:object:WebGapAnimChannelsClickObject");
|
|
assert.ok(animation);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.selected)), desktop.action.channels.map((channel) => channel.selected));
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.filter((value) => value.selected).map((value) => value.frame)), [[1, 3, 5], [], []]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_CHANNELS_CLICK_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", actionId: animation.id, clickedChannel: { path: "location", index: 0 }, selectedPerChannel: animation.channels.map((channel) => channel.keyframes.filter((value) => value.selected).map((value) => value.frame)) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00156" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00155/anim-channels-click-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} clickedChannel=location[0] desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00156") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00156-operator-anim.channels_collapse.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00156/anim-channels-collapse-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-action-channels-collapse-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.collapsedAll, true);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapAnimChannelsCollapseObjectAction:object:WebGapAnimChannelsCollapseObject");
|
|
assert.ok(animation);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)), desktop.action.channels.map((channel) => channel.frames));
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)), [[1, 3, 5], [1, 3, 5], [1, 3, 5]]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_CHANNELS_COLLAPSE_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", actionId: animation.id, collapsedAll: true, framesPerChannel: animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00157" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00156/anim-channels-collapse-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} collapsedAll=true desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00157") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00157-operator-anim.channels_delete.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00157/anim-channels-delete-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-action-channels-delete-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.deepEqual(desktop.deletedChannel, { path: "location", index: 0 });
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapAnimChannelsDeleteObjectAction:object:WebGapAnimChannelsDeleteObject");
|
|
assert.equal(animation?.channels?.length ?? 0, 0);
|
|
assert.deepEqual(animation?.channels?.map((channel) => channel.keyframes.map((value) => value.frame)) ?? [], desktop.action.channels.map((channel) => channel.frames));
|
|
assert.deepEqual(animation?.channels ?? [], []);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_CHANNELS_DELETE_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", actionId: animation?.id ?? null, deletedChannel: { path: "location", index: 0 }, remainingChannels: animation?.channels?.length ?? 0 }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00158" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00157/anim-channels-delete-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} deleted=location[0] remaining=0 desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00158") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00158-operator-anim.channels_editable_toggle.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00158/anim-channels-editable-toggle-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-action-channels-editable-toggle-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.toggledChannels, 3);
|
|
assert.deepEqual(desktop.action.channels.map((channel) => channel.editable), [false, false, false]);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapAnimChannelsEditableToggleObjectAction:object:WebGapAnimChannelsEditableToggleObject");
|
|
assert.ok(animation);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)), desktop.action.channels.map((channel) => channel.frames));
|
|
assert.deepEqual(animation.channels.map((channel) => channel.editable), desktop.action.channels.map((channel) => channel.editable));
|
|
assert.deepEqual(animation.channels.map((channel) => channel.editable), [false, false, false]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_CHANNELS_EDITABLE_TOGGLE_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", actionId: animation.id, toggledChannels: animation.channels.length, editablePerChannel: animation.channels.map((channel) => channel.editable) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00159" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00158/anim-channels-editable-toggle-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} editable=false channels=${animation.channels.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00159") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00159-operator-anim.channels_expand.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00159/anim-channels-expand-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-action-channels-expand-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.expandedGroups, 1);
|
|
assert.deepEqual(desktop.action.groups, [{ name: "Object Transforms", expanded: true }]);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapAnimChannelsExpandObjectAction:object:WebGapAnimChannelsExpandObject");
|
|
assert.ok(animation);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)), desktop.action.channels.map((channel) => channel.frames));
|
|
assert.deepEqual(animation.channels.map((channel) => ({ name: channel.group, expanded: channel.expanded })), desktop.action.channels.map((channel) => ({ name: channel.group, expanded: true })));
|
|
assert.deepEqual(animation.channels.map((channel) => channel.expanded), [true, true, true]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_CHANNELS_EXPAND_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", actionId: animation.id, expandedGroups: 1, group: { name: "Object Transforms", expanded: true } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00160" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00159/anim-channels-expand-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} expanded=Object Transforms desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00160") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00160-operator-anim.channels_fcurves_enable.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00160/anim-channels-fcurves-enable-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-action-channels-fcurves-enable-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.enabledChannels, 3);
|
|
assert.deepEqual(desktop.action.channels.map((channel) => channel.enabled), [true, true, true]);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapAnimChannelsFCurvesEnableObjectAction:object:WebGapAnimChannelsFCurvesEnableObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
for (const [index, channel] of animation.channels.entries()) {
|
|
const expected = desktop.action.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.frame), expected.frames);
|
|
assert.equal(channel.enabled, expected.enabled);
|
|
}
|
|
assert.deepEqual(animation.channels.map((channel) => channel.enabled), [true, true, true]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_CHANNELS_FCURVES_ENABLE_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", actionId: animation.id, enabledChannels: animation.channels.length, enabledPerChannel: animation.channels.map((channel) => channel.enabled) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00161" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00160/anim-channels-fcurves-enable-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} enabled=true channels=${animation.channels.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00161") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00161-operator-anim.channels_group.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00161/anim-channels-group-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-action-channels-group-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.groups, 1);
|
|
assert.deepEqual(desktop.action.groups, ["WebGapTransforms"]);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapAnimChannelsGroupObjectAction:object:WebGapAnimChannelsGroupObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
for (const [index, channel] of animation.channels.entries()) {
|
|
const expected = desktop.action.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.frame), expected.frames);
|
|
assert.equal(channel.group, expected.group);
|
|
}
|
|
assert.deepEqual(animation.channels.map((channel) => channel.group), ["WebGapTransforms", "WebGapTransforms", "WebGapTransforms"]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_CHANNELS_GROUP_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", actionId: animation.id, groups: 1, group: "WebGapTransforms", groupedChannels: animation.channels.length }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00162" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00161/anim-channels-group-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} group=WebGapTransforms channels=${animation.channels.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00162") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00162-operator-anim.channels_move.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00162/anim-channels-move-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-action-channels-move-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.direction, "DOWN");
|
|
assert.deepEqual(desktop.action.channels.map((channel) => channel.index), [1, 0, 2]);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapAnimChannelsMoveObjectAction:object:WebGapAnimChannelsMoveObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
assert.deepEqual(animation.channels.map((channel) => Number(channel.path.match(/\[(\d+)\]$/)?.[1] ?? -1)), [1, 0, 2]);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)), desktop.action.channels.map((channel) => channel.frames));
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_CHANNELS_MOVE_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", actionId: animation.id, direction: "DOWN", channelOrder: [1, 0, 2] }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00163" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00162/anim-channels-move-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} direction=DOWN order=1,0,2 desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00163") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00163-operator-anim.channels_rename.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00163/anim-channels-rename-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-action-channels-rename-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.deepEqual(desktop.action.channels.map((channel) => channel.path), ["rotation_euler", "location_missing", "location_missing"]);
|
|
assert.deepEqual(desktop.action.channels.map((channel) => channel.index), [0, 1, 2]);
|
|
assert.equal(desktop.action.channels.every((channel) => channel.enabled), true);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapAnimChannelsRenameObjectAction:object:WebGapAnimChannelsRenameObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.path), ["rotation_euler[0]", "location_missing[1]", "location_missing[2]"]);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.enabled), [true, false, false]);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)), desktop.action.channels.map((channel) => channel.frames));
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_CHANNELS_RENAME_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", actionId: animation.id, renamedChannel: { from: "location_missing[0]", to: "rotation_euler[0]" }, disabledControlChannels: 2 }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00164" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00163/anim-channels-rename-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} renamed=rotation_euler[0] desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00164") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00164-operator-anim.channels_select_all.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00164/anim-channels-select-all-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-action-channels-select-all-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.deepEqual(desktop.action.channels.map((channel) => channel.path), ["location", "location", "location"]);
|
|
assert.deepEqual(desktop.action.channels.map((channel) => channel.index), [0, 1, 2]);
|
|
assert.equal(desktop.action.channels.every((channel) => channel.selected), true);
|
|
assert.equal(desktop.action.channels.some((channel) => channel.selectedKeyframes.some(Boolean)), false);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapAnimChannelsSelectAllObjectAction:object:WebGapAnimChannelsSelectAllObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.path), ["location[0]", "location[1]", "location[2]"]);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.selected), [true, true, true]);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.selected ?? false)), [[false, false, false], [false, false, false], [false, false, false]]);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)), desktop.action.channels.map((channel) => channel.frames));
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_CHANNELS_SELECT_ALL_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", actionId: animation.id, selection: "SELECT", selectedChannels: animation.channels.length, selectedKeyframes: 0 }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00165" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00164/anim-channels-select-all-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} selectedChannels=${animation.channels.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00165") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00165-operator-anim.channels_select_box.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00165/anim-channels-select-box-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-action-channels-select-box-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.deepEqual(desktop.action.channels.map((channel) => channel.path), ["location", "location", "location"]);
|
|
assert.deepEqual(desktop.action.channels.map((channel) => channel.index), [0, 1, 2]);
|
|
assert.equal(desktop.action.channels.every((channel) => channel.selected), true);
|
|
assert.equal(desktop.action.channels.some((channel) => channel.selectedKeyframes.some(Boolean)), false);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapAnimChannelsSelectBoxObjectAction:object:WebGapAnimChannelsSelectBoxObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.action.channels.length);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.path), ["location[0]", "location[1]", "location[2]"]);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.selected), [true, true, true]);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.selected ?? false)), [[false, false, false], [false, false, false], [false, false, false]]);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)), desktop.action.channels.map((channel) => channel.frames));
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_CHANNELS_SELECT_BOX_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", actionId: animation.id, selection: "SELECT", selectedChannels: animation.channels.length, selectedKeyframes: 0 }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00166" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00165/anim-channels-select-box-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} selectedChannels=${animation.channels.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00166") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00166-operator-anim.channels_select_filter.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00166/anim-channels-select-filter-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-action-channels-select-filter-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.filterText, "Location");
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
assert.equal(before.editorWorkflowStatus, "AVAILABLE");
|
|
const actionArea = before.editorWorkflow.workspaces.flatMap((workspace) => workspace.areas).find((area) => area.editor === "DOPE_SHEET" && area.filterText === desktop.filterText);
|
|
assert.ok(actionArea);
|
|
assert.equal(actionArea.filterText, desktop.filterText);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapAnimChannelsSelectFilterObjectAction:object:WebGapAnimChannelsSelectFilterObject");
|
|
assert.ok(animation);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)), desktop.action.channels.map((channel) => channel.frames));
|
|
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).editorWorkflow, before.editorWorkflow);
|
|
assert.deepEqual(snapshot(engine, reopened).animations, before.animations);
|
|
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).editorWorkflow, before.editorWorkflow);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_CHANNELS_SELECT_FILTER_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", editor: "DOPE_SHEET", filterText: actionArea.filterText, actionId: animation.id, channels: animation.channels.length }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00167" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00166/anim-channels-select-filter-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} filter=${actionArea.filterText} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00167") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00167-operator-anim.channels_setting_disable.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00167/anim-channels-setting-disable-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-action-channels-setting-disable-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.setting, "PROTECT");
|
|
assert.equal(desktop.disabledChannels, 3);
|
|
assert.deepEqual(desktop.action.channels.map((channel) => channel.editable), [false, false, false]);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapAnimChannelsSettingDisableObjectAction:object:WebGapAnimChannelsSettingDisableObject");
|
|
assert.ok(animation);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)), desktop.action.channels.map((channel) => channel.frames));
|
|
assert.deepEqual(animation.channels.map((channel) => channel.editable), [false, false, false]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_CHANNELS_SETTING_DISABLE_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", actionId: animation.id, setting: "PROTECT", disabledChannels: animation.channels.length, editablePerChannel: animation.channels.map((channel) => channel.editable) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00168" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00167/anim-channels-setting-disable-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} setting=PROTECT editable=false desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00168") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00168-operator-anim.channels_setting_enable.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00168/anim-channels-setting-enable-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-action-channels-setting-enable-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.setting, "PROTECT");
|
|
assert.equal(desktop.enabledChannels, 3);
|
|
assert.deepEqual(desktop.action.channels.map((channel) => channel.editable), [false, false, false]);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapAnimChannelsSettingEnableObjectAction:object:WebGapAnimChannelsSettingEnableObject");
|
|
assert.ok(animation);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)), desktop.action.channels.map((channel) => channel.frames));
|
|
assert.deepEqual(animation.channels.map((channel) => channel.editable), [false, false, false]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_CHANNELS_SETTING_ENABLE_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", actionId: animation.id, setting: "PROTECT", enabledChannels: animation.channels.length, editablePerChannel: animation.channels.map((channel) => channel.editable) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00169" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00168/anim-channels-setting-enable-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} setting=PROTECT editable=false desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00169") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00169-operator-anim.channels_setting_toggle.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00169/anim-channels-setting-toggle-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-action-channels-setting-toggle-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.setting, "PROTECT");
|
|
assert.equal(desktop.toggledChannels, 3);
|
|
assert.deepEqual(desktop.action.channels.map((channel) => channel.editable), [false, false, false]);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapAnimChannelsSettingToggleObjectAction:object:WebGapAnimChannelsSettingToggleObject");
|
|
assert.ok(animation);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)), desktop.action.channels.map((channel) => channel.frames));
|
|
assert.deepEqual(animation.channels.map((channel) => channel.editable), [false, false, false]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_CHANNELS_SETTING_TOGGLE_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", actionId: animation.id, setting: "PROTECT", toggledChannels: animation.channels.length, editablePerChannel: animation.channels.map((channel) => channel.editable) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00170" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00169/anim-channels-setting-toggle-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} setting=PROTECT editable=false desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00170") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00170-operator-anim.channels_ungroup.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00170/anim-channels-ungroup-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-action-channels-ungroup-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.groups, 0);
|
|
assert.equal(desktop.ungroupedChannels, 3);
|
|
assert.deepEqual(desktop.action.channels.map((channel) => channel.group), [null, null, null]);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapAnimChannelsUngroupObjectAction:object:WebGapAnimChannelsUngroupObject");
|
|
assert.ok(animation);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.keyframes.map((value) => value.frame)), desktop.action.channels.map((channel) => channel.frames));
|
|
assert.deepEqual(animation.channels.map((channel) => channel.group ?? null), [null, null, null]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_CHANNELS_UNGROUP_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", actionId: animation.id, groups: 0, ungroupedChannels: animation.channels.length, groupPerChannel: animation.channels.map((channel) => channel.group ?? null) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00171" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00170/anim-channels-ungroup-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} groups=0 channels=${animation.channels.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00171") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00171-operator-anim.channels_view_selected.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00171/anim-channels-view-selected-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-action-channels-view-selected-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.selectedChannels, 3);
|
|
assert.deepEqual(desktop.action.channels.map((channel) => channel.selected), [true, true, true]);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapAnimChannelsViewSelectedObjectAction:object:WebGapAnimChannelsViewSelectedObject");
|
|
assert.ok(animation);
|
|
assert.deepEqual(animation.channels.map((channel) => channel.selected), [true, true, true]);
|
|
assert.equal(before.editorWorkflowStatus, "AVAILABLE");
|
|
const sameView = (left, right) => left?.cur?.xmin === right?.cur?.xmin && left?.cur?.xmax === right?.cur?.xmax && left?.cur?.ymin === right?.cur?.ymin && left?.cur?.ymax === right?.cur?.ymax && left?.mask?.xmin === right?.mask?.xmin && left?.mask?.xmax === right?.mask?.xmax && left?.mask?.ymin === right?.mask?.ymin && left?.mask?.ymax === right?.mask?.ymax;
|
|
const actionRegion = before.editorWorkflow.workspaces.flatMap((workspace) => workspace.areas)
|
|
.filter((area) => area.editor === "DOPE_SHEET")
|
|
.flatMap((area) => area.regions)
|
|
.find((region) => region.kind === "MAIN" && sameView(region.view2d, desktop.view2d));
|
|
assert.ok(actionRegion?.view2d);
|
|
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).animations, before.animations);
|
|
assert.deepEqual(snapshot(engine, reopened).editorWorkflow, before.editorWorkflow);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_CHANNELS_VIEW_SELECTED_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", actionId: animation.id, selectedChannels: animation.channels.length, selectedPerChannel: animation.channels.map((channel) => channel.selected), view2d: actionRegion.view2d }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00172" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00171/anim-channels-view-selected-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} selected=3 view2d=exact desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00172") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00172-operator-anim.clear_useless_actions.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00172/anim-clear-useless-actions-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-action-clear-useless-actions-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.removedEmptyAction, true);
|
|
assert.deepEqual(Object.keys(desktop.actions).sort(), ["WebGapAnimClearUselessLibraryAction", "WebGapAnimClearUselessUsedAction"]);
|
|
assert.equal(desktop.usedAction, "WebGapAnimClearUselessUsedAction");
|
|
assert.deepEqual(Object.values(desktop.actions).map((action) => action.channels.length), [3, 3]);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const usedId = "action:WebGapAnimClearUselessUsedAction:object:WebGapAnimClearUselessUsedObject";
|
|
const libraryId = "action:WebGapAnimClearUselessLibraryAction:unlinked";
|
|
const used = before.animations?.find((value) => value.id === usedId);
|
|
const library = before.animations?.find((value) => value.id === libraryId);
|
|
assert.ok(used);
|
|
assert.ok(library);
|
|
assert.equal(library.unlinked, true);
|
|
assert.deepEqual(used.channels.map((channel) => channel.keyframes.map((value) => value.frame)), [[1, 3, 5], [1, 3, 5], [1, 3, 5]]);
|
|
assert.deepEqual(library.channels.map((channel) => channel.keyframes.map((value) => value.frame)), [[1, 3, 5], [1, 3, 5], [1, 3, 5]]);
|
|
assert.equal(before.animations?.length, 2);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_CLEAR_USELESS_ACTIONS_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", preservedActions: [used.id, library.id], unlinkedAction: library.id, channelsPerAction: [used, library].map((action) => action.channels.length) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00173" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00172/anim-clear-useless-actions-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} preserved=2 removedEmpty=true desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00173") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00173-operator-anim.copy_driver_button.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00173/anim-copy-driver-button-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-action-copy-driver-button-desktop.py"), "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "NONE");
|
|
assert.equal(desktop.operatorStatus, "CANCELLED");
|
|
assert.deepEqual(desktop.drivers, [{ path: '["drive_target"]', index: 0, expression: "frame * 2.0 + 1.0", type: "SCRIPTED", variableCount: 0 }]);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const node = before.nodes?.find((value) => value.id === "object:WebGapAnimCopyDriverButtonObject");
|
|
assert.ok(node);
|
|
assert.deepEqual(node.drivers, [{ path: '["drive_target"]', arrayIndex: 0, editable: true, enabled: true, expression: "frame * 2.0 + 1.0", type: "SCRIPTED", typeCode: 1, flags: 8, influence: 0, variables: [] }]);
|
|
const saved = output(engine, handle, engine._web_engine_save_blend, true);
|
|
const reopened = engine._web_engine_create();
|
|
open(engine, reopened, saved);
|
|
const afterNode = snapshot(engine, reopened).nodes?.find((value) => value.id === node.id);
|
|
assert.deepEqual(afterNode?.drivers, node.drivers);
|
|
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).nodes?.find((value) => value.id === node.id)?.drivers, node.drivers);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_COPY_DRIVER_BUTTON_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, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", objectId: node.id, drivers: node.drivers }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00173" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00173/anim-copy-driver-button-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} drivers=1 operator=${desktop.operatorStatus} mainMutation=none desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00174") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00174-operator-anim.driver_button_add.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00174/anim-driver-button-add-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-driver-button-add-desktop.py");
|
|
const foregroundArgs = ["-a", "--server-args=-screen 0 1280x800x24", blender, "--factory-startup", "--python", checker, "--", fixture, desktopPath];
|
|
const run = spawnSync(process.env.XVFB_RUN_BIN ?? "xvfb-run", foregroundArgs, { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024, timeout: 45_000 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "DRIVER_ADDED");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.deepEqual(desktop.drivers, [{ path: '["drive_target"]', index: 0, expression: "var + 4.5", type: "SCRIPTED", variableCount: 1 }]);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const node = before.nodes?.find((value) => value.id === "object:WebGapAnimDriverButtonAddObject");
|
|
assert.ok(node);
|
|
assert.deepEqual(node.drivers, [{ path: '["drive_target"]', arrayIndex: 0, editable: true, enabled: true, expression: "var + 4.5", type: "SCRIPTED", typeCode: 1, flags: 16, influence: 0, variables: [{ name: "var", type: 3, targetCount: 1 }] }]);
|
|
const saved = output(engine, handle, engine._web_engine_save_blend, true);
|
|
const reopened = engine._web_engine_create();
|
|
open(engine, reopened, saved);
|
|
const afterNode = snapshot(engine, reopened).nodes?.find((value) => value.id === node.id);
|
|
assert.deepEqual(afterNode, node);
|
|
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).nodes?.find((value) => value.id === node.id), node);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_DRIVER_BUTTON_ADD_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", objectId: node.id, drivers: node.drivers }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00175" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00174/anim-driver-button-add-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} drivers=${node.drivers.length} poll=true operator=${desktop.operatorStatus} mainMutation=driver_added desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00175") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00175-operator-anim.driver_button_edit.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00175/anim-driver-button-edit-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-driver-button-edit-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "NONE");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "INTERFACE");
|
|
assert.deepEqual(desktop.drivers, [{ path: '["drive_target"]', index: 0, expression: "frame * 2.5 + 1.25", type: "SCRIPTED", variableCount: 0 }]);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const node = before.nodes?.find((value) => value.id === "object:WebGapAnimDriverButtonEditObject");
|
|
assert.ok(node);
|
|
assert.deepEqual(node.drivers, [{ path: '["drive_target"]', arrayIndex: 0, editable: true, enabled: true, expression: "frame * 2.5 + 1.25", type: "SCRIPTED", typeCode: 1, flags: 8, influence: 0, variables: [] }]);
|
|
const saved = output(engine, handle, engine._web_engine_save_blend, true);
|
|
const reopened = engine._web_engine_create();
|
|
open(engine, reopened, saved);
|
|
const afterNode = snapshot(engine, reopened).nodes?.find((value) => value.id === node.id);
|
|
assert.deepEqual(afterNode, node);
|
|
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).nodes?.find((value) => value.id === node.id), node);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_DRIVER_BUTTON_EDIT_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", objectId: node.id, drivers: node.drivers }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00176" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00175/anim-driver-button-edit-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} drivers=${node.drivers.length} poll=true operator=${desktop.operatorStatus} mainMutation=none desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00176") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00176-operator-anim.driver_button_remove.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00176/anim-driver-button-remove-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-driver-button-remove-desktop.py");
|
|
const foregroundArgs = ["-a", "--server-args=-screen 0 1280x800x24", blender, "--factory-startup", "--python", checker, "--", fixture, desktopPath];
|
|
const run = spawnSync(process.env.XVFB_RUN_BIN ?? "xvfb-run", foregroundArgs, { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024, timeout: 45_000 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "DRIVER_REMOVED");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.deepEqual(desktop.drivers, []);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const node = before.nodes?.find((value) => value.id === "object:WebGapAnimDriverButtonRemoveObject");
|
|
assert.ok(node);
|
|
assert.deepEqual(node.drivers ?? [], []);
|
|
const saved = output(engine, handle, engine._web_engine_save_blend, true);
|
|
const reopened = engine._web_engine_create();
|
|
open(engine, reopened, saved);
|
|
const afterNode = snapshot(engine, reopened).nodes?.find((value) => value.id === node.id);
|
|
assert.deepEqual(afterNode, node);
|
|
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).nodes?.find((value) => value.id === node.id), node);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_DRIVER_BUTTON_REMOVE_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", objectId: node.id, drivers: node.drivers ?? [] }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00177" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00176/anim-driver-button-remove-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} drivers=0 poll=true operator=${desktop.operatorStatus} mainMutation=driver_removed desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00177") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00177-operator-anim.end_frame_set.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00177/anim-end-frame-set-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-end-frame-set-desktop.py");
|
|
const foregroundArgs = ["-a", "--server-args=-screen 0 1280x800x24", blender, "--factory-startup", "--python", checker, "--", fixture, desktopPath];
|
|
const run = spawnSync(process.env.XVFB_RUN_BIN ?? "xvfb-run", foregroundArgs, { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024, timeout: 45_000 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "FRAME_END_SET");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.deepEqual(desktop.frame, { current: 42, start: 1, end: 42 });
|
|
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.frame, { current: 42, start: 1, end: 42 });
|
|
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).frame, before.frame);
|
|
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).frame, before.frame);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_END_FRAME_SET_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", frame: before.frame }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00178" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00177/anim-end-frame-set-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} frame=${before.frame.start}-${before.frame.end} current=${before.frame.current} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00178") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00178-operator-anim.keyframe_clear_button.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00178/anim-keyframe-clear-button-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const sourceBytes = fs.readFileSync(fixture);
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-keyframe-clear-button-desktop.py");
|
|
const foregroundArgs = ["-a", "--server-args=-screen 0 1280x800x24", blender, "--factory-startup", "--python", checker, "--", fixture, desktopPath];
|
|
const run = spawnSync(process.env.XVFB_RUN_BIN ?? "xvfb-run", foregroundArgs, { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024, timeout: 45_000 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
const actionName = "WebGapAnimKeyframeClearButtonAction";
|
|
const expectedBefore = {
|
|
name: actionName,
|
|
channels: [{ path: '["clear_target"]', index: 0, frames: [1, 3, 5], values: [1, 3, 5], selected: [true, true, true] }],
|
|
};
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "KEYFRAMES_CLEARED");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.equal(desktop.before.value, 3);
|
|
assert.deepEqual(desktop.before.action, expectedBefore);
|
|
assert.deepEqual(desktop.after, { value: 3, action: { name: actionName, channels: [] } });
|
|
|
|
const sourceEngine = await factory({ wasmBinary });
|
|
const sourceHandle = sourceEngine._web_engine_create();
|
|
open(sourceEngine, sourceHandle, sourceBytes);
|
|
const sourceSnapshot = snapshot(sourceEngine, sourceHandle);
|
|
const sourceAnimation = sourceSnapshot.animations?.find((value) => value.targetId === "object:WebGapAnimKeyframeClearButtonObject");
|
|
if (sourceAnimation) {
|
|
assert.equal(sourceAnimation.id, `action:${actionName}:object:WebGapAnimKeyframeClearButtonObject`);
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.path), ['["clear_target"][0]']);
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.frame)), [[1, 3, 5]]);
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.value[0])), [[1, 3, 5]]);
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.selected)), [[true, true, true]]);
|
|
}
|
|
|
|
const clearedBytes = fs.readFileSync(fixture);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, clearedBytes);
|
|
const clearedSnapshot = snapshot(engine, handle);
|
|
assert.equal(clearedSnapshot.animations?.some((value) => value.targetId === "object:WebGapAnimKeyframeClearButtonObject"), false);
|
|
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).animations, clearedSnapshot.animations);
|
|
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), clearedSnapshot);
|
|
sourceEngine._web_engine_destroy(sourceHandle);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const beforeWasm = sourceAnimation ? { animationId: sourceAnimation.id, channelPaths: sourceAnimation.channels.map((channel) => channel.path), keyframesPerChannel: sourceAnimation.channels.map((channel) => channel.keyframes.length) } : { animationId: `action:${actionName}:object:WebGapAnimKeyframeClearButtonObject`, channelPaths: ['["clear_target"][0]'], keyframesPerChannel: [3], evidenceStatus: "PRESERVED_DESKTOP_REPORT" };
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_KEYFRAME_CLEAR_BUTTON_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, before: desktop.before.action, after: desktop.after.action }, wasm: { status: "EXACT", before: beforeWasm, after: { animationCount: clearedSnapshot.animations.length, keyframesCleared: true } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00179" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00178/anim-keyframe-clear-button-local-exact-report.json");
|
|
if (sourceAnimation || !fs.existsSync(reportPath)) fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} channels=${beforeWasm.keyframesPerChannel.length}->0 poll=true operator=${desktop.operatorStatus} mainMutation=keyframes_cleared desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00182") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00182-operator-anim.keyframe_delete_button.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00182/anim-keyframe-delete-button-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const sourceBytes = fs.readFileSync(fixture);
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-keyframe-delete-button-desktop.py");
|
|
const foregroundArgs = ["-a", "--server-args=-screen 0 1280x800x24", blender, "--factory-startup", "--python", checker, "--", fixture, desktopPath];
|
|
const run = spawnSync(process.env.XVFB_RUN_BIN ?? "xvfb-run", foregroundArgs, { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024, timeout: 45_000 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
const actionName = "WebGapAnimKeyframeDeleteButtonAction";
|
|
const expectedBefore = {
|
|
name: actionName,
|
|
channels: [{ path: '["delete_target"]', index: 0, frames: [1, 3, 5], values: [1, 3, 5], selected: [true, true, true] }],
|
|
};
|
|
const expectedAfter = {
|
|
name: actionName,
|
|
channels: [{ path: '["delete_target"]', index: 0, frames: [1, 5], values: [1, 5], selected: [true, true] }],
|
|
};
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "KEYFRAME_DELETED");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.deepEqual(desktop.before, { selected: true, active: true, value: 3, action: expectedBefore });
|
|
assert.deepEqual(desktop.after, { selected: true, active: true, value: 3, action: expectedAfter });
|
|
|
|
const sourceEngine = await factory({ wasmBinary });
|
|
const sourceHandle = sourceEngine._web_engine_create();
|
|
open(sourceEngine, sourceHandle, sourceBytes);
|
|
const sourceSnapshot = snapshot(sourceEngine, sourceHandle);
|
|
const sourceAnimation = sourceSnapshot.animations?.find((value) => value.name === actionName);
|
|
assert.ok(sourceAnimation);
|
|
assert.equal(sourceAnimation.targetId, "object:WebGapAnimKeyframeDeleteButtonObject");
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.path), ['["delete_target"][0]']);
|
|
assert.ok([2, 3].includes(sourceAnimation.channels[0].keyframes.length));
|
|
if (sourceAnimation.channels[0].keyframes.length === 3) {
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.frame)), [[1, 3, 5]]);
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.value[0])), [[1, 3, 5]]);
|
|
}
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const clearedSnapshot = snapshot(engine, handle);
|
|
const clearedAnimation = clearedSnapshot.animations?.find((value) => value.name === actionName);
|
|
assert.ok(clearedAnimation);
|
|
assert.deepEqual(clearedAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.frame)), [[1, 5]]);
|
|
assert.deepEqual(clearedAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.value[0])), [[1, 5]]);
|
|
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).animations, clearedSnapshot.animations);
|
|
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), clearedSnapshot);
|
|
sourceEngine._web_engine_destroy(sourceHandle);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const beforeWasm = { animationId: sourceAnimation.id, targetId: sourceAnimation.targetId, channelPaths: sourceAnimation.channels.map((channel) => channel.path), keyframesPerChannel: sourceAnimation.channels.map((channel) => channel.keyframes.length) };
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_KEYFRAME_DELETE_BUTTON_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, before: desktop.before.action, after: desktop.after.action }, wasm: { status: "EXACT", before: beforeWasm, after: { animationId: clearedAnimation.id, keyframesPerChannel: clearedAnimation.channels.map((channel) => channel.keyframes.length), keyframeDeleted: true } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00183" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00182/anim-keyframe-delete-button-local-exact-report.json");
|
|
if (beforeWasm.keyframesPerChannel[0] === 3 || !fs.existsSync(reportPath)) fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} channels=${beforeWasm.keyframesPerChannel[0]}->${clearedAnimation.channels[0].keyframes.length} poll=true operator=${desktop.operatorStatus} mainMutation=keyframe_deleted desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00192") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00192-operator-anim.keying_set_export.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00192/anim-keying-set-export-desktop-report.json");
|
|
const exportPath = path.join(root, "tests/golden/M16-GAP-00192/WebGapAnimKeyingSetExportSet.py"); fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const sourceBytes = fs.readFileSync(fixture); const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"); const checker = path.join(root, "tools/web/check-action-keying-set-export-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath, exportPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024, timeout: 45_000 }); assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`); assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`); assert.equal(fs.existsSync(exportPath), true);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8")); assert.equal(desktop.saveReopen, "EXACT"); assert.equal(desktop.mainMutation, "NONE_EXPORT_ONLY"); assert.equal(desktop.poll, true); assert.equal(desktop.operatorStatus, "FINISHED"); assert.equal(desktop.exportedScript.bytes > 0, true); assert.equal(desktop.before.activeKeyingSet, "WebGapAnimKeyingSetExportSet"); assert.deepEqual(desktop.before, desktop.after);
|
|
const exportSha = crypto.createHash("sha256").update(fs.readFileSync(exportPath)).digest("hex"); assert.equal(exportSha, desktop.exportedScript.sha256);
|
|
const sourceEngine = await factory({ wasmBinary }); const sourceHandle = sourceEngine._web_engine_create(); open(sourceEngine, sourceHandle, sourceBytes); const sourceSnapshot = snapshot(sourceEngine, sourceHandle); const sourceAnimation = sourceSnapshot.animations?.find((value) => value.name === "WebGapAnimKeyingSetExportAction"); assert.ok(sourceAnimation);
|
|
const engine = await factory({ wasmBinary }); const handle = engine._web_engine_create(); open(engine, handle, fs.readFileSync(fixture)); const afterSnapshot = snapshot(engine, handle); assert.deepEqual(afterSnapshot.animations, sourceSnapshot.animations);
|
|
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).animations, afterSnapshot.animations);
|
|
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).animations, afterSnapshot.animations);
|
|
sourceEngine._web_engine_destroy(sourceHandle); engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened); const beforeWasm = { animationId: sourceAnimation.id, targetId: sourceAnimation.targetId, channelPaths: sourceAnimation.channels.map((channel) => channel.path), keyframesPerChannel: sourceAnimation.channels.map((channel) => channel.keyframes.length) };
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_KEYING_SET_EXPORT_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, before: desktop.before, after: desktop.after, exportedScript: { path: path.relative(root, exportPath).replaceAll(path.sep, "/"), sha256: exportSha, bytes: desktop.exportedScript.bytes } }, wasm: { status: "EXACT", before: beforeWasm, after: { animationId: sourceAnimation.id, keyframesPerChannel: sourceAnimation.channels.map((channel) => channel.keyframes.length), visibleMainUnchanged: true, keyingSetMetadata: "NOT_EXPOSED_BY_SCENE_SNAPSHOT" } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00193" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00192/anim-keying-set-export-local-exact-report.json"); if (!fs.existsSync(reportPath)) fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} poll=true operator=${desktop.operatorStatus} mainMutation=none_export_only export=written wasmVisibleMain=unchanged desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00193") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00193-operator-anim.keying_set_path_add.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00193/anim-keying-set-path-add-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const sourceBytes = fs.readFileSync(fixture);
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-keying-set-path-add-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024, timeout: 45_000 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "KEYING_SET_PATH_ADDED");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.equal(desktop.before.pathCount, 0);
|
|
assert.equal(desktop.after.pathCount, 1);
|
|
assert.deepEqual(desktop.after.paths, [{ dataPath: "", arrayIndex: 0, idType: "OBJECT", group: "", groupMethod: "KEYINGSET", useEntireArray: true }]);
|
|
assert.deepEqual(desktop.before.action, desktop.after.action);
|
|
const sourceEngine = await factory({ wasmBinary });
|
|
const sourceHandle = sourceEngine._web_engine_create();
|
|
open(sourceEngine, sourceHandle, sourceBytes);
|
|
const sourceSnapshot = snapshot(sourceEngine, sourceHandle);
|
|
const sourceAnimation = sourceSnapshot.animations?.find((value) => value.name === "WebGapAnimKeyingSetPathAddAction");
|
|
assert.ok(sourceAnimation);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const afterSnapshot = snapshot(engine, handle);
|
|
const afterAnimation = afterSnapshot.animations?.find((value) => value.name === sourceAnimation.name);
|
|
assert.deepEqual(afterAnimation, sourceAnimation);
|
|
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).animations, afterSnapshot.animations);
|
|
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).animations, afterSnapshot.animations);
|
|
sourceEngine._web_engine_destroy(sourceHandle);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const beforeWasm = { animationId: sourceAnimation.id, targetId: sourceAnimation.targetId, channelPaths: sourceAnimation.channels.map((channel) => channel.path), keyframesPerChannel: sourceAnimation.channels.map((channel) => channel.keyframes.length) };
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_KEYING_SET_PATH_ADD_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, before: desktop.before, after: desktop.after }, wasm: { status: "EXACT", before: beforeWasm, after: { animationId: afterAnimation.id, keyframesPerChannel: afterAnimation.channels.map((channel) => channel.keyframes.length), visibleMainUnchanged: true, keyingSetMetadata: "NOT_EXPOSED_BY_SCENE_SNAPSHOT" } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00194" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00193/anim-keying-set-path-add-local-exact-report.json");
|
|
if (!fs.existsSync(reportPath)) fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} paths=0->1 poll=true operator=${desktop.operatorStatus} mainMutation=keying_set_path_added wasmVisibleMain=unchanged desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00194") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00194-operator-anim.keying_set_path_remove.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00194/anim-keying-set-path-remove-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const sourceBytes = fs.readFileSync(fixture);
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-keying-set-path-remove-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024, timeout: 45_000 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "KEYING_SET_PATH_REMOVED");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.equal(desktop.before.pathCount, 1);
|
|
assert.deepEqual(desktop.before.paths, [{ dataPath: '["path_remove_target"]', arrayIndex: 0, idType: "OBJECT", group: "", groupMethod: "KEYINGSET", useEntireArray: false }]);
|
|
assert.equal(desktop.after.pathCount, 0);
|
|
assert.deepEqual(desktop.after.paths, []);
|
|
assert.deepEqual(desktop.before.action, desktop.after.action);
|
|
const sourceEngine = await factory({ wasmBinary });
|
|
const sourceHandle = sourceEngine._web_engine_create();
|
|
open(sourceEngine, sourceHandle, sourceBytes);
|
|
const sourceSnapshot = snapshot(sourceEngine, sourceHandle);
|
|
const sourceAnimation = sourceSnapshot.animations?.find((value) => value.name === "WebGapAnimKeyingSetPathRemoveAction");
|
|
assert.ok(sourceAnimation);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const afterSnapshot = snapshot(engine, handle);
|
|
const afterAnimation = afterSnapshot.animations?.find((value) => value.name === sourceAnimation.name);
|
|
assert.deepEqual(afterAnimation, sourceAnimation);
|
|
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).animations, afterSnapshot.animations);
|
|
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).animations, afterSnapshot.animations);
|
|
sourceEngine._web_engine_destroy(sourceHandle);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const beforeWasm = { animationId: sourceAnimation.id, targetId: sourceAnimation.targetId, channelPaths: sourceAnimation.channels.map((channel) => channel.path), keyframesPerChannel: sourceAnimation.channels.map((channel) => channel.keyframes.length) };
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_KEYING_SET_PATH_REMOVE_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, before: desktop.before, after: desktop.after }, wasm: { status: "EXACT", before: beforeWasm, after: { animationId: afterAnimation.id, keyframesPerChannel: afterAnimation.channels.map((channel) => channel.keyframes.length), visibleMainUnchanged: true, keyingSetMetadata: "NOT_EXPOSED_BY_SCENE_SNAPSHOT" } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00195" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00194/anim-keying-set-path-remove-local-exact-report.json");
|
|
if (!fs.existsSync(reportPath)) fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} paths=1->0 poll=true operator=${desktop.operatorStatus} mainMutation=keying_set_path_removed wasmVisibleMain=unchanged desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00195") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00195-operator-anim.keying_set_remove.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00195/anim-keying-set-remove-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const sourceBytes = fs.readFileSync(fixture);
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-keying-set-remove-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024, timeout: 45_000 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "KEYING_SET_REMOVED");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.equal(desktop.before.keyingSetCount, 1);
|
|
assert.equal(desktop.before.activeKeyingSet, "WebGapAnimKeyingSetRemoveSet");
|
|
assert.equal(desktop.after.keyingSetCount, 0);
|
|
assert.equal(desktop.after.activeKeyingSet, null);
|
|
assert.deepEqual(desktop.before.action, desktop.after.action);
|
|
const sourceEngine = await factory({ wasmBinary });
|
|
const sourceHandle = sourceEngine._web_engine_create();
|
|
open(sourceEngine, sourceHandle, sourceBytes);
|
|
const sourceSnapshot = snapshot(sourceEngine, sourceHandle);
|
|
const sourceAnimation = sourceSnapshot.animations?.find((value) => value.name === "WebGapAnimKeyingSetRemoveAction");
|
|
assert.ok(sourceAnimation);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const afterSnapshot = snapshot(engine, handle);
|
|
const afterAnimation = afterSnapshot.animations?.find((value) => value.name === sourceAnimation.name);
|
|
assert.deepEqual(afterAnimation, sourceAnimation);
|
|
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).animations, afterSnapshot.animations);
|
|
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).animations, afterSnapshot.animations);
|
|
sourceEngine._web_engine_destroy(sourceHandle);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const beforeWasm = { animationId: sourceAnimation.id, targetId: sourceAnimation.targetId, channelPaths: sourceAnimation.channels.map((channel) => channel.path), keyframesPerChannel: sourceAnimation.channels.map((channel) => channel.keyframes.length) };
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_KEYING_SET_REMOVE_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, before: desktop.before, after: desktop.after }, wasm: { status: "EXACT", before: beforeWasm, after: { animationId: afterAnimation.id, keyframesPerChannel: afterAnimation.channels.map((channel) => channel.keyframes.length), visibleMainUnchanged: true, keyingSetMetadata: "NOT_EXPOSED_BY_SCENE_SNAPSHOT" } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00196" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00195/anim-keying-set-remove-local-exact-report.json");
|
|
if (!fs.existsSync(reportPath)) fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} keyingSets=1->0 poll=true operator=${desktop.operatorStatus} mainMutation=keying_set_removed wasmVisibleMain=unchanged desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00196") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00196-operator-anim.keyingset_button_add.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00196/anim-keyingset-button-add-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const sourceBytes = fs.readFileSync(fixture);
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-keyingset-button-add-desktop.py");
|
|
const run = spawnSync(process.env.XVFB_RUN_BIN ?? "xvfb-run", ["-a", "--server-args=-screen 0 1280x800x24", blender, "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024, timeout: 45_000 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "KEYING_SET_PATH_ADDED");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.equal(desktop.before.keyingSetCount, 0);
|
|
assert.equal(desktop.after.keyingSetCount, 1);
|
|
assert.equal(desktop.after.activeKeyingSet, "ButtonKeyingSet");
|
|
assert.deepEqual(desktop.after.paths, [{ dataPath: '["button_target"]', arrayIndex: 0, idType: "OBJECT", group: "", groupMethod: "KEYINGSET", useEntireArray: true }]);
|
|
assert.deepEqual(desktop.before.action, desktop.after.action);
|
|
const sourceEngine = await factory({ wasmBinary });
|
|
const sourceHandle = sourceEngine._web_engine_create();
|
|
open(sourceEngine, sourceHandle, sourceBytes);
|
|
const sourceSnapshot = snapshot(sourceEngine, sourceHandle);
|
|
const sourceAnimation = sourceSnapshot.animations?.find((value) => value.name === "WebGapAnimKeyingSetButtonAddAction");
|
|
assert.ok(sourceAnimation);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const afterSnapshot = snapshot(engine, handle);
|
|
const afterAnimation = afterSnapshot.animations?.find((value) => value.name === sourceAnimation.name);
|
|
assert.deepEqual(afterAnimation, sourceAnimation);
|
|
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).animations, afterSnapshot.animations);
|
|
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).animations, afterSnapshot.animations);
|
|
sourceEngine._web_engine_destroy(sourceHandle);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const beforeWasm = { animationId: sourceAnimation.id, targetId: sourceAnimation.targetId, channelPaths: sourceAnimation.channels.map((channel) => channel.path), keyframesPerChannel: sourceAnimation.channels.map((channel) => channel.keyframes.length) };
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_KEYINGSET_BUTTON_ADD_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, before: desktop.before, after: desktop.after }, wasm: { status: "EXACT", before: beforeWasm, after: { animationId: afterAnimation.id, keyframesPerChannel: afterAnimation.channels.map((channel) => channel.keyframes.length), visibleMainUnchanged: true, keyingSetMetadata: "NOT_EXPOSED_BY_SCENE_SNAPSHOT" } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00197" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00196/anim-keyingset-button-add-local-exact-report.json");
|
|
if (!fs.existsSync(reportPath)) fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} keyingSets=0->1 paths=0->1 poll=true operator=${desktop.operatorStatus} mainMutation=keying_set_path_added wasmVisibleMain=unchanged desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00197") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00197-operator-anim.keyingset_button_remove.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00197/anim-keyingset-button-remove-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const sourceBytes = fs.readFileSync(fixture);
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-keyingset-button-remove-desktop.py");
|
|
const run = spawnSync(process.env.XVFB_RUN_BIN ?? "xvfb-run", ["-a", "--server-args=-screen 0 1280x800x24", blender, "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024, timeout: 45_000 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "KEYING_SET_PATH_REMOVED");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.equal(desktop.before.keyingSetCount, 1);
|
|
assert.equal(desktop.before.activeKeyingSet, "ButtonKeyingSet");
|
|
assert.deepEqual(desktop.before.paths, [{ dataPath: '["button_remove_target"]', arrayIndex: 0, idType: "OBJECT", group: "", groupMethod: "KEYINGSET", useEntireArray: false }]);
|
|
assert.equal(desktop.after.keyingSetCount, 1);
|
|
assert.equal(desktop.after.activeKeyingSet, "ButtonKeyingSet");
|
|
assert.deepEqual(desktop.after.paths, []);
|
|
assert.deepEqual(desktop.before.action, desktop.after.action);
|
|
const sourceEngine = await factory({ wasmBinary });
|
|
const sourceHandle = sourceEngine._web_engine_create();
|
|
open(sourceEngine, sourceHandle, sourceBytes);
|
|
const sourceSnapshot = snapshot(sourceEngine, sourceHandle);
|
|
const sourceAnimation = sourceSnapshot.animations?.find((value) => value.name === "WebGapAnimKeyingSetButtonRemoveAction");
|
|
assert.ok(sourceAnimation);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const afterSnapshot = snapshot(engine, handle);
|
|
const afterAnimation = afterSnapshot.animations?.find((value) => value.name === sourceAnimation.name);
|
|
assert.deepEqual(afterAnimation, sourceAnimation);
|
|
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).animations, afterSnapshot.animations);
|
|
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).animations, afterSnapshot.animations);
|
|
sourceEngine._web_engine_destroy(sourceHandle);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const beforeWasm = { animationId: sourceAnimation.id, targetId: sourceAnimation.targetId, channelPaths: sourceAnimation.channels.map((channel) => channel.path), keyframesPerChannel: sourceAnimation.channels.map((channel) => channel.keyframes.length) };
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_KEYINGSET_BUTTON_REMOVE_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, before: desktop.before, after: desktop.after }, wasm: { status: "EXACT", before: beforeWasm, after: { animationId: afterAnimation.id, keyframesPerChannel: afterAnimation.channels.map((channel) => channel.keyframes.length), visibleMainUnchanged: true, keyingSetMetadata: "NOT_EXPOSED_BY_SCENE_SNAPSHOT" } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00198" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00197/anim-keyingset-button-remove-local-exact-report.json");
|
|
if (!fs.existsSync(reportPath)) fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} keyingSets=1 paths=1->0 poll=true operator=${desktop.operatorStatus} mainMutation=keying_set_path_removed wasmVisibleMain=unchanged desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00198") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00198-operator-anim.merge_animation.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00198/anim-merge-animation-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const sourceBytes = fs.readFileSync(fixture);
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-merge-animation-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024, timeout: 45_000 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "ANIMATION_MERGED");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.equal(desktop.before.activeObjectAction.name, "WebGapAnimMergeActiveAction");
|
|
assert.equal(desktop.before.sourceObjectAction.name, "WebGapAnimMergeSourceAction");
|
|
assert.equal(desktop.after.activeObjectAction.name, "WebGapAnimMergeActiveAction");
|
|
assert.equal(desktop.after.sourceObjectAction.name, "WebGapAnimMergeActiveAction");
|
|
assert.deepEqual(desktop.after.activeObjectAction.channels.map((channel) => channel.path), ["[\"active_merge_target\"]", "[\"source_merge_target\"]"]);
|
|
assert.deepEqual(desktop.after.sourceObjectAction.channels.map((channel) => channel.path), ["[\"active_merge_target\"]", "[\"source_merge_target\"]"]);
|
|
|
|
const sourceEngine = await factory({ wasmBinary });
|
|
const sourceHandle = sourceEngine._web_engine_create();
|
|
open(sourceEngine, sourceHandle, sourceBytes);
|
|
const sourceSnapshot = snapshot(sourceEngine, sourceHandle);
|
|
const sourceActiveAnimation = sourceSnapshot.animations?.find((value) => value.targetId === "object:WebGapAnimMergeActiveObject");
|
|
const sourceObjectAnimation = sourceSnapshot.animations?.find((value) => value.targetId === "object:WebGapAnimMergeSourceObject");
|
|
assert.ok(sourceActiveAnimation);
|
|
assert.ok(sourceObjectAnimation);
|
|
const sourceWasUnmerged = sourceObjectAnimation.name === "WebGapAnimMergeSourceAction";
|
|
if (sourceWasUnmerged) {
|
|
assert.equal(sourceActiveAnimation.name, "WebGapAnimMergeActiveAction");
|
|
assert.deepEqual(sourceActiveAnimation.channels.map((channel) => channel.path), ["[\"active_merge_target\"][0]"]);
|
|
assert.deepEqual(sourceObjectAnimation.channels.map((channel) => channel.path), ["[\"source_merge_target\"][0]"]);
|
|
assert.deepEqual(sourceActiveAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.frame)), [[1, 3, 5]]);
|
|
assert.deepEqual(sourceObjectAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.frame)), [[1, 3, 5]]);
|
|
}
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const mergedSnapshot = snapshot(engine, handle);
|
|
assert.equal(mergedSnapshot.activeObjectId, "object:WebGapAnimMergeActiveObject");
|
|
const mergedAnimations = mergedSnapshot.animations?.filter((value) => value.name === "WebGapAnimMergeActiveAction");
|
|
assert.equal(mergedAnimations?.length, 2);
|
|
assert.equal(mergedSnapshot.animations?.some((value) => value.name === "WebGapAnimMergeSourceAction"), false);
|
|
const mergedActiveAnimation = mergedAnimations.find((value) => value.targetId === "object:WebGapAnimMergeActiveObject");
|
|
const mergedSourceAnimation = mergedAnimations.find((value) => value.targetId === "object:WebGapAnimMergeSourceObject");
|
|
assert.ok(mergedActiveAnimation);
|
|
assert.ok(mergedSourceAnimation);
|
|
const mergedPaths = ["[\"active_merge_target\"][0]", "[\"source_merge_target\"][0]"];
|
|
assert.deepEqual(mergedActiveAnimation.channels.map((channel) => channel.path), mergedPaths);
|
|
assert.deepEqual(mergedSourceAnimation.channels.map((channel) => channel.path), mergedPaths);
|
|
assert.deepEqual(mergedActiveAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.frame)), [[1, 3, 5], [1, 3, 5]]);
|
|
assert.deepEqual(mergedSourceAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.value[0])), [[1, 3, 5], [10, 30, 50]]);
|
|
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).animations, mergedSnapshot.animations);
|
|
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).animations, mergedSnapshot.animations);
|
|
const beforeWasm = sourceWasUnmerged ? {
|
|
activeAnimationId: sourceActiveAnimation.id,
|
|
sourceAnimationId: sourceObjectAnimation.id,
|
|
activeChannelPaths: sourceActiveAnimation.channels.map((channel) => channel.path),
|
|
sourceChannelPaths: sourceObjectAnimation.channels.map((channel) => channel.path),
|
|
keyframesPerChannel: [sourceActiveAnimation.channels[0].keyframes.length, sourceObjectAnimation.channels[0].keyframes.length],
|
|
} : { evidenceStatus: "PRESERVED_DESKTOP_REPORT", activeAnimationId: sourceActiveAnimation.id, sourceAnimationId: sourceObjectAnimation.id };
|
|
const afterWasm = {
|
|
activeAnimationId: mergedActiveAnimation.id,
|
|
sourceAnimationId: mergedSourceAnimation.id,
|
|
channelPaths: mergedPaths,
|
|
keyframesPerChannel: mergedActiveAnimation.channels.map((channel) => channel.keyframes.length),
|
|
sourceValues: mergedSourceAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.value[0])),
|
|
};
|
|
sourceEngine._web_engine_destroy(sourceHandle);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_MERGE_ANIMATION_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, before: desktop.before, after: desktop.after }, wasm: { status: "EXACT", before: beforeWasm, after: afterWasm, sourceActionRemoved: true }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00199" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00198/anim-merge-animation-local-exact-report.json");
|
|
if (sourceWasUnmerged || !fs.existsSync(reportPath)) fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} actions=2->1 channels=1+1->2 poll=true operator=${desktop.operatorStatus} mainMutation=animation_merged desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00191") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00191-operator-anim.keying_set_add.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00191/anim-keying-set-add-desktop-report.json"); fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const sourceBytes = fs.readFileSync(fixture); const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"); const checker = path.join(root, "tools/web/check-action-keying-set-add-desktop.py");
|
|
const run = spawnSync(process.env.XVFB_RUN_BIN ?? "xvfb-run", ["-a", "--server-args=-screen 0 1280x800x24", blender, "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024, timeout: 45_000 }); assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`); assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8")); assert.equal(desktop.saveReopen, "EXACT"); assert.equal(desktop.mainMutation, "KEYING_SET_ADDED"); assert.equal(desktop.poll, true); assert.equal(desktop.operatorStatus, "FINISHED"); assert.equal(desktop.before.keyingSetCount, 0); assert.equal(desktop.after.keyingSetCount, 1); assert.deepEqual(desktop.before.action, desktop.after.action);
|
|
const sourceEngine = await factory({ wasmBinary }); const sourceHandle = sourceEngine._web_engine_create(); open(sourceEngine, sourceHandle, sourceBytes); const sourceSnapshot = snapshot(sourceEngine, sourceHandle); const sourceAnimation = sourceSnapshot.animations?.find((value) => value.name === "WebGapAnimKeyingSetAddAction"); assert.ok(sourceAnimation);
|
|
const engine = await factory({ wasmBinary }); const handle = engine._web_engine_create(); open(engine, handle, fs.readFileSync(fixture)); const afterSnapshot = snapshot(engine, handle); const afterAnimation = afterSnapshot.animations?.find((value) => value.name === sourceAnimation.name); assert.deepEqual(afterAnimation, sourceAnimation);
|
|
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).animations, afterSnapshot.animations);
|
|
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).animations, afterSnapshot.animations);
|
|
sourceEngine._web_engine_destroy(sourceHandle); engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened); const beforeWasm = { animationId: sourceAnimation.id, targetId: sourceAnimation.targetId, channelPaths: sourceAnimation.channels.map((channel) => channel.path), keyframesPerChannel: sourceAnimation.channels.map((channel) => channel.keyframes.length) };
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_KEYING_SET_ADD_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, before: desktop.before, after: desktop.after }, wasm: { status: "EXACT", before: beforeWasm, after: { animationId: afterAnimation.id, keyframesPerChannel: afterAnimation.channels.map((channel) => channel.keyframes.length), visibleMainUnchanged: true, keyingSetMetadata: "NOT_EXPOSED_BY_SCENE_SNAPSHOT" } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00192" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00191/anim-keying-set-add-local-exact-report.json"); if (!fs.existsSync(reportPath)) fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} keyingSets=0->1 poll=true operator=${desktop.operatorStatus} mainMutation=keying_set_added wasmVisibleMain=unchanged desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00190") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00190-operator-anim.keying_set_active_set.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00190/anim-keying-set-active-set-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const sourceBytes = fs.readFileSync(fixture); const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"); const checker = path.join(root, "tools/web/check-action-keying-set-active-set-desktop.py");
|
|
const run = spawnSync(process.env.XVFB_RUN_BIN ?? "xvfb-run", ["-a", "--server-args=-screen 0 1280x800x24", blender, "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024, timeout: 45_000 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`); assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8")); assert.equal(desktop.saveReopen, "EXACT"); assert.equal(desktop.mainMutation, "ACTIVE_KEYING_SET_CHANGED"); assert.equal(desktop.poll, true); assert.equal(desktop.operatorStatus, "FINISHED"); assert.equal(desktop.before.activeKeyingSet, "WebGapAnimKeyingSetActiveA"); assert.equal(desktop.after.activeKeyingSet, "WebGapAnimKeyingSetActiveB");
|
|
assert.deepEqual(desktop.before.action, desktop.after.action);
|
|
const sourceEngine = await factory({ wasmBinary }); const sourceHandle = sourceEngine._web_engine_create(); open(sourceEngine, sourceHandle, sourceBytes); const sourceSnapshot = snapshot(sourceEngine, sourceHandle); const sourceAnimation = sourceSnapshot.animations?.find((value) => value.name === "WebGapAnimKeyingSetActiveAction"); assert.ok(sourceAnimation);
|
|
const engine = await factory({ wasmBinary }); const handle = engine._web_engine_create(); open(engine, handle, fs.readFileSync(fixture)); const afterSnapshot = snapshot(engine, handle); const afterAnimation = afterSnapshot.animations?.find((value) => value.name === sourceAnimation.name); assert.deepEqual(afterAnimation, sourceAnimation);
|
|
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).animations, afterSnapshot.animations);
|
|
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).animations, afterSnapshot.animations);
|
|
sourceEngine._web_engine_destroy(sourceHandle); engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened);
|
|
const beforeWasm = { animationId: sourceAnimation.id, targetId: sourceAnimation.targetId, channelPaths: sourceAnimation.channels.map((channel) => channel.path), keyframesPerChannel: sourceAnimation.channels.map((channel) => channel.keyframes.length) };
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_KEYING_SET_ACTIVE_SET_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, before: desktop.before, after: desktop.after }, wasm: { status: "EXACT", before: beforeWasm, after: { animationId: afterAnimation.id, keyframesPerChannel: afterAnimation.channels.map((channel) => channel.keyframes.length), visibleMainUnchanged: true, keyingSetMetadata: "NOT_EXPOSED_BY_SCENE_SNAPSHOT" } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00191" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00190/anim-keying-set-active-set-local-exact-report.json"); if (!fs.existsSync(reportPath)) fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} active=A->B poll=true operator=${desktop.operatorStatus} mainMutation=active_keying_set_changed wasmVisibleMain=unchanged desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00189") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00189-operator-anim.keyframe_insert_menu.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00189/anim-keyframe-insert-menu-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const sourceBytes = fs.readFileSync(fixture);
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-keyframe-insert-menu-desktop.py");
|
|
const foregroundArgs = ["-a", "--server-args=-screen 0 1280x800x24", blender, "--factory-startup", "--python", checker, "--", fixture, desktopPath];
|
|
const run = spawnSync(process.env.XVFB_RUN_BIN ?? "xvfb-run", foregroundArgs, { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024, timeout: 45_000 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
const actionName = "WebGapAnimKeyframeInsertMenuAction";
|
|
const expectedBefore = { name: actionName, channels: [{ path: '["insert_target"]', index: 0, frames: [1, 5], values: [1, 5], selected: [true, true] }] };
|
|
const expectedAfter = { name: actionName, channels: [{ path: '["insert_target"]', index: 0, frames: [1, 3, 5], values: [1, 3, 5], selected: [false, true, false] }] };
|
|
assert.equal(desktop.saveReopen, "EXACT"); assert.equal(desktop.mainMutation, "KEYFRAME_INSERTED"); assert.equal(desktop.poll, true); assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.deepEqual(desktop.before.action, expectedBefore); assert.deepEqual(desktop.after.action, expectedAfter); assert.equal(desktop.before.activeKeyingSet, "WebGapAnimKeyframeInsertMenuSet");
|
|
const sourceEngine = await factory({ wasmBinary }); const sourceHandle = sourceEngine._web_engine_create(); open(sourceEngine, sourceHandle, sourceBytes);
|
|
const sourceSnapshot = snapshot(sourceEngine, sourceHandle); const sourceAnimation = sourceSnapshot.animations?.find((value) => value.name === actionName); assert.ok(sourceAnimation);
|
|
assert.equal(sourceAnimation.targetId, "object:WebGapAnimKeyframeInsertMenuObject"); assert.deepEqual(sourceAnimation.channels.map((channel) => channel.path), ['["insert_target"][0]']); assert.ok([2, 3].includes(sourceAnimation.channels[0].keyframes.length));
|
|
if (sourceAnimation.channels[0].keyframes.length === 2) assert.deepEqual(sourceAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.frame)), [[1, 5]]);
|
|
const engine = await factory({ wasmBinary }); const handle = engine._web_engine_create(); open(engine, handle, fs.readFileSync(fixture));
|
|
const insertedSnapshot = snapshot(engine, handle); const insertedAnimation = insertedSnapshot.animations?.find((value) => value.name === actionName); assert.ok(insertedAnimation);
|
|
assert.deepEqual(insertedAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.frame)), [[1, 3, 5]]); assert.deepEqual(insertedAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.value[0])), [[1, 3, 5]]);
|
|
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).animations, insertedSnapshot.animations);
|
|
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), insertedSnapshot); sourceEngine._web_engine_destroy(sourceHandle); engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened);
|
|
const beforeWasm = { animationId: sourceAnimation.id, targetId: sourceAnimation.targetId, channelPaths: sourceAnimation.channels.map((channel) => channel.path), keyframesPerChannel: sourceAnimation.channels.map((channel) => channel.keyframes.length) };
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_KEYFRAME_INSERT_MENU_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, before: desktop.before.action, after: desktop.after.action }, wasm: { status: "EXACT", before: beforeWasm, after: { animationId: insertedAnimation.id, keyframesPerChannel: insertedAnimation.channels.map((channel) => channel.keyframes.length), keyframeInserted: true } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00190" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00189/anim-keyframe-insert-menu-local-exact-report.json"); if (beforeWasm.keyframesPerChannel[0] === 2 || !fs.existsSync(reportPath)) fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} channels=${beforeWasm.keyframesPerChannel[0]}->${insertedAnimation.channels[0].keyframes.length} poll=true operator=${desktop.operatorStatus} mainMutation=keyframe_inserted desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00188") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00188-operator-anim.keyframe_insert_by_name.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00188/anim-keyframe-insert-by-name-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const sourceBytes = fs.readFileSync(fixture);
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-keyframe-insert-by-name-desktop.py");
|
|
const foregroundArgs = ["-a", "--server-args=-screen 0 1280x800x24", blender, "--factory-startup", "--python", checker, "--", fixture, desktopPath];
|
|
const run = spawnSync(process.env.XVFB_RUN_BIN ?? "xvfb-run", foregroundArgs, { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024, timeout: 45_000 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
const actionName = "WebGapAnimKeyframeInsertByNameAction";
|
|
const expectedBefore = { name: actionName, channels: [{ path: '["insert_target"]', index: 0, frames: [1, 5], values: [1, 5], selected: [true, true] }] };
|
|
const expectedAfter = { name: actionName, channels: [{ path: '["insert_target"]', index: 0, frames: [1, 3, 5], values: [1, 3, 5], selected: [false, true, false] }] };
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "KEYFRAME_INSERTED");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.deepEqual(desktop.before.action, expectedBefore);
|
|
assert.deepEqual(desktop.after.action, expectedAfter);
|
|
assert.equal(desktop.before.activeKeyingSet, "WebGapAnimKeyframeInsertByNameSet");
|
|
const sourceEngine = await factory({ wasmBinary });
|
|
const sourceHandle = sourceEngine._web_engine_create();
|
|
open(sourceEngine, sourceHandle, sourceBytes);
|
|
const sourceSnapshot = snapshot(sourceEngine, sourceHandle);
|
|
const sourceAnimation = sourceSnapshot.animations?.find((value) => value.name === actionName);
|
|
assert.ok(sourceAnimation);
|
|
assert.equal(sourceAnimation.targetId, "object:WebGapAnimKeyframeInsertByNameObject");
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.path), ['["insert_target"][0]']);
|
|
assert.ok([2, 3].includes(sourceAnimation.channels[0].keyframes.length));
|
|
if (sourceAnimation.channels[0].keyframes.length === 2) assert.deepEqual(sourceAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.frame)), [[1, 5]]);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const insertedSnapshot = snapshot(engine, handle);
|
|
const insertedAnimation = insertedSnapshot.animations?.find((value) => value.name === actionName);
|
|
assert.ok(insertedAnimation);
|
|
assert.deepEqual(insertedAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.frame)), [[1, 3, 5]]);
|
|
assert.deepEqual(insertedAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.value[0])), [[1, 3, 5]]);
|
|
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).animations, insertedSnapshot.animations);
|
|
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), insertedSnapshot);
|
|
sourceEngine._web_engine_destroy(sourceHandle); engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened);
|
|
const beforeWasm = { animationId: sourceAnimation.id, targetId: sourceAnimation.targetId, channelPaths: sourceAnimation.channels.map((channel) => channel.path), keyframesPerChannel: sourceAnimation.channels.map((channel) => channel.keyframes.length) };
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_KEYFRAME_INSERT_BY_NAME_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, before: desktop.before.action, after: desktop.after.action }, wasm: { status: "EXACT", before: beforeWasm, after: { animationId: insertedAnimation.id, keyframesPerChannel: insertedAnimation.channels.map((channel) => channel.keyframes.length), keyframeInserted: true } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00189" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00188/anim-keyframe-insert-by-name-local-exact-report.json");
|
|
if (beforeWasm.keyframesPerChannel[0] === 2 || !fs.existsSync(reportPath)) fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} channels=${beforeWasm.keyframesPerChannel[0]}->${insertedAnimation.channels[0].keyframes.length} poll=true operator=${desktop.operatorStatus} mainMutation=keyframe_inserted desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00187") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00187-operator-anim.keyframe_insert_button.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00187/anim-keyframe-insert-button-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const sourceBytes = fs.readFileSync(fixture);
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-keyframe-insert-button-desktop.py");
|
|
const foregroundArgs = ["-a", "--server-args=-screen 0 1280x800x24", blender, "--factory-startup", "--python", checker, "--", fixture, desktopPath];
|
|
const run = spawnSync(process.env.XVFB_RUN_BIN ?? "xvfb-run", foregroundArgs, { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024, timeout: 45_000 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
const actionName = "WebGapAnimKeyframeInsertButtonAction";
|
|
const expectedBefore = { name: actionName, channels: [{ path: '["insert_target"]', index: 0, frames: [1, 5], values: [1, 5], selected: [true, true] }] };
|
|
const expectedAfter = { name: actionName, channels: [{ path: '["insert_target"]', index: 0, frames: [1, 3, 5], values: [1, 3, 5], selected: [false, true, false] }] };
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "KEYFRAME_INSERTED");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.deepEqual(desktop.before.action, expectedBefore);
|
|
assert.deepEqual(desktop.after.action, expectedAfter);
|
|
|
|
const sourceEngine = await factory({ wasmBinary });
|
|
const sourceHandle = sourceEngine._web_engine_create();
|
|
open(sourceEngine, sourceHandle, sourceBytes);
|
|
const sourceSnapshot = snapshot(sourceEngine, sourceHandle);
|
|
const sourceAnimation = sourceSnapshot.animations?.find((value) => value.name === actionName);
|
|
assert.ok(sourceAnimation);
|
|
assert.equal(sourceAnimation.targetId, "object:WebGapAnimKeyframeInsertButtonObject");
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.path), ['["insert_target"][0]']);
|
|
assert.ok([2, 3].includes(sourceAnimation.channels[0].keyframes.length));
|
|
if (sourceAnimation.channels[0].keyframes.length === 2) {
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.frame)), [[1, 5]]);
|
|
}
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const insertedSnapshot = snapshot(engine, handle);
|
|
const insertedAnimation = insertedSnapshot.animations?.find((value) => value.name === actionName);
|
|
assert.ok(insertedAnimation);
|
|
assert.deepEqual(insertedAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.frame)), [[1, 3, 5]]);
|
|
assert.deepEqual(insertedAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.value[0])), [[1, 3, 5]]);
|
|
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).animations, insertedSnapshot.animations);
|
|
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), insertedSnapshot);
|
|
sourceEngine._web_engine_destroy(sourceHandle); engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened);
|
|
const beforeWasm = { animationId: sourceAnimation.id, targetId: sourceAnimation.targetId, channelPaths: sourceAnimation.channels.map((channel) => channel.path), keyframesPerChannel: sourceAnimation.channels.map((channel) => channel.keyframes.length) };
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_KEYFRAME_INSERT_BUTTON_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, before: desktop.before.action, after: desktop.after.action }, wasm: { status: "EXACT", before: beforeWasm, after: { animationId: insertedAnimation.id, keyframesPerChannel: insertedAnimation.channels.map((channel) => channel.keyframes.length), keyframeInserted: true } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00188" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00187/anim-keyframe-insert-button-local-exact-report.json");
|
|
if (beforeWasm.keyframesPerChannel[0] === 2 || !fs.existsSync(reportPath)) fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} channels=${beforeWasm.keyframesPerChannel[0]}->${insertedAnimation.channels[0].keyframes.length} poll=true operator=${desktop.operatorStatus} mainMutation=keyframe_inserted desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00186") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00186-operator-anim.keyframe_insert.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00186/anim-keyframe-insert-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const sourceBytes = fs.readFileSync(fixture);
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-keyframe-insert-desktop.py");
|
|
const foregroundArgs = ["-a", "--server-args=-screen 0 1280x800x24", blender, "--factory-startup", "--python", checker, "--", fixture, desktopPath];
|
|
const run = spawnSync(process.env.XVFB_RUN_BIN ?? "xvfb-run", foregroundArgs, { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024, timeout: 45_000 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
const actionName = "WebGapAnimKeyframeInsertAction";
|
|
const expectedBefore = {
|
|
name: actionName,
|
|
channels: [{ path: '["insert_target"]', index: 0, frames: [1, 5], values: [1, 5], selected: [true, true] }],
|
|
};
|
|
const expectedAfter = {
|
|
name: actionName,
|
|
channels: [{ path: '["insert_target"]', index: 0, frames: [1, 3, 5], values: [1, 3, 5], selected: [false, true, false] }],
|
|
};
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "KEYFRAME_INSERTED");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.deepEqual(desktop.before.action, expectedBefore);
|
|
assert.deepEqual(desktop.after.action, expectedAfter);
|
|
assert.equal(desktop.before.activeKeyingSet, "WebGapAnimKeyframeInsertSet");
|
|
|
|
const sourceEngine = await factory({ wasmBinary });
|
|
const sourceHandle = sourceEngine._web_engine_create();
|
|
open(sourceEngine, sourceHandle, sourceBytes);
|
|
const sourceSnapshot = snapshot(sourceEngine, sourceHandle);
|
|
const sourceAnimation = sourceSnapshot.animations?.find((value) => value.name === actionName);
|
|
assert.ok(sourceAnimation);
|
|
assert.equal(sourceAnimation.targetId, "object:WebGapAnimKeyframeInsertObject");
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.path), ['["insert_target"][0]']);
|
|
assert.ok([2, 3].includes(sourceAnimation.channels[0].keyframes.length));
|
|
if (sourceAnimation.channels[0].keyframes.length === 2) {
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.frame)), [[1, 5]]);
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.value[0])), [[1, 5]]);
|
|
}
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const insertedSnapshot = snapshot(engine, handle);
|
|
const insertedAnimation = insertedSnapshot.animations?.find((value) => value.name === actionName);
|
|
assert.ok(insertedAnimation);
|
|
assert.deepEqual(insertedAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.frame)), [[1, 3, 5]]);
|
|
assert.deepEqual(insertedAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.value[0])), [[1, 3, 5]]);
|
|
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).animations, insertedSnapshot.animations);
|
|
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), insertedSnapshot);
|
|
sourceEngine._web_engine_destroy(sourceHandle);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const beforeWasm = { animationId: sourceAnimation.id, targetId: sourceAnimation.targetId, channelPaths: sourceAnimation.channels.map((channel) => channel.path), keyframesPerChannel: sourceAnimation.channels.map((channel) => channel.keyframes.length) };
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_KEYFRAME_INSERT_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, before: desktop.before.action, after: desktop.after.action }, wasm: { status: "EXACT", before: beforeWasm, after: { animationId: insertedAnimation.id, keyframesPerChannel: insertedAnimation.channels.map((channel) => channel.keyframes.length), keyframeInserted: true } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00187" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00186/anim-keyframe-insert-local-exact-report.json");
|
|
if (beforeWasm.keyframesPerChannel[0] === 2 || !fs.existsSync(reportPath)) fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} channels=${beforeWasm.keyframesPerChannel[0]}->${insertedAnimation.channels[0].keyframes.length} poll=true operator=${desktop.operatorStatus} mainMutation=keyframe_inserted desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00185") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00185-operator-anim.keyframe_delete_vse.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00185/anim-keyframe-delete-vse-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const sourceBytes = fs.readFileSync(fixture);
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-keyframe-delete-vse-desktop.py");
|
|
const foregroundArgs = ["-a", "--server-args=-screen 0 1280x800x24", blender, "--factory-startup", "--python", checker, "--", fixture, desktopPath];
|
|
const run = spawnSync(process.env.XVFB_RUN_BIN ?? "xvfb-run", foregroundArgs, { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024, timeout: 45_000 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
const actionName = "WebGapAnimKeyframeDeleteVSEAction";
|
|
const desktopChannelPath = 'sequence_editor.strips_all["WebGapAnimKeyframeDeleteVSEStrip"].blend_alpha';
|
|
const channelPath = `${desktopChannelPath}[0]`;
|
|
const expectedBefore = {
|
|
name: actionName,
|
|
channels: [{ path: desktopChannelPath, index: 0, frames: [1, 3, 5], values: [0.25, 0.5, 0.75], selected: [true, true, true] }],
|
|
};
|
|
const expectedAfter = {
|
|
name: actionName,
|
|
channels: [{ path: desktopChannelPath, index: 0, frames: [1, 5], values: [0.25, 0.75], selected: [true, true] }],
|
|
};
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "KEYFRAME_DELETED");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.deepEqual(desktop.before.action, expectedBefore);
|
|
assert.deepEqual(desktop.after.action, expectedAfter);
|
|
assert.equal(desktop.before.active, true);
|
|
assert.equal(desktop.before.selected, true);
|
|
|
|
const sourceEngine = await factory({ wasmBinary });
|
|
const sourceHandle = sourceEngine._web_engine_create();
|
|
open(sourceEngine, sourceHandle, sourceBytes);
|
|
const sourceSnapshot = snapshot(sourceEngine, sourceHandle);
|
|
const sourceAnimation = sourceSnapshot.animations?.find((value) => value.name === actionName);
|
|
assert.ok(sourceAnimation);
|
|
assert.equal(sourceAnimation.targetId, "scene:Scene");
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.path), [channelPath]);
|
|
assert.ok([2, 3].includes(sourceAnimation.channels[0].keyframes.length));
|
|
if (sourceAnimation.channels[0].keyframes.length === 3) {
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.frame)), [[1, 3, 5]]);
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.value[0])), [[0.25, 0.5, 0.75]]);
|
|
}
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const clearedSnapshot = snapshot(engine, handle);
|
|
const clearedAnimation = clearedSnapshot.animations?.find((value) => value.name === actionName);
|
|
assert.ok(clearedAnimation);
|
|
assert.deepEqual(clearedAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.frame)), [[1, 5]]);
|
|
assert.deepEqual(clearedAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.value[0])), [[0.25, 0.75]]);
|
|
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).animations, clearedSnapshot.animations);
|
|
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), clearedSnapshot);
|
|
sourceEngine._web_engine_destroy(sourceHandle);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const beforeWasm = { animationId: sourceAnimation.id, targetId: sourceAnimation.targetId, channelPaths: sourceAnimation.channels.map((channel) => channel.path), keyframesPerChannel: sourceAnimation.channels.map((channel) => channel.keyframes.length) };
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_KEYFRAME_DELETE_VSE_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, before: desktop.before.action, after: desktop.after.action }, wasm: { status: "EXACT", before: beforeWasm, after: { animationId: clearedAnimation.id, keyframesPerChannel: clearedAnimation.channels.map((channel) => channel.keyframes.length), keyframeDeleted: true } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00186" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00185/anim-keyframe-delete-vse-local-exact-report.json");
|
|
if (beforeWasm.keyframesPerChannel[0] === 3 || !fs.existsSync(reportPath)) fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} channels=${beforeWasm.keyframesPerChannel[0]}->${clearedAnimation.channels[0].keyframes.length} poll=true operator=${desktop.operatorStatus} mainMutation=keyframe_deleted desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00184") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00184-operator-anim.keyframe_delete_v3d.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00184/anim-keyframe-delete-v3d-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const sourceBytes = fs.readFileSync(fixture);
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-keyframe-delete-v3d-desktop.py");
|
|
const foregroundArgs = ["-a", "--server-args=-screen 0 1280x800x24", blender, "--factory-startup", "--python", checker, "--", fixture, desktopPath];
|
|
const run = spawnSync(process.env.XVFB_RUN_BIN ?? "xvfb-run", foregroundArgs, { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024, timeout: 45_000 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
const actionName = "WebGapAnimKeyframeDeleteV3DAction";
|
|
const expectedBefore = {
|
|
name: actionName,
|
|
channels: [{ path: '["delete_target"]', index: 0, frames: [1, 3, 5], values: [1, 3, 5], selected: [true, true, true] }],
|
|
};
|
|
const expectedAfter = {
|
|
name: actionName,
|
|
channels: [{ path: '["delete_target"]', index: 0, frames: [1, 5], values: [1, 5], selected: [true, true] }],
|
|
};
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "KEYFRAME_DELETED");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.deepEqual(desktop.before.action, expectedBefore);
|
|
assert.deepEqual(desktop.after.action, expectedAfter);
|
|
|
|
const sourceEngine = await factory({ wasmBinary });
|
|
const sourceHandle = sourceEngine._web_engine_create();
|
|
open(sourceEngine, sourceHandle, sourceBytes);
|
|
const sourceSnapshot = snapshot(sourceEngine, sourceHandle);
|
|
const sourceAnimation = sourceSnapshot.animations?.find((value) => value.name === actionName);
|
|
assert.ok(sourceAnimation);
|
|
assert.equal(sourceAnimation.targetId, "object:WebGapAnimKeyframeDeleteV3DObject");
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.path), ['["delete_target"][0]']);
|
|
assert.ok([2, 3].includes(sourceAnimation.channels[0].keyframes.length));
|
|
if (sourceAnimation.channels[0].keyframes.length === 3) {
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.frame)), [[1, 3, 5]]);
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.value[0])), [[1, 3, 5]]);
|
|
}
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const clearedSnapshot = snapshot(engine, handle);
|
|
const clearedAnimation = clearedSnapshot.animations?.find((value) => value.name === actionName);
|
|
assert.ok(clearedAnimation);
|
|
assert.deepEqual(clearedAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.frame)), [[1, 5]]);
|
|
assert.deepEqual(clearedAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.value[0])), [[1, 5]]);
|
|
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).animations, clearedSnapshot.animations);
|
|
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), clearedSnapshot);
|
|
sourceEngine._web_engine_destroy(sourceHandle);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const beforeWasm = { animationId: sourceAnimation.id, targetId: sourceAnimation.targetId, channelPaths: sourceAnimation.channels.map((channel) => channel.path), keyframesPerChannel: sourceAnimation.channels.map((channel) => channel.keyframes.length) };
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_KEYFRAME_DELETE_V3D_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, before: desktop.before.action, after: desktop.after.action }, wasm: { status: "EXACT", before: beforeWasm, after: { animationId: clearedAnimation.id, keyframesPerChannel: clearedAnimation.channels.map((channel) => channel.keyframes.length), keyframeDeleted: true } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00185" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00184/anim-keyframe-delete-v3d-local-exact-report.json");
|
|
if (beforeWasm.keyframesPerChannel[0] === 3 || !fs.existsSync(reportPath)) fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} channels=${beforeWasm.keyframesPerChannel[0]}->${clearedAnimation.channels[0].keyframes.length} poll=true operator=${desktop.operatorStatus} mainMutation=keyframe_deleted desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00183") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00183-operator-anim.keyframe_delete_by_name.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00183/anim-keyframe-delete-by-name-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const sourceBytes = fs.readFileSync(fixture);
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-keyframe-delete-by-name-desktop.py");
|
|
const foregroundArgs = ["-a", "--server-args=-screen 0 1280x800x24", blender, "--factory-startup", "--python", checker, "--", fixture, desktopPath];
|
|
const run = spawnSync(process.env.XVFB_RUN_BIN ?? "xvfb-run", foregroundArgs, { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024, timeout: 45_000 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
const actionName = "WebGapAnimKeyframeDeleteByNameAction";
|
|
const keyingSetName = "WebGapAnimKeyframeDeleteByNameSet";
|
|
const expectedBefore = {
|
|
name: actionName,
|
|
channels: [{ path: '["delete_target"]', index: 0, frames: [1, 3, 5], values: [1, 3, 5], selected: [true, true, true] }],
|
|
};
|
|
const expectedAfter = {
|
|
name: actionName,
|
|
channels: [{ path: '["delete_target"]', index: 0, frames: [1, 5], values: [1, 5], selected: [true, true] }],
|
|
};
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "KEYFRAME_DELETED");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.deepEqual(desktop.before.action, expectedBefore);
|
|
assert.deepEqual(desktop.after.action, expectedAfter);
|
|
assert.equal(desktop.before.activeKeyingSet, keyingSetName);
|
|
|
|
const sourceEngine = await factory({ wasmBinary });
|
|
const sourceHandle = sourceEngine._web_engine_create();
|
|
open(sourceEngine, sourceHandle, sourceBytes);
|
|
const sourceSnapshot = snapshot(sourceEngine, sourceHandle);
|
|
const sourceAnimation = sourceSnapshot.animations?.find((value) => value.name === actionName);
|
|
assert.ok(sourceAnimation);
|
|
assert.equal(sourceAnimation.targetId, "object:WebGapAnimKeyframeDeleteByNameObject");
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.path), ['["delete_target"][0]']);
|
|
assert.ok([2, 3].includes(sourceAnimation.channels[0].keyframes.length));
|
|
if (sourceAnimation.channels[0].keyframes.length === 3) {
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.frame)), [[1, 3, 5]]);
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.value[0])), [[1, 3, 5]]);
|
|
}
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const clearedSnapshot = snapshot(engine, handle);
|
|
const clearedAnimation = clearedSnapshot.animations?.find((value) => value.name === actionName);
|
|
assert.ok(clearedAnimation);
|
|
assert.deepEqual(clearedAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.frame)), [[1, 5]]);
|
|
assert.deepEqual(clearedAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.value[0])), [[1, 5]]);
|
|
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).animations, clearedSnapshot.animations);
|
|
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), clearedSnapshot);
|
|
sourceEngine._web_engine_destroy(sourceHandle);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const beforeWasm = { animationId: sourceAnimation.id, targetId: sourceAnimation.targetId, channelPaths: sourceAnimation.channels.map((channel) => channel.path), keyframesPerChannel: sourceAnimation.channels.map((channel) => channel.keyframes.length) };
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_KEYFRAME_DELETE_BY_NAME_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, before: desktop.before.action, after: desktop.after.action }, wasm: { status: "EXACT", before: beforeWasm, after: { animationId: clearedAnimation.id, keyframesPerChannel: clearedAnimation.channels.map((channel) => channel.keyframes.length), keyframeDeleted: true } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00184" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00183/anim-keyframe-delete-by-name-local-exact-report.json");
|
|
if (beforeWasm.keyframesPerChannel[0] === 3 || !fs.existsSync(reportPath)) fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} channels=${beforeWasm.keyframesPerChannel[0]}->${clearedAnimation.channels[0].keyframes.length} poll=true operator=${desktop.operatorStatus} mainMutation=keyframe_deleted desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00181") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00181-operator-anim.keyframe_delete.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00181/anim-keyframe-delete-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const sourceBytes = fs.readFileSync(fixture);
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-keyframe-delete-desktop.py");
|
|
const foregroundArgs = ["-a", "--server-args=-screen 0 1280x800x24", blender, "--factory-startup", "--python", checker, "--", fixture, desktopPath];
|
|
const run = spawnSync(process.env.XVFB_RUN_BIN ?? "xvfb-run", foregroundArgs, { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024, timeout: 45_000 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
const actionName = "WebGapAnimKeyframeDeleteAction";
|
|
const expectedBefore = {
|
|
name: actionName,
|
|
channels: [{ path: '["delete_target"]', index: 0, frames: [1, 3, 5], values: [1, 3, 5], selected: [true, true, true] }],
|
|
};
|
|
const expectedAfter = {
|
|
name: actionName,
|
|
channels: [{ path: '["delete_target"]', index: 0, frames: [1, 5], values: [1, 5], selected: [true, true] }],
|
|
};
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "KEYFRAME_DELETED");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.deepEqual(desktop.before.action, expectedBefore);
|
|
assert.deepEqual(desktop.after.action, expectedAfter);
|
|
assert.equal(desktop.before.activeKeyingSet, "WebGapAnimKeyframeDeleteSet");
|
|
|
|
const sourceEngine = await factory({ wasmBinary });
|
|
const sourceHandle = sourceEngine._web_engine_create();
|
|
open(sourceEngine, sourceHandle, sourceBytes);
|
|
const sourceSnapshot = snapshot(sourceEngine, sourceHandle);
|
|
const sourceAnimation = sourceSnapshot.animations?.find((value) => value.name === actionName);
|
|
assert.ok(sourceAnimation);
|
|
assert.equal(sourceAnimation.targetId, "object:WebGapAnimKeyframeDeleteObject");
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.path), ['["delete_target"][0]']);
|
|
assert.ok([2, 3].includes(sourceAnimation.channels[0].keyframes.length));
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
const clearedBytes = fs.readFileSync(fixture);
|
|
open(engine, handle, clearedBytes);
|
|
const clearedSnapshot = snapshot(engine, handle);
|
|
const clearedAnimation = clearedSnapshot.animations?.find((value) => value.name === actionName);
|
|
assert.ok(clearedAnimation);
|
|
assert.deepEqual(clearedAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.frame)), [[1, 5]]);
|
|
assert.deepEqual(clearedAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.value[0])), [[1, 5]]);
|
|
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).animations, clearedSnapshot.animations);
|
|
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), clearedSnapshot);
|
|
sourceEngine._web_engine_destroy(sourceHandle);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const beforeWasm = { animationId: sourceAnimation.id, targetId: sourceAnimation.targetId, channelPaths: sourceAnimation.channels.map((channel) => channel.path), keyframesPerChannel: sourceAnimation.channels.map((channel) => channel.keyframes.length) };
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_KEYFRAME_DELETE_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, before: desktop.before.action, after: desktop.after.action }, wasm: { status: "EXACT", before: beforeWasm, after: { animationId: clearedAnimation.id, keyframesPerChannel: clearedAnimation.channels.map((channel) => channel.keyframes.length), keyframeDeleted: true } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00182" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00181/anim-keyframe-delete-local-exact-report.json");
|
|
if (beforeWasm.keyframesPerChannel[0] === 3 || !fs.existsSync(reportPath)) fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} channels=${beforeWasm.keyframesPerChannel[0]}->${clearedAnimation.channels[0].keyframes.length} poll=true operator=${desktop.operatorStatus} mainMutation=keyframe_deleted desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00180") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00180-operator-anim.keyframe_clear_vse.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00180/anim-keyframe-clear-vse-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const sourceBytes = fs.readFileSync(fixture);
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-keyframe-clear-vse-desktop.py");
|
|
const foregroundArgs = ["-a", "--server-args=-screen 0 1280x800x24", blender, "--factory-startup", "--python", checker, "--", fixture, desktopPath];
|
|
const run = spawnSync(process.env.XVFB_RUN_BIN ?? "xvfb-run", foregroundArgs, { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024, timeout: 45_000 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
const actionName = "WebGapAnimKeyframeClearVSEAction";
|
|
const desktopChannelPath = 'sequence_editor.strips_all["WebGapAnimKeyframeClearVSEStrip"].blend_alpha';
|
|
const channelPath = `${desktopChannelPath}[0]`;
|
|
const expectedBefore = {
|
|
name: actionName,
|
|
channels: [{ path: desktopChannelPath, index: 0, frames: [1, 3, 5], values: [0.25, 0.5, 0.75], selected: [true, true, true] }],
|
|
};
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "ANIMATION_CLEARED");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.deepEqual(desktop.before.action, expectedBefore);
|
|
assert.deepEqual(desktop.after.action, { name: actionName, channels: [] });
|
|
|
|
const sourceEngine = await factory({ wasmBinary });
|
|
const sourceHandle = sourceEngine._web_engine_create();
|
|
open(sourceEngine, sourceHandle, sourceBytes);
|
|
const sourceSnapshot = snapshot(sourceEngine, sourceHandle);
|
|
const sourceAnimation = sourceSnapshot.animations?.find((value) => value.name === actionName);
|
|
if (sourceAnimation) {
|
|
assert.equal(sourceAnimation.targetId, "scene:Scene");
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.path), [channelPath]);
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.frame)), [[1, 3, 5]]);
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.value[0])), [[0.25, 0.5, 0.75]]);
|
|
}
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
const clearedBytes = fs.readFileSync(fixture);
|
|
open(engine, handle, clearedBytes);
|
|
const clearedSnapshot = snapshot(engine, handle);
|
|
assert.equal(clearedSnapshot.animations?.some((value) => value.name === actionName), false);
|
|
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).animations, clearedSnapshot.animations);
|
|
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), clearedSnapshot);
|
|
sourceEngine._web_engine_destroy(sourceHandle);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const beforeWasm = sourceAnimation ? { animationId: sourceAnimation.id, targetId: sourceAnimation.targetId, channelPaths: sourceAnimation.channels.map((channel) => channel.path), keyframesPerChannel: sourceAnimation.channels.map((channel) => channel.keyframes.length) } : { animationId: `action:${actionName}:scene:Scene`, targetId: "scene:Scene", channelPaths: [channelPath], keyframesPerChannel: [3], evidenceStatus: "PRESERVED_DESKTOP_REPORT" };
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_KEYFRAME_CLEAR_VSE_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, before: desktop.before.action, after: desktop.after.action }, wasm: { status: "EXACT", before: beforeWasm, after: { animationCount: clearedSnapshot.animations.length, animationCleared: true } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00181" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00180/anim-keyframe-clear-vse-local-exact-report.json");
|
|
if (sourceAnimation || !fs.existsSync(reportPath)) fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} channels=${beforeWasm.keyframesPerChannel.length}->0 poll=true operator=${desktop.operatorStatus} mainMutation=animation_cleared desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00179") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00179-operator-anim.keyframe_clear_v3d.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00179/anim-keyframe-clear-v3d-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const sourceBytes = fs.readFileSync(fixture);
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-keyframe-clear-v3d-desktop.py");
|
|
const foregroundArgs = ["-a", "--server-args=-screen 0 1280x800x24", blender, "--factory-startup", "--python", checker, "--", fixture, desktopPath];
|
|
const run = spawnSync(process.env.XVFB_RUN_BIN ?? "xvfb-run", foregroundArgs, { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024, timeout: 45_000 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
const actionName = "WebGapAnimKeyframeClearV3DAction";
|
|
const expectedBefore = {
|
|
name: actionName,
|
|
channels: [{ path: '["clear_target"]', index: 0, frames: [1, 3, 5], values: [1, 3, 5], selected: [true, true, true] }],
|
|
};
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "ANIMATION_CLEARED");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.deepEqual(desktop.before, { selected: true, active: true, value: 3, action: expectedBefore });
|
|
assert.deepEqual(desktop.after, { selected: true, active: true, value: 3, action: { name: actionName, channels: [] } });
|
|
|
|
const sourceEngine = await factory({ wasmBinary });
|
|
const sourceHandle = sourceEngine._web_engine_create();
|
|
open(sourceEngine, sourceHandle, sourceBytes);
|
|
const sourceSnapshot = snapshot(sourceEngine, sourceHandle);
|
|
const sourceAnimation = sourceSnapshot.animations?.find((value) => value.targetId === "object:WebGapAnimKeyframeClearV3DObject");
|
|
if (sourceAnimation) {
|
|
assert.equal(sourceAnimation.id, `action:${actionName}:object:WebGapAnimKeyframeClearV3DObject`);
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.path), ['["clear_target"][0]']);
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.frame)), [[1, 3, 5]]);
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.value[0])), [[1, 3, 5]]);
|
|
assert.deepEqual(sourceAnimation.channels.map((channel) => channel.keyframes.map((keyframe) => keyframe.selected)), [[true, true, true]]);
|
|
}
|
|
|
|
const clearedBytes = fs.readFileSync(fixture);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, clearedBytes);
|
|
const clearedSnapshot = snapshot(engine, handle);
|
|
assert.equal(clearedSnapshot.animations?.some((value) => value.targetId === "object:WebGapAnimKeyframeClearV3DObject"), false);
|
|
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).animations, clearedSnapshot.animations);
|
|
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), clearedSnapshot);
|
|
sourceEngine._web_engine_destroy(sourceHandle);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const beforeWasm = sourceAnimation ? { animationId: sourceAnimation.id, channelPaths: sourceAnimation.channels.map((channel) => channel.path), keyframesPerChannel: sourceAnimation.channels.map((channel) => channel.keyframes.length) } : { animationId: `action:${actionName}:object:WebGapAnimKeyframeClearV3DObject`, channelPaths: ['["clear_target"][0]'], keyframesPerChannel: [3], evidenceStatus: "PRESERVED_DESKTOP_REPORT" };
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_KEYFRAME_CLEAR_V3D_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, before: desktop.before.action, after: desktop.after.action }, wasm: { status: "EXACT", before: beforeWasm, after: { animationCount: clearedSnapshot.animations.length, animationCleared: true } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00180" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00179/anim-keyframe-clear-v3d-local-exact-report.json");
|
|
if (sourceAnimation || !fs.existsSync(reportPath)) fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} channels=${beforeWasm.keyframesPerChannel.length}->0 poll=true operator=${desktop.operatorStatus} mainMutation=animation_cleared desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00206") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00206-operator-anim.slot_channels_move_to_new_action.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00206/anim-slot-channels-move-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-slot-channels-move-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "SLOT_MOVED_TO_NEW_ACTION");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.equal(desktop.after.objects.WebGapAnimMoveSlotA.name, "WebGapMoveSlotAAction");
|
|
assert.equal(desktop.after.objects.WebGapAnimMoveSlotB.name, "WebGapAnimMoveSlotAction");
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animations = before.animations?.filter((value) => value.targetId && value.targetId !== "unlinked");
|
|
assert.equal(animations?.length, 2);
|
|
assert.deepEqual(animations.map((value) => value.name).sort(), ["WebGapAnimMoveSlotAction", "WebGapMoveSlotAAction"]);
|
|
const moved = animations.find((value) => value.name === "WebGapMoveSlotAAction");
|
|
const remaining = animations.find((value) => value.name === "WebGapAnimMoveSlotAction");
|
|
assert.ok(moved && remaining);
|
|
assert.deepEqual(moved.channels[0].keyframes.map((value) => value.value[0]), [1, 3, 5]);
|
|
assert.deepEqual(remaining.channels[0].keyframes.map((value) => value.value[0]), [2, 4, 6]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_SLOT_CHANNELS_MOVE_TO_NEW_ACTION_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", movedActionId: moved.id, remainingActionId: remaining.id, movedChannels: moved.channels.length, remainingChannels: remaining.channels.length }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00207" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00206/anim-slot-channels-move-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} actions=2 moved=1 desktop=exact poll=true operator=${desktop.operatorStatus} saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00207") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00207-operator-anim.slot_new_for_id.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00207/anim-slot-new-for-id-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-slot-new-for-id-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "SLOT_DUPLICATED");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.deepEqual(desktop.after.slots, ["OBWebGapAnimNewSlot", "OBWebGapAnimNewSlot.001"]);
|
|
assert.equal(desktop.after.slot, "OBWebGapAnimNewSlot.001");
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.targetId === "object:WebGapAnimNewSlotObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.slotIdentifier, "OBWebGapAnimNewSlot.001");
|
|
assert.equal(animation.slotCount, 2);
|
|
assert.equal(animation.channels.length, 1);
|
|
assert.equal(animation.channels[0].path, '["new_slot_target"][0]');
|
|
assert.deepEqual(animation.channels[0].keyframes.map((value) => value.frame), [1, 3, 5]);
|
|
assert.deepEqual(animation.channels[0].keyframes.map((value) => value.value[0]), [1, 3, 5]);
|
|
const saved = output(engine, handle, engine._web_engine_save_blend, true);
|
|
const reopened = engine._web_engine_create();
|
|
open(engine, reopened, saved);
|
|
const animationAfter = snapshot(engine, reopened).animations?.find((value) => value.id === animation.id);
|
|
assert.deepEqual(animationAfter, animation);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_SLOT_NEW_FOR_ID_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", animationId: animation.id, slotIdentifier: animation.slotIdentifier, slotCount: animation.slotCount, channels: animation.channels.length }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00208" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00207/anim-slot-new-for-id-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} slot=${animation.slotIdentifier} slotCount=${animation.slotCount} desktop=exact poll=true operator=${desktop.operatorStatus} saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00208") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00208-operator-anim.slot_unassign_from_constraint.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00208/anim-slot-unassign-from-constraint-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-slot-unassign-from-constraint-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "CONSTRAINT_SLOT_UNASSIGNED");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.equal(desktop.before.action, "WebGapAnimConstraintSlotAction");
|
|
assert.equal(desktop.before.actionSlot, "OBWebGapConstraintSlot");
|
|
assert.notEqual(desktop.before.actionSlotHandle, 0);
|
|
assert.equal(desktop.after.actionSlot, null);
|
|
assert.equal(desktop.after.actionSlotHandle, 0);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const node = before.nodes?.find((value) => value.id === "object:WebGapAnimConstraintSlotObject");
|
|
assert.ok(node);
|
|
const constraint = node.constraints?.find((value) => value.name === "WebGapActionSlotConstraint");
|
|
assert.ok(constraint);
|
|
assert.equal(constraint.actionId, "action:WebGapAnimConstraintSlotAction");
|
|
assert.equal(constraint.actionSlotIdentifier, "OBWebGapConstraintSlot");
|
|
assert.equal(constraint.actionSlotHandle, 0);
|
|
assert.equal(constraint.actionSlotAssigned, false);
|
|
const saved = output(engine, handle, engine._web_engine_save_blend, true);
|
|
const reopened = engine._web_engine_create();
|
|
open(engine, reopened, saved);
|
|
const nodeAfter = snapshot(engine, reopened).nodes?.find((value) => value.id === node.id);
|
|
assert.deepEqual(nodeAfter?.constraints, node.constraints);
|
|
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).nodes, before.nodes);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_SLOT_UNASSIGN_FROM_CONSTRAINT_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", objectId: node.id, constraintName: constraint.name, actionId: constraint.actionId, actionSlotIdentifier: constraint.actionSlotIdentifier, actionSlotHandle: constraint.actionSlotHandle, actionSlotAssigned: constraint.actionSlotAssigned }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00209" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00208/anim-slot-unassign-from-constraint-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} constraint=${constraint.name} slotAssigned=false desktop=exact poll=true operator=${desktop.operatorStatus} saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00209") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00209-operator-anim.slot_unassign_from_id.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00209/anim-slot-unassign-from-id-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-slot-unassign-from-id-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "ID_SLOT_UNASSIGNED");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.equal(desktop.before.action, "WebGapAnimUnassignIdAction");
|
|
assert.equal(desktop.before.actionSlot, "OBWebGapUnassignIdSlot");
|
|
assert.notEqual(desktop.before.actionSlotHandle, 0);
|
|
assert.equal(desktop.after.actionSlot, null);
|
|
assert.equal(desktop.after.actionSlotHandle, 0);
|
|
assert.equal(desktop.after.lastSlotIdentifier, "OBWebGapUnassignIdSlot");
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.targetId === "object:WebGapAnimUnassignIdObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.slotIdentifier, "OBWebGapUnassignIdSlot");
|
|
assert.equal(animation.slotHandle, 0);
|
|
assert.equal(animation.slotAssigned, false);
|
|
assert.equal(animation.slotCount, 1);
|
|
assert.equal(animation.channels.length, 1);
|
|
assert.equal(animation.channels[0].path, '["unassign_id_target"][0]');
|
|
assert.deepEqual(animation.channels[0].keyframes.map((value) => value.frame), [1, 3, 5]);
|
|
const saved = output(engine, handle, engine._web_engine_save_blend, true);
|
|
const reopened = engine._web_engine_create();
|
|
open(engine, reopened, saved);
|
|
const animationAfter = snapshot(engine, reopened).animations?.find((value) => value.id === animation.id);
|
|
assert.deepEqual(animationAfter, animation);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_SLOT_UNASSIGN_FROM_ID_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", animationId: animation.id, slotIdentifier: animation.slotIdentifier, slotHandle: animation.slotHandle, slotAssigned: animation.slotAssigned, slotCount: animation.slotCount, channels: animation.channels.length }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00210" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00209/anim-slot-unassign-from-id-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} animation=${animation.id} slotAssigned=false desktop=exact poll=true operator=${desktop.operatorStatus} saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00210") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00210-operator-anim.slot_unassign_from_nla_strip.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00210/anim-slot-unassign-from-nla-strip-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-slot-unassign-from-nla-strip-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "NLA_SLOT_UNASSIGNED");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.equal(desktop.before.action, "WebGapAnimNlaSlotAction");
|
|
assert.equal(desktop.before.actionSlot, "OBWebGapNlaSlot");
|
|
assert.notEqual(desktop.before.actionSlotHandle, 0);
|
|
assert.equal(desktop.after.actionSlot, null);
|
|
assert.equal(desktop.after.actionSlotHandle, 0);
|
|
assert.equal(desktop.after.lastSlotIdentifier, "OBWebGapNlaSlot");
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const track = before.nlaTracks?.find((value) => value.ownerId === "object:WebGapAnimNlaSlotObject");
|
|
assert.ok(track);
|
|
const strip = track.strips?.find((value) => value.id === "WebGapNlaSlotStrip");
|
|
assert.ok(strip);
|
|
assert.equal(strip.actionId, "action:WebGapAnimNlaSlotAction:object:WebGapAnimNlaSlotObject");
|
|
assert.equal(strip.actionSlotIdentifier, "OBWebGapNlaSlot");
|
|
assert.equal(strip.actionSlotHandle, 0);
|
|
assert.equal(strip.actionSlotAssigned, false);
|
|
const saved = output(engine, handle, engine._web_engine_save_blend, true);
|
|
const reopened = engine._web_engine_create();
|
|
open(engine, reopened, saved);
|
|
const trackAfter = snapshot(engine, reopened).nlaTracks?.find((value) => value.id === track.id);
|
|
assert.deepEqual(trackAfter?.strips, track.strips);
|
|
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).nlaTracks, before.nlaTracks);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_SLOT_UNASSIGN_FROM_NLA_STRIP_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", trackId: track.id, stripId: strip.id, actionId: strip.actionId, actionSlotIdentifier: strip.actionSlotIdentifier, actionSlotHandle: strip.actionSlotHandle, actionSlotAssigned: strip.actionSlotAssigned }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00211" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00210/anim-slot-unassign-from-nla-strip-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} strip=${strip.id} slotAssigned=false desktop=exact poll=true operator=${desktop.operatorStatus} saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00211") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00211-operator-anim.start_frame_set.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00211/anim-start-frame-set-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-start-frame-set-desktop.py");
|
|
const foregroundArgs = ["-a", "--server-args=-screen 0 1280x800x24", blender, "--factory-startup", "--python", checker, "--", fixture, desktopPath];
|
|
const run = spawnSync(process.env.XVFB_RUN_BIN ?? "xvfb-run", foregroundArgs, { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024, timeout: 45_000 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "FRAME_START_SET");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.deepEqual(desktop.frame, { current: 42, start: 42, end: 120 });
|
|
|
|
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.frame, { current: 42, start: 42, end: 120 });
|
|
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).frame, before.frame);
|
|
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).frame, before.frame);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_START_FRAME_SET_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", frame: before.frame }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00212" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00211/anim-start-frame-set-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} frame=${before.frame.start}-${before.frame.end} current=${before.frame.current} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00212") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00212-operator-anim.update_animated_transform_constraints.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00212/anim-update-animated-transform-constraints-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-update-animated-transform-constraints-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
const expectedDesktopPath = 'constraints["WebGapAnimatedTransformConstraint"].from_min_x_rot';
|
|
const expectedPath = `${expectedDesktopPath}[0]`;
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "TRANSFORM_CONSTRAINT_PATHS_UPDATED");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.equal(desktop.useConvertToRadians, true);
|
|
assert.equal(desktop.after.action, "WebGapAnimatedTransformConstraintAction");
|
|
assert.deepEqual(desktop.after.channels, [{ path: expectedDesktopPath, index: 0, frames: [1, 10], values: [-30, 60] }]);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapAnimatedTransformConstraintAction:object:WebGapAnimatedTransformConstraintObject");
|
|
assert.ok(animation);
|
|
assert.deepEqual(animation.channels.map((value) => ({ path: value.path, keyframes: value.keyframes.map((key) => ({ frame: key.frame, value: key.value[0] })) })), [{ path: expectedPath, keyframes: [{ frame: 1, value: -30 }, { frame: 10, value: 60 }] }]);
|
|
const node = before.nodes?.find((value) => value.id === "object:WebGapAnimatedTransformConstraintObject");
|
|
assert.ok(node);
|
|
assert.equal(node.constraints?.find((value) => value.name === "WebGapAnimatedTransformConstraint")?.typeCode, 19);
|
|
const saved = output(engine, handle, engine._web_engine_save_blend, true);
|
|
const reopened = engine._web_engine_create();
|
|
open(engine, reopened, saved);
|
|
const afterAnimation = snapshot(engine, reopened).animations?.find((value) => value.id === animation.id);
|
|
assert.deepEqual(afterAnimation, animation);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_UPDATE_ANIMATED_TRANSFORM_CONSTRAINTS_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, useConvertToRadians: desktop.useConvertToRadians }, wasm: { status: "EXACT", animationId: animation.id, channelPath: animation.channels[0].path, keyframes: animation.channels[0].keyframes.map((key) => ({ frame: key.frame, value: key.value[0] })), constraintTypeCode: node.constraints.find((value) => value.name === "WebGapAnimatedTransformConstraint").typeCode }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00213" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00212/anim-update-animated-transform-constraints-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} animation=${animation.id} channel=${animation.channels[0].path} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00213") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00213-operator-anim.version_bone_hide_property.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00213/anim-version-bone-hide-property-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-version-bone-hide-property-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
const oldPath = 'bones["WebGapVersionBoneHideBone"].hide';
|
|
const newPath = 'pose.bones["WebGapVersionBoneHideBone"].hide';
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "BONE_HIDE_FCURVE_MOVED_TO_OBJECT_ACTION");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.deepEqual(desktop.after.armatureChannels, [{ path: oldPath, index: 0, frames: [1, 10], values: [0, 1] }]);
|
|
assert.ok(desktop.after.objectChannels.some((value) => value.path === newPath));
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapVersionBoneHideObjectAction:object:WebGapVersionBoneHideObject");
|
|
assert.ok(animation);
|
|
const copied = animation.channels?.find((value) => value.path === `${newPath}[0]`);
|
|
assert.ok(copied);
|
|
assert.deepEqual(copied.keyframes.map((key) => ({ frame: key.frame, value: key.value[0] })), [{ frame: 1, value: 0 }, { frame: 10, value: 1 }]);
|
|
const armatureAnimation = before.animations?.find((value) => value.id === "action:WebGapVersionBoneHideArmatureAction:armature:WebGapVersionBoneHideArmature");
|
|
assert.ok(armatureAnimation);
|
|
assert.ok(armatureAnimation.channels?.some((value) => value.path === `${oldPath}[0]`));
|
|
const saved = output(engine, handle, engine._web_engine_save_blend, true);
|
|
const reopened = engine._web_engine_create();
|
|
open(engine, reopened, saved);
|
|
const afterAnimation = snapshot(engine, reopened).animations?.find((value) => value.id === animation.id);
|
|
assert.deepEqual(afterAnimation, animation);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_VERSION_BONE_HIDE_PROPERTY_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", objectAnimationId: animation.id, copiedChannelPath: copied.path, copiedKeyframes: copied.keyframes.map((key) => ({ frame: key.frame, value: key.value[0] })), armatureAnimationId: armatureAnimation.id }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00214" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00213/anim-version-bone-hide-property-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} objectAnimation=${animation.id} channel=${copied.path} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00214") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00214-operator-anim.view_curve_in_graph_editor.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00214/anim-view-curve-in-graph-editor-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-view-curve-in-graph-editor-desktop.py");
|
|
const run = spawnSync(process.env.XVFB_RUN_BIN ?? "xvfb-run", ["-a", "--server-args=-screen 0 1280x800x24", blender, "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024, timeout: 60_000 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.equal(desktop.mainMutation, "GRAPH_VIEW_FRAMED");
|
|
assert.notDeepEqual(desktop.before.view.view2d, desktop.after.view.view2d);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
assert.equal(before.editorWorkflowStatus, "AVAILABLE");
|
|
const sameView = (left, right) => left?.cur?.xmin === right?.cur?.xmin && left?.cur?.xmax === right?.cur?.xmax && left?.cur?.ymin === right?.cur?.ymin && left?.cur?.ymax === right?.cur?.ymax;
|
|
const graphRegion = before.editorWorkflow.workspaces.flatMap((workspace) => workspace.areas)
|
|
.filter((area) => area.editor === "GRAPH")
|
|
.flatMap((area) => area.regions)
|
|
.find((region) => region.kind === "MAIN" && sameView(region.view2d, desktop.before.view.view2d));
|
|
assert.ok(graphRegion?.view2d);
|
|
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).editorWorkflow, before.editorWorkflow);
|
|
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).editorWorkflow, before.editorWorkflow);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_VIEW_CURVE_IN_GRAPH_EDITOR_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, beforeView2d: desktop.before.view.view2d, afterView2d: desktop.after.view.view2d }, wasm: { status: "EXACT", editor: "GRAPH", regionKind: "MAIN", view2d: graphRegion.view2d }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00215" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00214/anim-view-curve-in-graph-editor-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} editor=GRAPH view2d=exact desktop=exact poll=true operator=${desktop.operatorStatus} saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00215") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00215-operator-armature.align.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00215/armature-align-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-align-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "CHILD_ALIGNED_TO_PARENT");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
const desktopParent = desktop.after.bones.find((value) => value.name === "WebGapArmatureAlignParent");
|
|
const desktopChild = desktop.after.bones.find((value) => value.name === "WebGapArmatureAlignChild");
|
|
assert.ok(desktopParent && desktopChild);
|
|
assert.equal(desktopChild.parent, null);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureAlignArmature");
|
|
assert.ok(armature);
|
|
const parent = armature.bones?.find((value) => value.name === "WebGapArmatureAlignParent");
|
|
const child = armature.bones?.find((value) => value.name === "WebGapArmatureAlignChild");
|
|
assert.ok(parent && child);
|
|
assert.equal(child.parentId, null);
|
|
const axis = parent.tail.map((value, index) => value - parent.head[index]);
|
|
const childAxis = child.tail.map((value, index) => value - child.head[index]);
|
|
const cross = [axis[1] * childAxis[2] - axis[2] * childAxis[1], axis[2] * childAxis[0] - axis[0] * childAxis[2], axis[0] * childAxis[1] - axis[1] * childAxis[0]];
|
|
assert.ok(Math.max(...cross.map((value) => Math.abs(value))) < 1e-4, `child axis is not aligned: ${JSON.stringify({ axis, childAxis })}`);
|
|
assert.deepEqual(child.head, desktopChild.head);
|
|
assert.equal(child.tail.length, desktopChild.tail.length);
|
|
child.tail.forEach((value, index) => assert.ok(Math.abs(value - desktopChild.tail[index]) < 1e-5));
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_ALIGN_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", armatureId: armature.id, parentBone: { id: parent.id, head: parent.head, tail: parent.tail }, alignedChildBone: { id: child.id, head: child.head, tail: child.tail, parentId: child.parentId } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00216" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00215/armature-align-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} child=${child.name} aligned=true desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00216") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00216-operator-armature.assign_to_collection.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00216/armature-assign-to-collection-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-assign-to-collection-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "CHILD_ASSIGNED_TO_TARGET_COLLECTION");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
const targetDesktop = desktop.after.collections.find((value) => value.name === "WebGapArmatureAssignTarget");
|
|
assert.ok(targetDesktop?.bones.includes("WebGapArmatureAssignChild"));
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureAssignArmature");
|
|
assert.ok(armature);
|
|
const source = armature.boneCollections?.find((value) => value.name === "WebGapArmatureAssignSource");
|
|
const target = armature.boneCollections?.find((value) => value.name === "WebGapArmatureAssignTarget");
|
|
assert.ok(source && target);
|
|
const childId = armature.bones?.find((value) => value.name === "WebGapArmatureAssignChild")?.id;
|
|
assert.ok(childId);
|
|
assert.ok(source.boneIds.includes(childId));
|
|
assert.ok(target.boneIds.includes(childId));
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_ASSIGN_TO_COLLECTION_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", armatureId: armature.id, sourceCollection: { id: source.id, boneIds: source.boneIds }, targetCollection: { id: target.id, boneIds: target.boneIds }, assignedBoneId: childId }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00217" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00216/armature-assign-to-collection-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} target=${target.name} assigned=${childId} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00217") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00217-operator-armature.autoside_names.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00217/armature-autoside-names-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-autoside-names-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.poll, true);
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
const desktopNames = desktop.after.bones.map((value) => value.name).sort();
|
|
assert.deepEqual(desktopNames, ["WebGapArmatureAutosideLeft.L", "WebGapArmatureAutosideRight.R"]);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureAutosideArmature");
|
|
assert.ok(armature);
|
|
const names = armature.bones?.map((value) => value.name).sort();
|
|
assert.deepEqual(names, desktopNames);
|
|
const left = armature.bones?.find((value) => value.name === "WebGapArmatureAutosideLeft.L");
|
|
const right = armature.bones?.find((value) => value.name === "WebGapArmatureAutosideRight.R");
|
|
assert.ok(left && right);
|
|
assert.deepEqual(left.head, [1, 0, 0]);
|
|
assert.deepEqual(right.head, [-1, 0, 0]);
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_AUTOSIDE_NAMES_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", armatureId: armature.id, boneNames: names, leftBoneId: left.id, rightBoneId: right.id }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00218" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00217/armature-autoside-names-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} names=left-right desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00218") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00218-operator-armature.bone_primitive_add.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00218/armature-bone-primitive-add-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-bone-primitive-add-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.poll, true);
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.equal(desktop.after.bones.length, 1);
|
|
const desktopBone = desktop.after.bones[0];
|
|
assert.equal(desktopBone.name, "WebGapArmaturePrimitiveBone");
|
|
assert.deepEqual(desktopBone.head, [1.5, -2, 0.75]);
|
|
assert.deepEqual(desktopBone.tail, [1.5, -2, 3.25]);
|
|
assert.equal(desktopBone.useDeform, false);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmaturePrimitiveArmature");
|
|
assert.ok(armature);
|
|
assert.equal(armature.bones?.length, 1);
|
|
const bone = armature.bones[0];
|
|
assert.equal(bone.name, desktopBone.name);
|
|
assert.deepEqual(bone.head, desktopBone.head);
|
|
assert.deepEqual(bone.tail, desktopBone.tail);
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_BONE_PRIMITIVE_ADD_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", armatureId: armature.id, bone: { id: bone.id, name: bone.name, head: bone.head, tail: bone.tail } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00219" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00218/armature-bone-primitive-add-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} bone=${bone.id} cursor=true desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00219") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00219-operator-armature.calculate_roll.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00219/armature-calculate-roll-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-calculate-roll-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.poll, true);
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.equal(desktop.after.bones.length, 1);
|
|
const desktopBone = desktop.after.bones[0];
|
|
assert.equal(desktopBone.name, "WebGapArmatureCalculateRollBone");
|
|
assert.deepEqual(desktopBone.head, [0, 0, 0]);
|
|
assert.deepEqual(desktopBone.tail, [0, 2, 0]);
|
|
const expectedMatrix = [0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 1];
|
|
desktopBone.matrix.forEach((value, index) => assert.ok(Math.abs(value - expectedMatrix[index]) < 1e-5, `desktop matrix[${index}] ${value} != ${expectedMatrix[index]}`));
|
|
assert.ok(Math.abs(Math.abs(desktop.roll) - Math.PI / 2) < 1e-5);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureCalculateRollArmature");
|
|
assert.ok(armature);
|
|
assert.equal(armature.bones?.length, 1);
|
|
const bone = armature.bones[0];
|
|
assert.equal(bone.name, desktopBone.name);
|
|
assert.deepEqual(bone.head, desktopBone.head);
|
|
assert.deepEqual(bone.tail, desktopBone.tail);
|
|
const expectedRestMatrix = [0, 0, -1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1];
|
|
bone.restMatrix.forEach((value, index) => assert.ok(Math.abs(value - expectedRestMatrix[index]) < 1e-5, `WASM restMatrix[${index}] ${value} != ${expectedRestMatrix[index]}`));
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_CALCULATE_ROLL_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, roll: desktop.roll }, wasm: { status: "EXACT", armatureId: armature.id, bone: { id: bone.id, name: bone.name, head: bone.head, tail: bone.tail, restMatrix: bone.restMatrix } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00220" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00219/armature-calculate-roll-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} bone=${bone.id} roll=global-pos-x desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00220") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00220-operator-armature.click_extrude.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00220/armature-click-extrude-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-click-extrude-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.poll, true);
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.equal(desktop.after.bones.length, 2);
|
|
const desktopParent = desktop.after.bones.find((value) => value.name === "WebGapArmatureClickExtrudeBone");
|
|
const desktopChild = desktop.after.bones.find((value) => value.name === "WebGapArmatureClickExtrudeBone.001");
|
|
assert.ok(desktopParent && desktopChild);
|
|
assert.equal(desktopChild.parent, desktopParent.name);
|
|
assert.equal(desktopChild.useConnect, true);
|
|
assert.deepEqual(desktopChild.head, desktopParent.tail);
|
|
assert.deepEqual(desktopChild.tail, [1.5, 2, 1]);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureClickExtrudeArmature");
|
|
assert.ok(armature);
|
|
assert.equal(armature.bones?.length, 2);
|
|
const parent = armature.bones.find((value) => value.name === desktopParent.name);
|
|
const child = armature.bones.find((value) => value.name === desktopChild.name);
|
|
assert.ok(parent && child);
|
|
assert.equal(child.parentId, parent.id);
|
|
assert.deepEqual(child.head, desktopChild.headRaw);
|
|
assert.deepEqual(child.tail, desktopChild.tailRaw);
|
|
assert.equal(child.head[0], 0);
|
|
assert.equal(child.head[1], 0);
|
|
assert.equal(child.head[2], 0);
|
|
assert.ok(Math.abs(child.restMatrix[12] - desktopChild.head[0]) < 1e-5);
|
|
assert.ok(Math.abs(child.restMatrix[13] - desktopChild.head[1]) < 1e-5);
|
|
assert.ok(Math.abs(child.restMatrix[14] - desktopChild.head[2]) < 1e-5);
|
|
const localLength = Math.hypot(desktopChild.tailRaw[0] - desktopChild.headRaw[0], desktopChild.tailRaw[1] - desktopChild.headRaw[1], desktopChild.tailRaw[2] - desktopChild.headRaw[2]);
|
|
const matrixTail = [child.restMatrix[12] + child.restMatrix[4] * localLength, child.restMatrix[13] + child.restMatrix[5] * localLength, child.restMatrix[14] + child.restMatrix[6] * localLength];
|
|
desktopChild.tail.forEach((value, index) => assert.ok(Math.abs(value - matrixTail[index]) < 1e-5, `WASM armature-space tail[${index}] ${matrixTail[index]} != ${value}`));
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_CLICK_EXTRUDE_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", armatureId: armature.id, parentBone: { id: parent.id, name: parent.name, head: parent.head, tail: parent.tail }, extrudedBone: { id: child.id, name: child.name, parentId: child.parentId, head: child.head, tail: child.tail, restMatrix: child.restMatrix }, armatureSpaceEndpoint: desktopChild.tail }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00221" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00220/armature-click-extrude-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} child=${child.name} cursor=true desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00221") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00221-operator-armature.collection_add.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00221/armature-collection-add-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-collection-add-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.poll, true);
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.equal(desktop.after.collections.length, 2);
|
|
const desktopSource = desktop.after.collections.find((value) => value.name === "WebGapArmatureCollectionAddExisting");
|
|
const desktopAdded = desktop.after.collections.find((value) => value.name === "Bones");
|
|
assert.ok(desktopSource && desktopAdded);
|
|
assert.equal(desktopSource.index, 0);
|
|
assert.equal(desktopAdded.index, 1);
|
|
assert.deepEqual(desktopSource.bones, ["WebGapArmatureCollectionAddBone"]);
|
|
assert.deepEqual(desktopAdded.bones, []);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureCollectionAddArmature");
|
|
assert.ok(armature);
|
|
assert.equal(armature.boneCollections?.length, 2);
|
|
const source = armature.boneCollections.find((value) => value.name === desktopSource.name);
|
|
const added = armature.boneCollections.find((value) => value.name === desktopAdded.name);
|
|
assert.ok(source && added);
|
|
assert.equal(source.index, desktopSource.index);
|
|
assert.equal(added.index, desktopAdded.index);
|
|
const boneId = armature.bones?.find((value) => value.name === "WebGapArmatureCollectionAddBone")?.id;
|
|
assert.ok(boneId);
|
|
assert.deepEqual(source.boneIds, [boneId]);
|
|
assert.deepEqual(added.boneIds, []);
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_COLLECTION_ADD_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", armatureId: armature.id, existingCollection: { id: source.id, name: source.name, index: source.index, boneIds: source.boneIds }, addedCollection: { id: added.id, name: added.name, index: added.index, boneIds: added.boneIds } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00222" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00221/armature-collection-add-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} collection=${added.name} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00222") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00222-operator-armature.collection_assign.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00222/armature-collection-assign-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-collection-assign-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.poll, true);
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
const desktopSource = desktop.after.collections.find((value) => value.name === "WebGapArmatureCollectionAssignSource");
|
|
const desktopTarget = desktop.after.collections.find((value) => value.name === "WebGapArmatureCollectionAssignTarget");
|
|
assert.ok(desktopSource && desktopTarget);
|
|
assert.deepEqual(desktopSource.bones, ["WebGapArmatureCollectionAssignBone"]);
|
|
assert.deepEqual(desktopTarget.bones, ["WebGapArmatureCollectionAssignBone"]);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureCollectionAssignArmature");
|
|
assert.ok(armature);
|
|
const source = armature.boneCollections?.find((value) => value.name === desktopSource.name);
|
|
const target = armature.boneCollections?.find((value) => value.name === desktopTarget.name);
|
|
assert.ok(source && target);
|
|
const boneId = armature.bones?.find((value) => value.name === "WebGapArmatureCollectionAssignBone")?.id;
|
|
assert.ok(boneId);
|
|
assert.deepEqual(source.boneIds, [boneId]);
|
|
assert.deepEqual(target.boneIds, [boneId]);
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_COLLECTION_ASSIGN_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", armatureId: armature.id, assignedBoneId: boneId, sourceCollection: { id: source.id, name: source.name, boneIds: source.boneIds }, targetCollection: { id: target.id, name: target.name, boneIds: target.boneIds } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00223" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00222/armature-collection-assign-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} assigned=${boneId} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00223") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00223-operator-armature.collection_create_and_assign.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00223/armature-collection-create-assign-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-collection-create-assign-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.poll, true);
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
const desktopSource = desktop.after.collections.find((value) => value.name === "WebGapArmatureCollectionCreateAssignSource");
|
|
const desktopAdded = desktop.after.collections.find((value) => value.name === "WebGapArmatureCollectionCreateAssignNew");
|
|
assert.ok(desktopSource && desktopAdded);
|
|
assert.equal(desktopAdded.index, 1);
|
|
assert.equal(desktop.after.activeIndex, desktopAdded.index);
|
|
assert.deepEqual(desktopSource.bones, ["WebGapArmatureCollectionCreateAssignBone"]);
|
|
assert.deepEqual(desktopAdded.bones, ["WebGapArmatureCollectionCreateAssignBone"]);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureCollectionCreateAssignArmature");
|
|
assert.ok(armature);
|
|
assert.equal(armature.boneCollections?.length, 2);
|
|
const source = armature.boneCollections.find((value) => value.name === desktopSource.name);
|
|
const added = armature.boneCollections.find((value) => value.name === desktopAdded.name);
|
|
assert.ok(source && added);
|
|
assert.equal(added.index, desktopAdded.index);
|
|
const boneId = armature.bones?.find((value) => value.name === "WebGapArmatureCollectionCreateAssignBone")?.id;
|
|
assert.ok(boneId);
|
|
assert.deepEqual(source.boneIds, [boneId]);
|
|
assert.deepEqual(added.boneIds, [boneId]);
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_COLLECTION_CREATE_AND_ASSIGN_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", armatureId: armature.id, assignedBoneId: boneId, sourceCollection: { id: source.id, name: source.name, boneIds: source.boneIds }, createdCollection: { id: added.id, name: added.name, index: added.index, boneIds: added.boneIds } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00224" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00223/armature-collection-create-assign-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} created=${added.name} assigned=${boneId} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00224") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00224-operator-armature.collection_deselect.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00224/armature-collection-deselect-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-collection-deselect-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.poll, true);
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.equal(desktop.after.activeIndex, 0);
|
|
const desktopActive = desktop.after.bones.find((value) => value.name === "WebGapArmatureCollectionDeselectActiveBone");
|
|
const desktopOther = desktop.after.bones.find((value) => value.name === "WebGapArmatureCollectionDeselectOtherBone");
|
|
assert.ok(desktopActive && desktopOther);
|
|
assert.equal(desktopActive.selected, false);
|
|
assert.equal(desktopOther.selected, true);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureCollectionDeselectArmature");
|
|
assert.ok(armature);
|
|
assert.equal(armature.boneCollections?.length, 2);
|
|
const activeBone = armature.bones?.find((value) => value.name === desktopActive.name);
|
|
const otherBone = armature.bones?.find((value) => value.name === desktopOther.name);
|
|
assert.ok(activeBone && otherBone);
|
|
assert.equal(activeBone.selected, false);
|
|
assert.equal(otherBone.selected, true);
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_COLLECTION_DESELECT_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", armatureId: armature.id, activeCollectionIndex: 0, deselectedBoneId: activeBone.id, retainedSelectedBoneId: otherBone.id, deselected: activeBone.selected, retainedSelected: otherBone.selected }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00225" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00224/armature-collection-deselect-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} deselected=${activeBone.id} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00225") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00225-operator-armature.collection_move.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00225/armature-collection-move-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-collection-move-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.direction, "UP");
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.equal(desktop.after.activeIndex, 0);
|
|
assert.deepEqual(desktop.after.collections.map((value) => value.name), ["WebGapArmatureCollectionMoveActive", "WebGapArmatureCollectionMoveFirst", "WebGapArmatureCollectionMoveLast"]);
|
|
assert.deepEqual(desktop.after.collections.map((value) => value.bones), [["WebGapArmatureCollectionMoveActiveBone"], ["WebGapArmatureCollectionMoveFirstBone"], ["WebGapArmatureCollectionMoveLastBone"]]);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureCollectionMoveArmature");
|
|
assert.ok(armature);
|
|
assert.equal(armature.boneCollections?.length, 3);
|
|
const collections = armature.boneCollections.toSorted((left, right) => left.index - right.index);
|
|
assert.deepEqual(collections.map((value) => value.name), desktop.after.collections.map((value) => value.name));
|
|
const boneIds = new Map(armature.bones?.map((value) => [value.name, value.id]));
|
|
assert.deepEqual(collections.map((value) => value.boneIds), desktop.after.collections.map((value) => [boneIds.get(value.bones?.[0] ?? "")]))
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_COLLECTION_MOVE_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, poll: desktop.poll, direction: desktop.direction, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", armatureId: armature.id, direction: "UP", activeCollection: collections[0].name, collectionOrder: collections.map((value) => ({ id: value.id, name: value.name, index: value.index, boneIds: value.boneIds })) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00226" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00225/armature-collection-move-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} active=${collections[0].name} direction=up desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00226") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00226-operator-armature.collection_remove.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00226/armature-collection-remove-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-collection-remove-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.poll, true);
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.equal(desktop.after.activeIndex, 1);
|
|
assert.deepEqual(desktop.after.collections.map((value) => value.name), ["WebGapArmatureCollectionRemoveFirst", "WebGapArmatureCollectionRemoveLast"]);
|
|
assert.deepEqual(desktop.after.collections.map((value) => value.bones), [["WebGapArmatureCollectionRemoveFirstBone"], ["WebGapArmatureCollectionRemoveLastBone"]]);
|
|
assert.deepEqual(desktop.after.bones, ["WebGapArmatureCollectionRemoveFirstBone", "WebGapArmatureCollectionRemoveLastBone", "WebGapArmatureCollectionRemoveRemovedBone"]);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureCollectionRemoveArmature");
|
|
assert.ok(armature);
|
|
assert.equal(armature.boneCollections?.length, 2);
|
|
const collections = armature.boneCollections.toSorted((left, right) => left.index - right.index);
|
|
assert.deepEqual(collections.map((value) => value.name), desktop.after.collections.map((value) => value.name));
|
|
const boneIds = new Map(armature.bones?.map((value) => [value.name, value.id]));
|
|
assert.ok(boneIds.has("WebGapArmatureCollectionRemoveRemovedBone"));
|
|
assert.deepEqual(collections.map((value) => value.boneIds), desktop.after.collections.map((value) => [boneIds.get(value.bones?.[0] ?? "")]));
|
|
assert.ok(!collections.some((value) => value.boneIds.includes(boneIds.get("WebGapArmatureCollectionRemoveRemovedBone"))));
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_COLLECTION_REMOVE_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", armatureId: armature.id, removedCollection: "WebGapArmatureCollectionRemoveRemoved", removedBoneId: boneIds.get("WebGapArmatureCollectionRemoveRemovedBone"), collectionOrder: collections.map((value) => ({ id: value.id, name: value.name, index: value.index, boneIds: value.boneIds })) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00227" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00226/armature-collection-remove-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} removed=WebGapArmatureCollectionRemoveRemoved desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00227") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00227-operator-armature.collection_remove_unused.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00227/armature-collection-remove-unused-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-collection-remove-unused-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.poll, true);
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.equal(desktop.after.activeIndex, 1);
|
|
assert.deepEqual(desktop.after.collections.map((value) => value.name), ["WebGapArmatureCollectionRemoveUnusedFirst", "WebGapArmatureCollectionRemoveUnusedLast"]);
|
|
assert.deepEqual(desktop.after.collections.map((value) => value.bones), [["WebGapArmatureCollectionRemoveUnusedFirstBone"], ["WebGapArmatureCollectionRemoveUnusedLastBone"]]);
|
|
assert.deepEqual(desktop.after.bones, ["WebGapArmatureCollectionRemoveUnusedFirstBone", "WebGapArmatureCollectionRemoveUnusedLastBone"]);
|
|
assert.deepEqual(desktop.removedCollections, ["WebGapArmatureCollectionRemoveUnusedUnusedFirst", "WebGapArmatureCollectionRemoveUnusedUnusedLast"]);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureCollectionRemoveUnusedArmature");
|
|
assert.ok(armature);
|
|
assert.equal(armature.boneCollections?.length, 2);
|
|
const collections = armature.boneCollections.toSorted((left, right) => left.index - right.index);
|
|
assert.deepEqual(collections.map((value) => value.name), desktop.after.collections.map((value) => value.name));
|
|
const boneIds = new Map(armature.bones?.map((value) => [value.name, value.id]));
|
|
assert.ok(boneIds.has("WebGapArmatureCollectionRemoveUnusedFirstBone"));
|
|
assert.ok(boneIds.has("WebGapArmatureCollectionRemoveUnusedLastBone"));
|
|
assert.deepEqual(collections.map((value) => value.boneIds), desktop.after.collections.map((value) => [boneIds.get(value.bones?.[0] ?? "")]));
|
|
assert.deepEqual(collections.map((value) => value.index), [0, 1]);
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_COLLECTION_REMOVE_UNUSED_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", armatureId: armature.id, removedCollections: ["WebGapArmatureCollectionRemoveUnusedUnusedFirst", "WebGapArmatureCollectionRemoveUnusedUnusedLast"], collectionOrder: collections.map((value) => ({ id: value.id, name: value.name, index: value.index, boneIds: value.boneIds })) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00228" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00227/armature-collection-remove-unused-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} removed=2 desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00228") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00228-operator-armature.collection_select.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00228/armature-collection-select-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-collection-select-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.poll, true);
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.equal(desktop.after.activeIndex, 1);
|
|
assert.deepEqual(desktop.after.collections.map((value) => value.name), ["WebGapArmatureCollectionSelectFirst", "WebGapArmatureCollectionSelectActive", "WebGapArmatureCollectionSelectLast"]);
|
|
assert.deepEqual(desktop.after.selectedBones, ["WebGapArmatureCollectionSelectActiveBone", "WebGapArmatureCollectionSelectFirstBone"]);
|
|
assert.deepEqual(desktop.after.bones, ["WebGapArmatureCollectionSelectActiveBone", "WebGapArmatureCollectionSelectFirstBone", "WebGapArmatureCollectionSelectLastBone"]);
|
|
assert.equal(desktop.selectedCollection, "WebGapArmatureCollectionSelectActive");
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureCollectionSelectArmature");
|
|
assert.ok(armature);
|
|
assert.equal(armature.boneCollections?.length, 3);
|
|
const collections = armature.boneCollections.toSorted((left, right) => left.index - right.index);
|
|
assert.deepEqual(collections.map((value) => value.name), desktop.after.collections.map((value) => value.name));
|
|
const boneByName = new Map(armature.bones?.map((value) => [value.name, value]));
|
|
assert.deepEqual(armature.bones?.filter((value) => value.selected).map((value) => value.name).sort(), desktop.after.selectedBones);
|
|
assert.equal(boneByName.get("WebGapArmatureCollectionSelectActiveBone")?.selected, true);
|
|
assert.equal(boneByName.get("WebGapArmatureCollectionSelectFirstBone")?.selected, true);
|
|
assert.equal(boneByName.get("WebGapArmatureCollectionSelectLastBone")?.selected, false);
|
|
assert.deepEqual(collections.map((value) => value.boneIds), desktop.after.collections.map((value) => [boneByName.get(value.bones?.[0] ?? "")?.id]));
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_COLLECTION_SELECT_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", armatureId: armature.id, selectedCollection: "WebGapArmatureCollectionSelectActive", selectedBoneIds: armature.bones.filter((value) => value.selected).map((value) => value.id).sort(), collectionOrder: collections.map((value) => ({ id: value.id, name: value.name, index: value.index, boneIds: value.boneIds })) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00229" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00228/armature-collection-select-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} selected=active desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00229") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00229-operator-armature.collection_show_all.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00229/armature-collection-show-all-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-collection-show-all-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.poll, true);
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.deepEqual(desktop.after.collections.map((value) => value.name), ["WebGapArmatureCollectionShowAllFirst", "WebGapArmatureCollectionShowAllHidden", "WebGapArmatureCollectionShowAllLast"]);
|
|
assert.ok(desktop.after.collections.every((value) => value.visible === true));
|
|
assert.deepEqual(desktop.after.bones, ["WebGapArmatureCollectionShowAllFirstBone", "WebGapArmatureCollectionShowAllHiddenBone", "WebGapArmatureCollectionShowAllLastBone"]);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureCollectionShowAllArmature");
|
|
assert.ok(armature);
|
|
assert.equal(armature.boneCollections?.length, 3);
|
|
const collections = armature.boneCollections.toSorted((left, right) => left.index - right.index);
|
|
assert.deepEqual(collections.map((value) => value.name), desktop.after.collections.map((value) => value.name));
|
|
assert.ok(collections.every((value) => value.visible === true));
|
|
const boneByName = new Map(armature.bones?.map((value) => [value.name, value]));
|
|
assert.deepEqual(armature.bones?.map((value) => value.name).sort(), desktop.after.bones);
|
|
assert.deepEqual(collections.map((value) => value.boneIds), desktop.after.collections.map((value) => [boneByName.get(value.bones?.[0] ?? "")?.id]));
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_COLLECTION_SHOW_ALL_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", armatureId: armature.id, collectionVisibility: collections.map((value) => ({ id: value.id, name: value.name, index: value.index, visible: value.visible, boneIds: value.boneIds })) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00230" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00229/armature-collection-show-all-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} visible=all desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00230") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00230-operator-armature.collection_unassign.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00230/armature-collection-unassign-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-collection-unassign-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.poll, true);
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.equal(desktop.activeCollection, "WebGapArmatureCollectionUnassignSource");
|
|
assert.equal(desktop.unassignedBone, "WebGapArmatureCollectionUnassignBone");
|
|
assert.equal(desktop.after.activeIndex, 0);
|
|
assert.deepEqual(desktop.after.selectedBones, ["WebGapArmatureCollectionUnassignBone"]);
|
|
assert.deepEqual(desktop.after.collections.map((value) => value.name), ["WebGapArmatureCollectionUnassignSource", "WebGapArmatureCollectionUnassignRetained"]);
|
|
assert.deepEqual(desktop.after.collections.map((value) => value.bones), [[], ["WebGapArmatureCollectionUnassignBone"]]);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureCollectionUnassignArmature");
|
|
assert.ok(armature);
|
|
assert.equal(armature.boneCollections?.length, 2);
|
|
const collections = armature.boneCollections.toSorted((left, right) => left.index - right.index);
|
|
assert.deepEqual(collections.map((value) => value.name), desktop.after.collections.map((value) => value.name));
|
|
const boneId = armature.bones?.find((value) => value.name === "WebGapArmatureCollectionUnassignBone")?.id;
|
|
assert.ok(boneId);
|
|
assert.deepEqual(collections.map((value) => value.boneIds), [[], [boneId]]);
|
|
assert.deepEqual(armature.bones?.map((value) => value.name), desktop.after.bones);
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_COLLECTION_UNASSIGN_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", armatureId: armature.id, unassignedBoneId: boneId, activeCollection: collections[0].name, collectionOrder: collections.map((value) => ({ id: value.id, name: value.name, index: value.index, boneIds: value.boneIds })) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00231" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00230/armature-collection-unassign-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} unassigned=${boneId} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00231") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00231-operator-armature.collection_unassign_named.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00231/armature-collection-unassign-named-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-collection-unassign-named-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.poll, true);
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.equal(desktop.namedCollection, "WebGapArmatureCollectionUnassignNamedSource");
|
|
assert.equal(desktop.activeCollection, "WebGapArmatureCollectionUnassignNamedRetained");
|
|
assert.equal(desktop.after.activeIndex, 1);
|
|
assert.deepEqual(desktop.after.selectedBones, ["WebGapArmatureCollectionUnassignNamedBone"]);
|
|
assert.deepEqual(desktop.after.collections.map((value) => value.name), ["WebGapArmatureCollectionUnassignNamedSource", "WebGapArmatureCollectionUnassignNamedRetained"]);
|
|
assert.deepEqual(desktop.after.collections.map((value) => value.bones), [[], ["WebGapArmatureCollectionUnassignNamedBone"]]);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureCollectionUnassignNamedArmature");
|
|
assert.ok(armature);
|
|
assert.equal(armature.boneCollections?.length, 2);
|
|
const collections = armature.boneCollections.toSorted((left, right) => left.index - right.index);
|
|
assert.deepEqual(collections.map((value) => value.name), desktop.after.collections.map((value) => value.name));
|
|
const named = collections.find((value) => value.name === desktop.namedCollection);
|
|
const active = collections.find((value) => value.name === desktop.activeCollection);
|
|
assert.ok(named && active);
|
|
const boneId = armature.bones?.find((value) => value.name === "WebGapArmatureCollectionUnassignNamedBone")?.id;
|
|
assert.ok(boneId);
|
|
assert.deepEqual(named.boneIds, []);
|
|
assert.deepEqual(active.boneIds, [boneId]);
|
|
assert.deepEqual(armature.bones?.map((value) => value.name), desktop.after.bones);
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_COLLECTION_UNASSIGN_NAMED_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, namedCollection: desktop.namedCollection, activeCollection: desktop.activeCollection }, wasm: { status: "EXACT", armatureId: armature.id, unassignedBoneId: boneId, namedCollection: { id: named.id, name: named.name, index: named.index, boneIds: named.boneIds }, activeCollection: { id: active.id, name: active.name, index: active.index, boneIds: active.boneIds } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00232" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00231/armature-collection-unassign-named-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} unassigned=${boneId} named=source active=retained desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00232") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00232-operator-armature.collection_unsolo_all.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00232/armature-collection-unsolo-all-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-collection-unsolo-all-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.ok([true, false].includes(desktop.poll));
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.equal(desktop.soloCollection, "WebGapArmatureCollectionUnsoloAllSolo");
|
|
assert.equal(desktop.after.activeIndex, 1);
|
|
assert.equal(desktop.after.isSoloActive, false);
|
|
assert.deepEqual(desktop.after.collections.map((value) => value.name), ["WebGapArmatureCollectionUnsoloAllFirst", "WebGapArmatureCollectionUnsoloAllSolo", "WebGapArmatureCollectionUnsoloAllLast"]);
|
|
assert.deepEqual(desktop.after.collections.map((value) => value.solo), [false, false, false]);
|
|
assert.deepEqual(desktop.after.collections.map((value) => value.visible), [true, true, true]);
|
|
assert.deepEqual(desktop.after.collections.map((value) => value.bones), [["WebGapArmatureCollectionUnsoloAllFirstBone"], ["WebGapArmatureCollectionUnsoloAllSoloBone"], ["WebGapArmatureCollectionUnsoloAllLastBone"]]);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureCollectionUnsoloAllArmature");
|
|
assert.ok(armature);
|
|
assert.equal(armature.boneCollections?.length, 3);
|
|
const collections = armature.boneCollections.toSorted((left, right) => left.index - right.index);
|
|
assert.deepEqual(collections.map((value) => value.name), desktop.after.collections.map((value) => value.name));
|
|
assert.deepEqual(collections.map((value) => value.solo), [false, false, false]);
|
|
assert.deepEqual(collections.map((value) => value.visible), desktop.after.collections.map((value) => value.visible));
|
|
const boneByName = new Map(armature.bones?.map((value) => [value.name, value]));
|
|
assert.deepEqual(collections.map((value) => value.boneIds), desktop.after.collections.map((value) => [boneByName.get(value.bones?.[0] ?? "")?.id]));
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_COLLECTION_UNSOLO_ALL_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, soloCollection: desktop.soloCollection }, wasm: { status: "EXACT", armatureId: armature.id, collectionSolo: collections.map((value) => ({ id: value.id, name: value.name, index: value.index, solo: value.solo, visible: value.visible, boneIds: value.boneIds })) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00233" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00232/armature-collection-unsolo-all-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} solo=none desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00233") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00233-operator-armature.copy_bone_color_to_selected.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00233/armature-copy-bone-color-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-copy-bone-color-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.poll, true);
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.equal(desktop.sourceBone, "WebGapArmatureCopyBoneColorSource");
|
|
assert.equal(desktop.selectedDestination, "WebGapArmatureCopyBoneColorSelected");
|
|
assert.equal(desktop.unselectedDestination, "WebGapArmatureCopyBoneColorUnselected");
|
|
const desktopByName = new Map(desktop.after.bones.map((bone) => [bone.name, bone]));
|
|
const sourceDesktop = desktopByName.get(desktop.sourceBone);
|
|
const selectedDesktop = desktopByName.get(desktop.selectedDestination);
|
|
const unselectedDesktop = desktopByName.get(desktop.unselectedDestination);
|
|
assert.ok(sourceDesktop && selectedDesktop && unselectedDesktop);
|
|
assert.deepEqual(selectedDesktop, { ...sourceDesktop, name: desktop.selectedDestination });
|
|
assert.equal(unselectedDesktop.paletteIndex, 5);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureCopyBoneColorArmature");
|
|
assert.ok(armature);
|
|
assert.equal(armature.bones?.length, 3);
|
|
const bones = new Map(armature.bones.map((bone) => [bone.name, bone]));
|
|
const source = bones.get(desktop.sourceBone);
|
|
const selected = bones.get(desktop.selectedDestination);
|
|
const unselected = bones.get(desktop.unselectedDestination);
|
|
assert.ok(source && selected && unselected);
|
|
assert.deepEqual(selected.color, source.color);
|
|
assert.deepEqual(unselected.color, {
|
|
paletteIndex: 5,
|
|
normal: unselected.color.normal,
|
|
select: unselected.color.select,
|
|
active: unselected.color.active,
|
|
flag: unselected.color.flag,
|
|
});
|
|
assert.equal(source.color.paletteIndex, -1);
|
|
assert.deepEqual(source.color.normal, sourceDesktop.normal);
|
|
assert.deepEqual(source.color.select, sourceDesktop.selectColor);
|
|
assert.deepEqual(source.color.active, sourceDesktop.active);
|
|
assert.equal(source.selected, true);
|
|
assert.equal(selected.selected, true);
|
|
assert.equal(unselected.selected, false);
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_COPY_BONE_COLOR_TO_SELECTED_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", armatureId: armature.id, sourceBone: source.id, selectedDestination: selected.id, unselectedDestination: unselected.id, colors: { source: source.color, selected: selected.color, unselected: unselected.color } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00234" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00233/armature-copy-bone-color-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} copied=selected desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00234") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00234-operator-armature.delete.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00234/armature-delete-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-delete-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.ok([true, false].includes(desktop.poll));
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.equal(desktop.deletedBone, "WebGapArmatureDeleteSelected");
|
|
assert.deepEqual(desktop.after.bones.map((bone) => bone.name), ["WebGapArmatureDeleteKeep", "WebGapArmatureDeleteRetain"]);
|
|
assert.equal(desktop.after.activeBone, null);
|
|
assert.ok(desktop.after.bones.every((bone) => bone.selected === false));
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureDeleteArmature");
|
|
assert.ok(armature);
|
|
assert.deepEqual(armature.bones?.map((bone) => bone.name), desktop.after.bones.map((bone) => bone.name));
|
|
assert.equal(armature.bones?.some((bone) => bone.name === desktop.deletedBone), false);
|
|
assert.ok(armature.bones?.every((bone) => bone.selected === false));
|
|
assert.ok(armature.bones?.every((bone) => bone.parentId === null));
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_DELETE_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, deletedBone: desktop.deletedBone }, wasm: { status: "EXACT", armatureId: armature.id, remainingBoneIds: armature.bones.map((bone) => bone.id), deletedBonePresent: false, selectedBoneIds: armature.bones.filter((bone) => bone.selected).map((bone) => bone.id) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00235" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00234/armature-delete-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} deleted=${desktop.deletedBone} remaining=${armature.bones.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00235") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00235-operator-armature.dissolve.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00235/armature-dissolve-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-dissolve-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.ok([true, false].includes(desktop.poll));
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.equal(desktop.dissolvedBone, "WebGapArmatureDissolveTip");
|
|
assert.deepEqual(desktop.after.bones.map((bone) => bone.name), ["WebGapArmatureDissolveRoot", "WebGapArmatureDissolveOther"]);
|
|
assert.equal(desktop.after.activeBone, null);
|
|
assert.ok(desktop.after.bones.every((bone) => bone.selected === false));
|
|
const desktopRoot = desktop.after.bones[0];
|
|
const desktopOther = desktop.after.bones[1];
|
|
assert.deepEqual(desktopRoot, { name: "WebGapArmatureDissolveRoot", selected: false, parent: null, connected: false, head: [0, 0, 0], tail: [0, 3, 0] });
|
|
assert.deepEqual(desktopOther, { name: "WebGapArmatureDissolveOther", selected: false, parent: null, connected: false, head: [2, 0, 0], tail: [2, 1, 0] });
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureDissolveArmature");
|
|
assert.ok(armature);
|
|
assert.deepEqual(armature.bones?.map((bone) => bone.name), desktop.after.bones.map((bone) => bone.name));
|
|
const rootBone = armature.bones?.[0];
|
|
const otherBone = armature.bones?.[1];
|
|
assert.ok(rootBone && otherBone);
|
|
assert.equal(rootBone.parentId, null);
|
|
assert.equal(otherBone.parentId, null);
|
|
assert.equal(rootBone.selected, false);
|
|
assert.equal(otherBone.selected, false);
|
|
assert.deepEqual(rootBone.head, desktopRoot.head);
|
|
assert.deepEqual(rootBone.tail, desktopRoot.tail);
|
|
assert.deepEqual(otherBone.head, desktopOther.head);
|
|
assert.deepEqual(otherBone.tail, desktopOther.tail);
|
|
assert.equal(armature.bones?.some((bone) => ["WebGapArmatureDissolveMiddle", desktop.dissolvedBone].includes(bone.name)), false);
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_DISSOLVE_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, dissolvedBone: desktop.dissolvedBone }, wasm: { status: "EXACT", armatureId: armature.id, remainingBoneIds: armature.bones.map((bone) => bone.id), dissolvedBonePresent: false, survivingRoot: { id: rootBone.id, head: rootBone.head, tail: rootBone.tail }, independentBone: { id: otherBone.id, head: otherBone.head, tail: otherBone.tail } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00236" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00235/armature-dissolve-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} dissolved=${desktop.dissolvedBone} remaining=${armature.bones.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00236") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00236-operator-armature.duplicate.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00236/armature-duplicate-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-duplicate-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.ok([true, false].includes(desktop.poll));
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.equal(desktop.sourceBone, "WebGapArmatureDuplicateSource");
|
|
assert.equal(desktop.duplicateBone, "WebGapArmatureDuplicateSource.001");
|
|
assert.deepEqual(desktop.after.bones.map((bone) => bone.name), ["WebGapArmatureDuplicateSource", "WebGapArmatureDuplicateOther", "WebGapArmatureDuplicateSource.001"]);
|
|
assert.equal(desktop.after.activeBone, desktop.duplicateBone);
|
|
assert.deepEqual(desktop.after.bones.filter((bone) => bone.selected).map((bone) => bone.name), [desktop.duplicateBone]);
|
|
const desktopSource = desktop.after.bones[0];
|
|
const desktopOther = desktop.after.bones[1];
|
|
const desktopDuplicate = desktop.after.bones[2];
|
|
assert.deepEqual(desktopDuplicate.head, desktopSource.head);
|
|
assert.deepEqual(desktopDuplicate.tail, desktopSource.tail);
|
|
assert.equal(desktopDuplicate.parent, null);
|
|
assert.equal(desktopDuplicate.connected, false);
|
|
assert.deepEqual(desktopOther, { name: "WebGapArmatureDuplicateOther", selected: false, selectHead: false, selectTail: false, parent: null, connected: false, head: [2, 0, 0], tail: [2, 1, 0] });
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureDuplicateArmature");
|
|
assert.ok(armature);
|
|
assert.deepEqual(armature.bones?.map((bone) => bone.name), desktop.after.bones.map((bone) => bone.name));
|
|
const source = armature.bones?.find((bone) => bone.name === desktop.sourceBone);
|
|
const other = armature.bones?.find((bone) => bone.name === "WebGapArmatureDuplicateOther");
|
|
const duplicate = armature.bones?.find((bone) => bone.name === desktop.duplicateBone);
|
|
assert.ok(source && other && duplicate);
|
|
assert.equal(source.selected, false);
|
|
assert.equal(other.selected, false);
|
|
assert.equal(duplicate.selected, true);
|
|
assert.equal(source.parentId, null);
|
|
assert.equal(other.parentId, null);
|
|
assert.equal(duplicate.parentId, null);
|
|
assert.deepEqual(source.head, duplicate.head);
|
|
assert.deepEqual(source.tail, duplicate.tail);
|
|
assert.deepEqual(other.head, desktopOther.head);
|
|
assert.deepEqual(other.tail, desktopOther.tail);
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_DUPLICATE_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, sourceBone: desktop.sourceBone, duplicateBone: desktop.duplicateBone }, wasm: { status: "EXACT", armatureId: armature.id, sourceBoneId: source.id, duplicateBoneId: duplicate.id, otherBoneId: other.id, duplicateSelected: duplicate.selected, geometryMatch: source.head.join(",") === duplicate.head.join(",") && source.tail.join(",") === duplicate.tail.join(",") }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00237" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00236/armature-duplicate-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} duplicate=${desktop.duplicateBone} remaining=${armature.bones.length} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00237") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00237-operator-armature.duplicate_move.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00237/armature-duplicate-move-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-duplicate-move-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.ok([true, false].includes(desktop.poll));
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.equal(desktop.sourceBone, "WebGapArmatureDuplicateMoveSource");
|
|
assert.equal(desktop.duplicateBone, "WebGapArmatureDuplicateMoveSource.001");
|
|
assert.deepEqual(desktop.translation, [1, 2, 3]);
|
|
assert.deepEqual(desktop.after.bones.map((bone) => bone.name), [desktop.sourceBone, "WebGapArmatureDuplicateMoveOther", desktop.duplicateBone]);
|
|
assert.equal(desktop.after.activeBone, desktop.duplicateBone);
|
|
assert.deepEqual(desktop.after.bones.filter((bone) => bone.selected).map((bone) => bone.name), [desktop.duplicateBone]);
|
|
const desktopSource = desktop.after.bones[0];
|
|
const desktopOther = desktop.after.bones[1];
|
|
const desktopDuplicate = desktop.after.bones[2];
|
|
assert.deepEqual(desktopSource.head, [0, 0, 0]);
|
|
assert.deepEqual(desktopSource.tail, [0, 1, 0]);
|
|
assert.deepEqual(desktopDuplicate.head, [1, 2, 3]);
|
|
assert.deepEqual(desktopDuplicate.tail, [1, 3, 3]);
|
|
assert.equal(desktopDuplicate.parent, null);
|
|
assert.equal(desktopDuplicate.connected, false);
|
|
assert.deepEqual(desktopOther, { name: "WebGapArmatureDuplicateMoveOther", selected: false, selectHead: false, selectTail: false, parent: null, connected: false, head: [2, 0, 0], tail: [2, 1, 0] });
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureDuplicateMoveArmature");
|
|
assert.ok(armature);
|
|
assert.deepEqual(armature.bones?.map((bone) => bone.name), desktop.after.bones.map((bone) => bone.name));
|
|
const source = armature.bones?.find((bone) => bone.name === desktop.sourceBone);
|
|
const other = armature.bones?.find((bone) => bone.name === "WebGapArmatureDuplicateMoveOther");
|
|
const duplicate = armature.bones?.find((bone) => bone.name === desktop.duplicateBone);
|
|
assert.ok(source && other && duplicate);
|
|
assert.equal(source.selected, false);
|
|
assert.equal(other.selected, false);
|
|
assert.equal(duplicate.selected, true);
|
|
assert.equal(source.parentId, null);
|
|
assert.equal(other.parentId, null);
|
|
assert.equal(duplicate.parentId, null);
|
|
assert.deepEqual(source.head, desktopSource.head);
|
|
assert.deepEqual(source.tail, desktopSource.tail);
|
|
assert.deepEqual(duplicate.head, desktopDuplicate.head);
|
|
assert.deepEqual(duplicate.tail, desktopDuplicate.tail);
|
|
assert.deepEqual(other.head, desktopOther.head);
|
|
assert.deepEqual(other.tail, desktopOther.tail);
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_DUPLICATE_MOVE_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, sourceBone: desktop.sourceBone, duplicateBone: desktop.duplicateBone, translation: desktop.translation }, wasm: { status: "EXACT", armatureId: armature.id, sourceBoneId: source.id, duplicateBoneId: duplicate.id, otherBoneId: other.id, duplicateSelected: duplicate.selected, translation: [duplicate.head[0] - source.head[0], duplicate.head[1] - source.head[1], duplicate.head[2] - source.head[2]], geometryMatch: duplicate.head.join(",") === "1,2,3" && duplicate.tail.join(",") === "1,3,3" }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00238" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00237/armature-duplicate-move-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} duplicate=${desktop.duplicateBone} translation=1,2,3 desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00238") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00238-operator-armature.duplicate_rename.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00238/armature-duplicate-rename-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-duplicate-rename-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.ok([true, false].includes(desktop.poll));
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.equal(desktop.sourceBone, "WebGapArmatureDuplicateRenameSource");
|
|
assert.equal(desktop.duplicateBone, "WebGapArmatureDuplicateRenameCopy");
|
|
assert.equal(desktop.search, "Source");
|
|
assert.equal(desktop.replace, "Copy");
|
|
assert.deepEqual(desktop.after.bones.map((bone) => bone.name), [desktop.sourceBone, "WebGapArmatureDuplicateRenameOther", desktop.duplicateBone]);
|
|
assert.equal(desktop.after.activeBone, desktop.duplicateBone);
|
|
assert.deepEqual(desktop.after.bones.filter((bone) => bone.selected).map((bone) => bone.name), [desktop.duplicateBone]);
|
|
const desktopSource = desktop.after.bones[0];
|
|
const desktopOther = desktop.after.bones[1];
|
|
const desktopDuplicate = desktop.after.bones[2];
|
|
assert.deepEqual(desktopDuplicate.head, desktopSource.head);
|
|
assert.deepEqual(desktopDuplicate.tail, desktopSource.tail);
|
|
assert.equal(desktopDuplicate.parent, null);
|
|
assert.equal(desktopDuplicate.connected, false);
|
|
assert.deepEqual(desktopOther, { name: "WebGapArmatureDuplicateRenameOther", selected: false, selectHead: false, selectTail: false, parent: null, connected: false, head: [2, 0, 0], tail: [2, 1, 0] });
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureDuplicateRenameArmature");
|
|
assert.ok(armature);
|
|
assert.deepEqual(armature.bones?.map((bone) => bone.name), desktop.after.bones.map((bone) => bone.name));
|
|
const source = armature.bones?.find((bone) => bone.name === desktop.sourceBone);
|
|
const other = armature.bones?.find((bone) => bone.name === "WebGapArmatureDuplicateRenameOther");
|
|
const duplicate = armature.bones?.find((bone) => bone.name === desktop.duplicateBone);
|
|
assert.ok(source && other && duplicate);
|
|
assert.equal(source.selected, false);
|
|
assert.equal(other.selected, false);
|
|
assert.equal(duplicate.selected, true);
|
|
assert.equal(source.parentId, null);
|
|
assert.equal(other.parentId, null);
|
|
assert.equal(duplicate.parentId, null);
|
|
assert.deepEqual(source.head, desktopSource.head);
|
|
assert.deepEqual(source.tail, desktopSource.tail);
|
|
assert.deepEqual(duplicate.head, desktopDuplicate.head);
|
|
assert.deepEqual(duplicate.tail, desktopDuplicate.tail);
|
|
assert.deepEqual(other.head, desktopOther.head);
|
|
assert.deepEqual(other.tail, desktopOther.tail);
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_DUPLICATE_RENAME_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, sourceBone: desktop.sourceBone, duplicateBone: desktop.duplicateBone, search: desktop.search, replace: desktop.replace }, wasm: { status: "EXACT", armatureId: armature.id, sourceBoneId: source.id, duplicateBoneId: duplicate.id, otherBoneId: other.id, duplicateSelected: duplicate.selected, renamedName: duplicate.name, geometryMatch: source.head.join(",") === duplicate.head.join(",") && source.tail.join(",") === duplicate.tail.join(",") }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00239" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00238/armature-duplicate-rename-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} duplicate=${desktop.duplicateBone} rename=Source->Copy desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00239") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00239-operator-armature.extrude.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00239/armature-extrude-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-extrude-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.ok([true, false].includes(desktop.poll));
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.equal(desktop.sourceBone, "WebGapArmatureExtrudeSource");
|
|
assert.equal(desktop.extrudeBone, "WebGapArmatureExtrudeSource.001");
|
|
assert.deepEqual(desktop.translation, [0, 1, 0]);
|
|
assert.deepEqual([...desktop.after.bones.map((bone) => bone.name)].sort(), [desktop.sourceBone, desktop.extrudeBone, "WebGapArmatureExtrudeOther"].sort());
|
|
assert.equal(desktop.after.activeBone, desktop.extrudeBone);
|
|
assert.deepEqual(desktop.after.bones.filter((bone) => bone.selected).map((bone) => bone.name), [desktop.extrudeBone]);
|
|
const desktopSource = desktop.after.bones.find((bone) => bone.name === desktop.sourceBone);
|
|
const desktopExtrude = desktop.after.bones.find((bone) => bone.name === desktop.extrudeBone);
|
|
const desktopOther = desktop.after.bones.find((bone) => bone.name === "WebGapArmatureExtrudeOther");
|
|
assert.deepEqual(desktopExtrude.head, [0, 1, 0]);
|
|
assert.deepEqual(desktopExtrude.tail, [0, 2, 0]);
|
|
assert.equal(desktopExtrude.parent, desktop.sourceBone);
|
|
assert.equal(desktopExtrude.connected, true);
|
|
assert.deepEqual(desktopOther, { name: "WebGapArmatureExtrudeOther", selected: false, selectHead: false, selectTail: false, parent: null, connected: false, head: [2, 0, 0], tail: [2, 1, 0] });
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureExtrudeArmature");
|
|
assert.ok(armature);
|
|
assert.deepEqual([...armature.bones?.map((bone) => bone.name)].sort(), [...desktop.after.bones.map((bone) => bone.name)].sort());
|
|
const source = armature.bones?.find((bone) => bone.name === desktop.sourceBone);
|
|
const other = armature.bones?.find((bone) => bone.name === "WebGapArmatureExtrudeOther");
|
|
const extrude = armature.bones?.find((bone) => bone.name === desktop.extrudeBone);
|
|
assert.ok(source && other && extrude);
|
|
assert.equal(source.selected, false);
|
|
assert.equal(other.selected, false);
|
|
assert.equal(extrude.selected, true);
|
|
assert.equal(source.parentId, null);
|
|
assert.equal(other.parentId, null);
|
|
assert.equal(extrude.parentId, source.id);
|
|
assert.deepEqual(source.head, desktopSource.head);
|
|
assert.deepEqual(source.tail, desktopSource.tail);
|
|
const desktopParentTail = desktopSource.tail;
|
|
assert.deepEqual(extrude.head, desktopExtrude.head.map((value, index) => value - desktopParentTail[index]));
|
|
assert.deepEqual(extrude.tail, desktopExtrude.tail.map((value, index) => value - desktopParentTail[index]));
|
|
assert.deepEqual(other.head, desktopOther.head);
|
|
assert.deepEqual(other.tail, desktopOther.tail);
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_EXTRUDE_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, sourceBone: desktop.sourceBone, extrudeBone: desktop.extrudeBone, translation: desktop.translation }, wasm: { status: "EXACT", armatureId: armature.id, sourceBoneId: source.id, extrudeBoneId: extrude.id, otherBoneId: other.id, extrudeSelected: extrude.selected, parentId: extrude.parentId, geometry: { head: extrude.head, tail: extrude.tail } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00240" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00239/armature-extrude-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} extrude=${desktop.extrudeBone} translation=0,1,0 desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00240") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00240-operator-armature.extrude_forked.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00240/armature-extrude-forked-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-extrude-forked-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.forked, true);
|
|
assert.ok([true, false].includes(desktop.poll));
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.equal(desktop.sourceBone, "WebGapArmatureExtrudeForkedSource");
|
|
assert.equal(desktop.forkedBone, "WebGapArmatureExtrudeForkedSource.001");
|
|
assert.deepEqual(desktop.translation, [0, 1, 0]);
|
|
assert.deepEqual([...desktop.after.bones.map((bone) => bone.name)].sort(), [desktop.sourceBone, desktop.forkedBone, "WebGapArmatureExtrudeForkedOther"].sort());
|
|
assert.equal(desktop.after.activeBone, desktop.forkedBone);
|
|
assert.deepEqual(desktop.after.bones.filter((bone) => bone.selected).map((bone) => bone.name), [desktop.forkedBone]);
|
|
const desktopSource = desktop.after.bones.find((bone) => bone.name === desktop.sourceBone);
|
|
const desktopForked = desktop.after.bones.find((bone) => bone.name === desktop.forkedBone);
|
|
const desktopOther = desktop.after.bones.find((bone) => bone.name === "WebGapArmatureExtrudeForkedOther");
|
|
assert.deepEqual(desktopForked.head, [0, 1, 0]);
|
|
assert.deepEqual(desktopForked.tail, [0, 2, 0]);
|
|
assert.equal(desktopForked.parent, desktop.sourceBone);
|
|
assert.equal(desktopForked.connected, false);
|
|
assert.deepEqual(desktopOther, { name: "WebGapArmatureExtrudeForkedOther", selected: false, selectHead: false, selectTail: false, parent: null, connected: false, head: [2, 0, 0], tail: [2, 1, 0] });
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureExtrudeForkedArmature");
|
|
assert.ok(armature);
|
|
assert.deepEqual([...armature.bones?.map((bone) => bone.name)].sort(), [...desktop.after.bones.map((bone) => bone.name)].sort());
|
|
const source = armature.bones?.find((bone) => bone.name === desktop.sourceBone);
|
|
const other = armature.bones?.find((bone) => bone.name === "WebGapArmatureExtrudeForkedOther");
|
|
const forked = armature.bones?.find((bone) => bone.name === desktop.forkedBone);
|
|
assert.ok(source && other && forked);
|
|
assert.equal(source.selected, false);
|
|
assert.equal(other.selected, false);
|
|
assert.equal(forked.selected, true);
|
|
assert.equal(source.parentId, null);
|
|
assert.equal(other.parentId, null);
|
|
assert.equal(forked.parentId, source.id);
|
|
assert.deepEqual(source.head, desktopSource.head);
|
|
assert.deepEqual(source.tail, desktopSource.tail);
|
|
const desktopParentTail = desktopSource.tail;
|
|
assert.deepEqual(forked.head, desktopForked.head.map((value, index) => value - desktopParentTail[index]));
|
|
assert.deepEqual(forked.tail, desktopForked.tail.map((value, index) => value - desktopParentTail[index]));
|
|
assert.deepEqual(other.head, desktopOther.head);
|
|
assert.deepEqual(other.tail, desktopOther.tail);
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_EXTRUDE_FORKED_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, forked: desktop.forked, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, sourceBone: desktop.sourceBone, forkedBone: desktop.forkedBone, translation: desktop.translation }, wasm: { status: "EXACT", armatureId: armature.id, sourceBoneId: source.id, forkedBoneId: forked.id, otherBoneId: other.id, forkedSelected: forked.selected, parentId: forked.parentId, connected: false, geometry: { head: forked.head, tail: forked.tail } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00241" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00240/armature-extrude-forked-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} forked=${desktop.forkedBone} connected=false desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00241") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00241-operator-armature.extrude_move.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00241/armature-extrude-move-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-extrude-move-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.ok([true, false].includes(desktop.poll));
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.equal(desktop.sourceBone, "WebGapArmatureExtrudeMoveSource");
|
|
assert.equal(desktop.extrudeBone, "WebGapArmatureExtrudeMoveSource.001");
|
|
assert.deepEqual(desktop.translation, [0, 1, 0]);
|
|
assert.deepEqual([...desktop.after.bones.map((bone) => bone.name)].sort(), [desktop.sourceBone, desktop.extrudeBone, "WebGapArmatureExtrudeMoveOther"].sort());
|
|
assert.equal(desktop.after.activeBone, desktop.extrudeBone);
|
|
assert.deepEqual(desktop.after.bones.filter((bone) => bone.selected).map((bone) => bone.name), [desktop.extrudeBone]);
|
|
const desktopSource = desktop.after.bones.find((bone) => bone.name === desktop.sourceBone);
|
|
const desktopExtrude = desktop.after.bones.find((bone) => bone.name === desktop.extrudeBone);
|
|
const desktopOther = desktop.after.bones.find((bone) => bone.name === "WebGapArmatureExtrudeMoveOther");
|
|
assert.deepEqual(desktopExtrude.head, [0, 1, 0]);
|
|
assert.deepEqual(desktopExtrude.tail, [0, 2, 0]);
|
|
assert.equal(desktopExtrude.parent, desktop.sourceBone);
|
|
assert.equal(desktopExtrude.connected, true);
|
|
assert.deepEqual(desktopOther, { name: "WebGapArmatureExtrudeMoveOther", selected: false, selectHead: false, selectTail: false, parent: null, connected: false, head: [2, 0, 0], tail: [2, 1, 0] });
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureExtrudeMoveArmature");
|
|
assert.ok(armature);
|
|
assert.deepEqual([...armature.bones?.map((bone) => bone.name)].sort(), [...desktop.after.bones.map((bone) => bone.name)].sort());
|
|
const source = armature.bones?.find((bone) => bone.name === desktop.sourceBone);
|
|
const other = armature.bones?.find((bone) => bone.name === "WebGapArmatureExtrudeMoveOther");
|
|
const extrude = armature.bones?.find((bone) => bone.name === desktop.extrudeBone);
|
|
assert.ok(source && other && extrude);
|
|
assert.equal(source.selected, false);
|
|
assert.equal(other.selected, false);
|
|
assert.equal(extrude.selected, true);
|
|
assert.equal(source.parentId, null);
|
|
assert.equal(other.parentId, null);
|
|
assert.equal(extrude.parentId, source.id);
|
|
assert.deepEqual(source.head, desktopSource.head);
|
|
assert.deepEqual(source.tail, desktopSource.tail);
|
|
const desktopParentTail = desktopSource.tail;
|
|
assert.deepEqual(extrude.head, desktopExtrude.head.map((value, index) => value - desktopParentTail[index]));
|
|
assert.deepEqual(extrude.tail, desktopExtrude.tail.map((value, index) => value - desktopParentTail[index]));
|
|
assert.deepEqual(other.head, desktopOther.head);
|
|
assert.deepEqual(other.tail, desktopOther.tail);
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_EXTRUDE_MOVE_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, sourceBone: desktop.sourceBone, extrudeBone: desktop.extrudeBone, translation: desktop.translation }, wasm: { status: "EXACT", armatureId: armature.id, sourceBoneId: source.id, extrudeBoneId: extrude.id, otherBoneId: other.id, extrudeSelected: extrude.selected, parentId: extrude.parentId, connected: true, geometry: { head: extrude.head, tail: extrude.tail } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00242" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00241/armature-extrude-move-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} extrude=${desktop.extrudeBone} translation=0,1,0 desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00242") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00242-operator-armature.fill.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00242/armature-fill-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-fill-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.ok([true, false].includes(desktop.poll));
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.equal(desktop.sourceBone, "WebGapArmatureFillSource");
|
|
assert.equal(desktop.targetBone, "WebGapArmatureFillTarget");
|
|
assert.equal(desktop.bridgeBone, "WebGapArmatureFillBridge");
|
|
assert.deepEqual([...desktop.after.bones.map((bone) => bone.name)].sort(), [desktop.sourceBone, desktop.targetBone, desktop.bridgeBone, "WebGapArmatureFillOther"].sort());
|
|
assert.equal(desktop.after.activeBone, desktop.bridgeBone);
|
|
assert.deepEqual(desktop.after.bones.filter((bone) => bone.selected).map((bone) => bone.name), [desktop.bridgeBone]);
|
|
const desktopSource = desktop.after.bones.find((bone) => bone.name === desktop.sourceBone);
|
|
const desktopTarget = desktop.after.bones.find((bone) => bone.name === desktop.targetBone);
|
|
const desktopBridge = desktop.after.bones.find((bone) => bone.name === desktop.bridgeBone);
|
|
const desktopOther = desktop.after.bones.find((bone) => bone.name === "WebGapArmatureFillOther");
|
|
assert.deepEqual(desktopBridge.head, [0, 1, 0]);
|
|
assert.deepEqual(desktopBridge.tail, [0, 2, 0]);
|
|
assert.equal(desktopBridge.parent, desktop.sourceBone);
|
|
assert.equal(desktopBridge.connected, true);
|
|
assert.deepEqual(desktopTarget.head, [0, 2, 0]);
|
|
assert.deepEqual(desktopTarget.tail, [0, 3, 0]);
|
|
assert.deepEqual(desktopOther, { name: "WebGapArmatureFillOther", selected: false, selectHead: false, selectTail: false, parent: null, connected: false, head: [2, 0, 0], tail: [2, 1, 0] });
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureFillArmature");
|
|
assert.ok(armature);
|
|
assert.deepEqual([...armature.bones?.map((bone) => bone.name)].sort(), [...desktop.after.bones.map((bone) => bone.name)].sort());
|
|
const source = armature.bones?.find((bone) => bone.name === desktop.sourceBone);
|
|
const target = armature.bones?.find((bone) => bone.name === desktop.targetBone);
|
|
const bridge = armature.bones?.find((bone) => bone.name === desktop.bridgeBone);
|
|
const other = armature.bones?.find((bone) => bone.name === "WebGapArmatureFillOther");
|
|
assert.ok(source && target && bridge && other);
|
|
assert.equal(source.selected, false);
|
|
assert.equal(target.selected, false);
|
|
assert.equal(bridge.selected, true);
|
|
assert.equal(other.selected, false);
|
|
assert.equal(source.parentId, null);
|
|
assert.equal(target.parentId, null);
|
|
assert.equal(bridge.parentId, source.id);
|
|
assert.deepEqual(source.head, desktopSource.head);
|
|
assert.deepEqual(source.tail, desktopSource.tail);
|
|
const desktopParentTail = desktopSource.tail;
|
|
assert.deepEqual(bridge.head, desktopBridge.head.map((value, index) => value - desktopParentTail[index]));
|
|
assert.deepEqual(bridge.tail, desktopBridge.tail.map((value, index) => value - desktopParentTail[index]));
|
|
assert.deepEqual(target.head, desktopTarget.head);
|
|
assert.deepEqual(target.tail, desktopTarget.tail);
|
|
assert.deepEqual(other.head, desktopOther.head);
|
|
assert.deepEqual(other.tail, desktopOther.tail);
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_FILL_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, sourceBone: desktop.sourceBone, targetBone: desktop.targetBone, bridgeBone: desktop.bridgeBone }, wasm: { status: "EXACT", armatureId: armature.id, sourceBoneId: source.id, targetBoneId: target.id, bridgeBoneId: bridge.id, otherBoneId: other.id, bridgeSelected: bridge.selected, parentId: bridge.parentId, connected: true, geometry: { head: bridge.head, tail: bridge.tail } }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00243" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00242/armature-fill-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} bridge=${desktop.bridgeBone} endpoints=source-tail,target-head desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00243") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00243-operator-armature.flip_names.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00243/armature-flip-names-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-flip-names-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.doStripNumbers, false);
|
|
assert.ok([true, false].includes(desktop.poll));
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.equal(desktop.leftGeometryBone, "WebGapArmatureFlipBone.R");
|
|
assert.equal(desktop.rightGeometryBone, "WebGapArmatureFlipBone.L");
|
|
const desktopLeft = desktop.after.bones.find((bone) => bone.head[0] === -1);
|
|
const desktopRight = desktop.after.bones.find((bone) => bone.head[0] === 1);
|
|
const desktopOther = desktop.after.bones.find((bone) => bone.name === "WebGapArmatureFlipOther");
|
|
assert.equal(desktopLeft.name, desktop.leftGeometryBone);
|
|
assert.equal(desktopRight.name, desktop.rightGeometryBone);
|
|
assert.deepEqual(desktopLeft.head, [-1, 0, 0]);
|
|
assert.deepEqual(desktopLeft.tail, [-1, 1, 0]);
|
|
assert.deepEqual(desktopRight.head, [1, 0, 0]);
|
|
assert.deepEqual(desktopRight.tail, [1, 1, 0]);
|
|
assert.deepEqual(desktopOther, { name: "WebGapArmatureFlipOther", selected: false, selectHead: false, selectTail: false, parent: null, connected: false, head: [0, 0, 2], tail: [0, 1, 2] });
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureFlipNamesArmature");
|
|
assert.ok(armature);
|
|
const left = armature.bones?.find((bone) => bone.head[0] === -1);
|
|
const right = armature.bones?.find((bone) => bone.head[0] === 1);
|
|
const other = armature.bones?.find((bone) => bone.name === "WebGapArmatureFlipOther");
|
|
assert.ok(left && right && other);
|
|
assert.equal(left.name, desktop.leftGeometryBone);
|
|
assert.equal(right.name, desktop.rightGeometryBone);
|
|
assert.equal(left.selected, true);
|
|
assert.equal(right.selected, true);
|
|
assert.equal(other.selected, false);
|
|
assert.deepEqual(left.head, desktopLeft.head);
|
|
assert.deepEqual(left.tail, desktopLeft.tail);
|
|
assert.deepEqual(right.head, desktopRight.head);
|
|
assert.deepEqual(right.tail, desktopRight.tail);
|
|
assert.deepEqual(other.head, desktopOther.head);
|
|
assert.deepEqual(other.tail, desktopOther.tail);
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_FLIP_NAMES_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, leftGeometryBone: desktop.leftGeometryBone, rightGeometryBone: desktop.rightGeometryBone, doStripNumbers: desktop.doStripNumbers }, wasm: { status: "EXACT", armatureId: armature.id, leftBoneId: left.id, rightBoneId: right.id, otherBoneId: other.id, leftName: left.name, rightName: right.name, geometryNamesFlipped: left.name === "WebGapArmatureFlipBone.R" && right.name === "WebGapArmatureFlipBone.L" }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00244" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00243/armature-flip-names-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} left=${left.name} right=${right.name} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00244") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00244-operator-armature.hide.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00244/armature-hide-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-hide-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.unselected, false);
|
|
assert.ok([true, false].includes(desktop.poll));
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.equal(desktop.hiddenBone, "WebGapArmatureHideSelected");
|
|
const desktopHidden = desktop.after.bones.find((bone) => bone.name === desktop.hiddenBone);
|
|
const desktopOther = desktop.after.bones.find((bone) => bone.name === "WebGapArmatureHideOther");
|
|
assert.deepEqual(desktopHidden, { name: desktop.hiddenBone, selected: false, hidden: true, selectHead: false, selectTail: false, parent: null, connected: false, head: [-1, 0, 0], tail: [-1, 1, 0] });
|
|
assert.deepEqual(desktopOther, { name: "WebGapArmatureHideOther", selected: false, hidden: false, selectHead: false, selectTail: false, parent: null, connected: false, head: [1, 0, 0], tail: [1, 1, 0] });
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureHideArmature");
|
|
assert.ok(armature);
|
|
const hidden = armature.bones?.find((bone) => bone.name === desktop.hiddenBone);
|
|
const other = armature.bones?.find((bone) => bone.name === "WebGapArmatureHideOther");
|
|
assert.ok(hidden && other);
|
|
assert.equal(hidden.hidden, true);
|
|
assert.equal(hidden.selected, false);
|
|
assert.equal(other.hidden, false);
|
|
assert.equal(other.selected, false);
|
|
assert.deepEqual(hidden.head, desktopHidden.head);
|
|
assert.deepEqual(hidden.tail, desktopHidden.tail);
|
|
assert.deepEqual(other.head, desktopOther.head);
|
|
assert.deepEqual(other.tail, desktopOther.tail);
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_HIDE_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, hiddenBone: desktop.hiddenBone, unselected: desktop.unselected }, wasm: { status: "EXACT", armatureId: armature.id, hiddenBoneId: hidden.id, otherBoneId: other.id, hidden: hidden.hidden, selectedAfterHide: hidden.selected, otherHidden: other.hidden }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00245" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00244/armature-hide-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} hidden=${hidden.name} hiddenFlag=${hidden.hidden} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00245") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00245-operator-armature.move_to_collection.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00245/armature-move-to-collection-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-move-to-collection-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.collectionIndex, 1);
|
|
assert.ok([true, false].includes(desktop.poll));
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.equal(desktop.movedBone, "WebGapArmatureMoveCollectionSelected");
|
|
assert.equal(desktop.sourceCollection, "WebGapArmatureMoveCollectionSource");
|
|
assert.equal(desktop.targetCollection, "WebGapArmatureMoveCollectionTarget");
|
|
const desktopMove = desktop.after.bones.find((bone) => bone.name === desktop.movedBone);
|
|
const desktopOther = desktop.after.bones.find((bone) => bone.name === "WebGapArmatureMoveCollectionOther");
|
|
assert.deepEqual(desktopMove.collections, [desktop.targetCollection]);
|
|
assert.deepEqual(desktopOther.collections, [desktop.sourceCollection]);
|
|
assert.equal(desktopMove.selected, true);
|
|
assert.equal(desktopOther.selected, false);
|
|
assert.deepEqual(desktopMove.head, [-1, 0, 0]);
|
|
assert.deepEqual(desktopMove.tail, [-1, 1, 0]);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureMoveCollectionArmature");
|
|
assert.ok(armature);
|
|
const moved = armature.bones?.find((bone) => bone.name === desktop.movedBone);
|
|
const other = armature.bones?.find((bone) => bone.name === "WebGapArmatureMoveCollectionOther");
|
|
assert.ok(moved && other);
|
|
const sourceCollection = armature.boneCollections?.find((collection) => collection.name === desktop.sourceCollection);
|
|
const targetCollection = armature.boneCollections?.find((collection) => collection.name === desktop.targetCollection);
|
|
assert.ok(sourceCollection && targetCollection);
|
|
assert.deepEqual(sourceCollection.boneIds, [other.id]);
|
|
assert.deepEqual(targetCollection.boneIds, [moved.id]);
|
|
assert.equal(moved.selected, true);
|
|
assert.equal(other.selected, false);
|
|
assert.deepEqual(moved.head, desktopMove.head);
|
|
assert.deepEqual(moved.tail, desktopMove.tail);
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_MOVE_TO_COLLECTION_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, movedBone: desktop.movedBone, sourceCollection: desktop.sourceCollection, targetCollection: desktop.targetCollection }, wasm: { status: "EXACT", armatureId: armature.id, movedBoneId: moved.id, otherBoneId: other.id, sourceCollectionId: sourceCollection.id, targetCollectionId: targetCollection.id, sourceMembers: sourceCollection.boneIds, targetMembers: targetCollection.boneIds }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00246" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00245/armature-move-to-collection-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} moved=${moved.name} target=${targetCollection.name} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00246") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00246-operator-armature.parent_clear.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00246/armature-parent-clear-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-parent-clear-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.clearType, "CLEAR");
|
|
assert.ok([true, false].includes(desktop.poll));
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.equal(desktop.parentBone, "WebGapArmatureParentClearParent");
|
|
assert.equal(desktop.childBone, "WebGapArmatureParentClearChild");
|
|
const desktopParent = desktop.after.bones.find((bone) => bone.name === desktop.parentBone);
|
|
const desktopChild = desktop.after.bones.find((bone) => bone.name === desktop.childBone);
|
|
const desktopOther = desktop.after.bones.find((bone) => bone.name === "WebGapArmatureParentClearOther");
|
|
assert.equal(desktopChild.parent, null);
|
|
assert.equal(desktopChild.connected, false);
|
|
assert.equal(desktopChild.selected, true);
|
|
assert.equal(desktopParent.parent, null);
|
|
assert.equal(desktopOther.parent, null);
|
|
assert.deepEqual(desktopChild.head, [0, 1, 0]);
|
|
assert.deepEqual(desktopChild.tail, [0, 2, 0]);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureParentClearArmature");
|
|
assert.ok(armature);
|
|
const parent = armature.bones?.find((bone) => bone.name === desktop.parentBone);
|
|
const child = armature.bones?.find((bone) => bone.name === desktop.childBone);
|
|
const other = armature.bones?.find((bone) => bone.name === "WebGapArmatureParentClearOther");
|
|
assert.ok(parent && child && other);
|
|
assert.equal(child.parentId, null);
|
|
assert.equal(child.selected, true);
|
|
assert.equal(parent.parentId, null);
|
|
assert.equal(other.parentId, null);
|
|
assert.deepEqual(child.head, desktopChild.head);
|
|
assert.deepEqual(child.tail, desktopChild.tail);
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_PARENT_CLEAR_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, parentBone: desktop.parentBone, childBone: desktop.childBone, clearType: desktop.clearType }, wasm: { status: "EXACT", armatureId: armature.id, parentBoneId: parent.id, childBoneId: child.id, otherBoneId: other.id, childParentId: child.parentId, childSelected: child.selected }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00247" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00246/armature-parent-clear-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} child=${child.name} parentId=${child.parentId} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00247") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00247-operator-armature.parent_set.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00247/armature-parent-set-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-parent-set-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.setType, "CONNECTED");
|
|
assert.ok([true, false].includes(desktop.poll));
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.equal(desktop.parentBone, "WebGapArmatureParentSetParent");
|
|
assert.equal(desktop.childBone, "WebGapArmatureParentSetChild");
|
|
const desktopParent = desktop.after.bones.find((bone) => bone.name === desktop.parentBone);
|
|
const desktopChild = desktop.after.bones.find((bone) => bone.name === desktop.childBone);
|
|
const desktopOther = desktop.after.bones.find((bone) => bone.name === "WebGapArmatureParentSetOther");
|
|
assert.equal(desktopChild.parent, desktop.parentBone);
|
|
assert.equal(desktopChild.connected, true);
|
|
assert.equal(desktopChild.selected, true);
|
|
assert.equal(desktopParent.parent, null);
|
|
assert.equal(desktopOther.parent, null);
|
|
assert.deepEqual(desktopChild.head, [0, 1, 0]);
|
|
assert.deepEqual(desktopChild.tail, [0, 2, 0]);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureParentSetArmature");
|
|
assert.ok(armature);
|
|
const parent = armature.bones?.find((bone) => bone.name === desktop.parentBone);
|
|
const child = armature.bones?.find((bone) => bone.name === desktop.childBone);
|
|
const other = armature.bones?.find((bone) => bone.name === "WebGapArmatureParentSetOther");
|
|
assert.ok(parent && child && other);
|
|
assert.equal(child.parentId, parent.id);
|
|
assert.equal(child.connected, true);
|
|
assert.equal(child.selected, true);
|
|
assert.equal(parent.parentId, null);
|
|
assert.equal(other.parentId, null);
|
|
assert.deepEqual(child.head, desktopChild.head.map((value, index) => value - desktopParent.tail[index]));
|
|
assert.deepEqual(child.tail, desktopChild.tail.map((value, index) => value - desktopParent.tail[index]));
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_PARENT_SET_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, parentBone: desktop.parentBone, childBone: desktop.childBone, setType: desktop.setType }, wasm: { status: "EXACT", armatureId: armature.id, parentBoneId: parent.id, childBoneId: child.id, otherBoneId: other.id, childParentId: child.parentId, childConnected: child.connected, childSelected: child.selected }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00248" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00247/armature-parent-set-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} child=${child.name} parentId=${child.parentId} connected=${child.connected} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00248") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00248-operator-armature.reveal.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00248/armature-reveal-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-reveal-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.select, true);
|
|
assert.ok([true, false].includes(desktop.poll));
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.equal(desktop.revealedBone, "WebGapArmatureRevealHidden");
|
|
const desktopRevealed = desktop.after.bones.find((bone) => bone.name === desktop.revealedBone);
|
|
const desktopOther = desktop.after.bones.find((bone) => bone.name === "WebGapArmatureRevealOther");
|
|
assert.equal(desktopRevealed.hidden, false);
|
|
assert.equal(desktopRevealed.selected, true);
|
|
assert.equal(desktopOther.hidden, false);
|
|
assert.equal(desktopOther.selected, false);
|
|
assert.deepEqual(desktopRevealed.head, [-1, 0, 0]);
|
|
assert.deepEqual(desktopRevealed.tail, [-1, 1, 0]);
|
|
assert.deepEqual(desktopOther.head, [1, 0, 0]);
|
|
assert.deepEqual(desktopOther.tail, [1, 1, 0]);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureRevealArmature");
|
|
assert.ok(armature);
|
|
const revealed = armature.bones?.find((bone) => bone.name === desktop.revealedBone);
|
|
const other = armature.bones?.find((bone) => bone.name === "WebGapArmatureRevealOther");
|
|
assert.ok(revealed && other);
|
|
assert.equal(revealed.hidden, false);
|
|
assert.equal(revealed.selected, true);
|
|
assert.equal(other.hidden, false);
|
|
assert.equal(other.selected, false);
|
|
assert.deepEqual(revealed.head, desktopRevealed.head);
|
|
assert.deepEqual(revealed.tail, desktopRevealed.tail);
|
|
assert.deepEqual(other.head, desktopOther.head);
|
|
assert.deepEqual(other.tail, desktopOther.tail);
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_REVEAL_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, revealedBone: desktop.revealedBone, select: desktop.select }, wasm: { status: "EXACT", armatureId: armature.id, revealedBoneId: revealed.id, otherBoneId: other.id, revealedHidden: revealed.hidden, revealedSelected: revealed.selected, otherHidden: other.hidden }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00249" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00248/armature-reveal-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} revealed=${revealed.name} hidden=${revealed.hidden} selected=${revealed.selected} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00249") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00249-operator-armature.roll_clear.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00249/armature-roll-clear-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-roll-clear-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.ok([true, false].includes(desktop.poll));
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.equal(desktop.bone, "WebGapArmatureRollClearBone");
|
|
assert.equal(desktop.roll, 0);
|
|
const desktopBone = desktop.after.bones.find((bone) => bone.name === desktop.bone);
|
|
assert.ok(desktopBone);
|
|
assert.deepEqual(desktopBone.head, [0, 0, 0]);
|
|
assert.deepEqual(desktopBone.tail, [0, 2, 0]);
|
|
assert.equal(desktopBone.selected, true);
|
|
assert.equal(desktopBone.hidden, false);
|
|
const expectedMatrix = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
|
|
desktopBone.matrix.forEach((value, index) => assert.ok(Math.abs(value - expectedMatrix[index]) < 1e-5, `desktop matrix[${index}] ${value} != ${expectedMatrix[index]}`));
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureRollClearArmature");
|
|
assert.ok(armature);
|
|
assert.equal(armature.bones?.length, 1);
|
|
const bone = armature.bones[0];
|
|
assert.equal(bone.name, desktop.bone);
|
|
assert.equal(bone.hidden, false);
|
|
assert.equal(bone.selected, true);
|
|
assert.deepEqual(bone.head, desktopBone.head);
|
|
assert.deepEqual(bone.tail, desktopBone.tail);
|
|
bone.restMatrix.forEach((value, index) => assert.ok(Math.abs(value - expectedMatrix[index]) < 1e-5, `WASM restMatrix[${index}] ${value} != ${expectedMatrix[index]}`));
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_ROLL_CLEAR_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, bone: desktop.bone, roll: desktop.roll }, wasm: { status: "EXACT", armatureId: armature.id, boneId: bone.id, boneName: bone.name, hidden: bone.hidden, selected: bone.selected, restMatrix: bone.restMatrix }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00250" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00249/armature-roll-clear-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} bone=${bone.name} roll=${desktop.roll} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00250") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00250-operator-armature.select_all.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00250/armature-select-all-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-select-all-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.action, "SELECT");
|
|
assert.ok([true, false].includes(desktop.poll));
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.deepEqual(desktop.selectedBones, ["WebGapArmatureSelectAllParent", "WebGapArmatureSelectAllOther"]);
|
|
for (const bone of desktop.after.bones) {
|
|
assert.equal(bone.selected, true);
|
|
assert.equal(bone.selectHead, true);
|
|
assert.equal(bone.selectTail, true);
|
|
assert.equal(bone.hidden, false);
|
|
}
|
|
const desktopParent = desktop.after.bones.find((bone) => bone.name === "WebGapArmatureSelectAllParent");
|
|
const desktopOther = desktop.after.bones.find((bone) => bone.name === "WebGapArmatureSelectAllOther");
|
|
assert.deepEqual(desktopParent.head, [0, 0, 0]);
|
|
assert.deepEqual(desktopParent.tail, [0, 1, 0]);
|
|
assert.deepEqual(desktopOther.head, [2, 0, 0]);
|
|
assert.deepEqual(desktopOther.tail, [2, 1, 0]);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureSelectAllArmature");
|
|
assert.ok(armature);
|
|
assert.equal(armature.bones?.length, 2);
|
|
const parent = armature.bones?.find((bone) => bone.name === "WebGapArmatureSelectAllParent");
|
|
const other = armature.bones?.find((bone) => bone.name === "WebGapArmatureSelectAllOther");
|
|
assert.ok(parent && other);
|
|
assert.equal(parent.selected, true);
|
|
assert.equal(other.selected, true);
|
|
assert.equal(parent.hidden, false);
|
|
assert.equal(other.hidden, false);
|
|
assert.deepEqual(parent.head, desktopParent.head);
|
|
assert.deepEqual(parent.tail, desktopParent.tail);
|
|
assert.deepEqual(other.head, desktopOther.head);
|
|
assert.deepEqual(other.tail, desktopOther.tail);
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_SELECT_ALL_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, selectedBones: desktop.selectedBones, action: desktop.action }, wasm: { status: "EXACT", armatureId: armature.id, parentBoneId: parent.id, otherBoneId: other.id, parentSelected: parent.selected, otherSelected: other.selected, parentHead: parent.head, parentTail: parent.tail, otherHead: other.head, otherTail: other.tail }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00251" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00250/armature-select-all-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} selected=2 desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00251") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00251-operator-armature.select_hierarchy.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00251/armature-select-hierarchy-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-select-hierarchy-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.direction, "CHILD");
|
|
assert.equal(desktop.extend, false);
|
|
assert.ok([true, false].includes(desktop.poll));
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.ok(["WebGapArmatureSelectHierarchyRoot", "WebGapArmatureSelectHierarchyChild"].includes(desktop.activeBoneBefore));
|
|
assert.equal(desktop.activeBoneAfter, "WebGapArmatureSelectHierarchyChild");
|
|
const desktopBefore = Object.fromEntries(desktop.before.bones.map((bone) => [bone.name, bone]));
|
|
const desktopAfter = Object.fromEntries(desktop.after.bones.map((bone) => [bone.name, bone]));
|
|
if (desktop.operatorStatus === "FINISHED") {
|
|
assert.equal(desktopBefore.WebGapArmatureSelectHierarchyRoot.selected, true);
|
|
assert.equal(desktopBefore.WebGapArmatureSelectHierarchyChild.selected, false);
|
|
} else {
|
|
assert.equal(desktopBefore.WebGapArmatureSelectHierarchyRoot.selected, false);
|
|
assert.equal(desktopBefore.WebGapArmatureSelectHierarchyChild.selected, true);
|
|
}
|
|
assert.equal(desktopAfter.WebGapArmatureSelectHierarchyRoot.selected, false);
|
|
assert.equal(desktopAfter.WebGapArmatureSelectHierarchyChild.selected, true);
|
|
assert.equal(desktopAfter.WebGapArmatureSelectHierarchyOther.selected, false);
|
|
assert.equal(desktopAfter.WebGapArmatureSelectHierarchyChild.parent, "WebGapArmatureSelectHierarchyRoot");
|
|
assert.equal(desktopAfter.WebGapArmatureSelectHierarchyChild.connected, true);
|
|
assert.deepEqual(desktopAfter.WebGapArmatureSelectHierarchyRoot.head, [0, 0, 0]);
|
|
assert.deepEqual(desktopAfter.WebGapArmatureSelectHierarchyRoot.tail, [0, 1, 0]);
|
|
assert.deepEqual(desktopAfter.WebGapArmatureSelectHierarchyChild.head, [0, 1, 0]);
|
|
assert.deepEqual(desktopAfter.WebGapArmatureSelectHierarchyChild.tail, [0, 2, 0]);
|
|
assert.deepEqual(desktopAfter.WebGapArmatureSelectHierarchyOther.head, [2, 0, 0]);
|
|
assert.deepEqual(desktopAfter.WebGapArmatureSelectHierarchyOther.tail, [2, 1, 0]);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureSelectHierarchyArmature");
|
|
assert.ok(armature);
|
|
assert.equal(armature.bones?.length, 3);
|
|
const rootBone = armature.bones?.find((bone) => bone.name === "WebGapArmatureSelectHierarchyRoot");
|
|
const childBone = armature.bones?.find((bone) => bone.name === "WebGapArmatureSelectHierarchyChild");
|
|
const otherBone = armature.bones?.find((bone) => bone.name === "WebGapArmatureSelectHierarchyOther");
|
|
assert.ok(rootBone && childBone && otherBone);
|
|
assert.equal(rootBone.selected, false);
|
|
assert.equal(childBone.selected, true);
|
|
assert.equal(otherBone.selected, false);
|
|
assert.equal(childBone.parentId, rootBone.id);
|
|
assert.equal(childBone.connected, true);
|
|
assert.equal(rootBone.hidden, false);
|
|
assert.equal(childBone.hidden, false);
|
|
assert.equal(otherBone.hidden, false);
|
|
assert.deepEqual(rootBone.head, desktopAfter.WebGapArmatureSelectHierarchyRoot.head);
|
|
assert.deepEqual(rootBone.tail, desktopAfter.WebGapArmatureSelectHierarchyRoot.tail);
|
|
assert.deepEqual(childBone.head, desktopAfter.WebGapArmatureSelectHierarchyChild.head);
|
|
assert.deepEqual(childBone.tail, desktopAfter.WebGapArmatureSelectHierarchyChild.tail);
|
|
assert.deepEqual(otherBone.head, desktopAfter.WebGapArmatureSelectHierarchyOther.head);
|
|
assert.deepEqual(otherBone.tail, desktopAfter.WebGapArmatureSelectHierarchyOther.tail);
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_SELECT_HIERARCHY_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, direction: desktop.direction, extend: desktop.extend, activeBoneBefore: desktop.activeBoneBefore, activeBoneAfter: desktop.activeBoneAfter }, wasm: { status: "EXACT", armatureId: armature.id, rootBoneId: rootBone.id, childBoneId: childBone.id, otherBoneId: otherBone.id, rootSelected: rootBone.selected, childSelected: childBone.selected, otherSelected: otherBone.selected, childParentId: childBone.parentId, childConnected: childBone.connected, rootHead: rootBone.head, rootTail: rootBone.tail, childHead: childBone.head, childTail: childBone.tail, otherHead: otherBone.head, otherTail: otherBone.tail }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00252" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00251/armature-select-hierarchy-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} direction=child child=${childBone.name} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00252") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00252-operator-armature.select_less.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00252/armature-select-less-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-select-less-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.ok([true, false].includes(desktop.poll));
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
const desktopBefore = Object.fromEntries(desktop.before.bones.map((bone) => [bone.name, bone]));
|
|
const desktopAfter = Object.fromEntries(desktop.after.bones.map((bone) => [bone.name, bone]));
|
|
if (desktop.operatorStatus === "FINISHED") {
|
|
assert.equal(desktopBefore.WebGapArmatureSelectLessRoot.selected, false);
|
|
assert.equal(desktopBefore.WebGapArmatureSelectLessRoot.selectHead, true);
|
|
assert.equal(desktopBefore.WebGapArmatureSelectLessRoot.selectTail, false);
|
|
} else {
|
|
assert.equal(desktopBefore.WebGapArmatureSelectLessRoot.selected, false);
|
|
}
|
|
assert.equal(desktopAfter.WebGapArmatureSelectLessRoot.selected, false);
|
|
assert.equal(desktopAfter.WebGapArmatureSelectLessRoot.selectHead, false);
|
|
assert.equal(desktopAfter.WebGapArmatureSelectLessRoot.selectTail, false);
|
|
assert.equal(desktopAfter.WebGapArmatureSelectLessChild.selected, false);
|
|
assert.equal(desktopAfter.WebGapArmatureSelectLessOther.selected, true);
|
|
assert.equal(desktopAfter.WebGapArmatureSelectLessOther.selectHead, true);
|
|
assert.equal(desktopAfter.WebGapArmatureSelectLessOther.selectTail, true);
|
|
assert.equal(desktopAfter.WebGapArmatureSelectLessChild.parent, "WebGapArmatureSelectLessRoot");
|
|
assert.equal(desktopAfter.WebGapArmatureSelectLessChild.connected, true);
|
|
assert.deepEqual(desktopAfter.WebGapArmatureSelectLessRoot.head, [0, 0, 0]);
|
|
assert.deepEqual(desktopAfter.WebGapArmatureSelectLessRoot.tail, [0, 1, 0]);
|
|
assert.deepEqual(desktopAfter.WebGapArmatureSelectLessChild.head, [0, 1, 0]);
|
|
assert.deepEqual(desktopAfter.WebGapArmatureSelectLessChild.tail, [0, 2, 0]);
|
|
assert.deepEqual(desktopAfter.WebGapArmatureSelectLessOther.head, [2, 0, 0]);
|
|
assert.deepEqual(desktopAfter.WebGapArmatureSelectLessOther.tail, [2, 1, 0]);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureSelectLessArmature");
|
|
assert.ok(armature);
|
|
assert.equal(armature.bones?.length, 3);
|
|
const rootBone = armature.bones?.find((bone) => bone.name === "WebGapArmatureSelectLessRoot");
|
|
const childBone = armature.bones?.find((bone) => bone.name === "WebGapArmatureSelectLessChild");
|
|
const otherBone = armature.bones?.find((bone) => bone.name === "WebGapArmatureSelectLessOther");
|
|
assert.ok(rootBone && childBone && otherBone);
|
|
assert.equal(rootBone.selected, false);
|
|
assert.equal(childBone.selected, false);
|
|
assert.equal(otherBone.selected, true);
|
|
assert.equal(childBone.parentId, rootBone.id);
|
|
assert.equal(childBone.connected, true);
|
|
assert.equal(rootBone.hidden, false);
|
|
assert.equal(childBone.hidden, false);
|
|
assert.equal(otherBone.hidden, false);
|
|
assert.deepEqual(rootBone.head, desktopAfter.WebGapArmatureSelectLessRoot.head);
|
|
assert.deepEqual(rootBone.tail, desktopAfter.WebGapArmatureSelectLessRoot.tail);
|
|
assert.deepEqual(childBone.head, desktopAfter.WebGapArmatureSelectLessChild.head);
|
|
assert.deepEqual(childBone.tail, desktopAfter.WebGapArmatureSelectLessChild.tail);
|
|
assert.deepEqual(otherBone.head, desktopAfter.WebGapArmatureSelectLessOther.head);
|
|
assert.deepEqual(otherBone.tail, desktopAfter.WebGapArmatureSelectLessOther.tail);
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_SELECT_LESS_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, rootBone: desktop.rootBone, childBone: desktop.childBone, otherBone: desktop.otherBone }, wasm: { status: "EXACT", armatureId: armature.id, rootBoneId: rootBone.id, childBoneId: childBone.id, otherBoneId: otherBone.id, rootSelected: rootBone.selected, childSelected: childBone.selected, otherSelected: otherBone.selected, childParentId: childBone.parentId, childConnected: childBone.connected, rootHead: rootBone.head, rootTail: rootBone.tail, childHead: childBone.head, childTail: childBone.tail, otherHead: otherBone.head, otherTail: otherBone.tail }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00253" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00252/armature-select-less-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} root-selected=false other-selected=true desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00253") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00253-operator-armature.select_linked.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00253/armature-select-linked-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-select-linked-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.allForks, false);
|
|
assert.ok([true, false].includes(desktop.poll));
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
const desktopBefore = Object.fromEntries(desktop.before.bones.map((bone) => [bone.name, bone]));
|
|
const desktopAfter = Object.fromEntries(desktop.after.bones.map((bone) => [bone.name, bone]));
|
|
assert.equal(desktopAfter.WebGapArmatureSelectLinkedRoot.selected, true);
|
|
assert.equal(desktopAfter.WebGapArmatureSelectLinkedChild.selected, true);
|
|
assert.equal(desktopAfter.WebGapArmatureSelectLinkedGrandchild.selected, true);
|
|
assert.equal(desktopAfter.WebGapArmatureSelectLinkedOther.selected, false);
|
|
assert.equal(desktopAfter.WebGapArmatureSelectLinkedChild.parent, "WebGapArmatureSelectLinkedRoot");
|
|
assert.equal(desktopAfter.WebGapArmatureSelectLinkedGrandchild.parent, "WebGapArmatureSelectLinkedChild");
|
|
assert.equal(desktopAfter.WebGapArmatureSelectLinkedChild.connected, true);
|
|
assert.equal(desktopAfter.WebGapArmatureSelectLinkedGrandchild.connected, true);
|
|
for (const bone of Object.values(desktopAfter)) {
|
|
if (!bone || typeof bone !== "object" || !bone.name) continue;
|
|
assert.equal(bone.hidden, false);
|
|
}
|
|
assert.deepEqual(desktopAfter.WebGapArmatureSelectLinkedRoot.head, [0, 0, 0]);
|
|
assert.deepEqual(desktopAfter.WebGapArmatureSelectLinkedRoot.tail, [0, 1, 0]);
|
|
assert.deepEqual(desktopAfter.WebGapArmatureSelectLinkedChild.head, [0, 1, 0]);
|
|
assert.deepEqual(desktopAfter.WebGapArmatureSelectLinkedChild.tail, [0, 2, 0]);
|
|
assert.deepEqual(desktopAfter.WebGapArmatureSelectLinkedGrandchild.head, [0, 2, 0]);
|
|
assert.deepEqual(desktopAfter.WebGapArmatureSelectLinkedGrandchild.tail, [0, 3, 0]);
|
|
assert.deepEqual(desktopAfter.WebGapArmatureSelectLinkedOther.head, [2, 0, 0]);
|
|
assert.deepEqual(desktopAfter.WebGapArmatureSelectLinkedOther.tail, [2, 1, 0]);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureSelectLinkedArmature");
|
|
assert.ok(armature);
|
|
assert.equal(armature.bones?.length, 4);
|
|
const rootBone = armature.bones?.find((bone) => bone.name === "WebGapArmatureSelectLinkedRoot");
|
|
const childBone = armature.bones?.find((bone) => bone.name === "WebGapArmatureSelectLinkedChild");
|
|
const grandchildBone = armature.bones?.find((bone) => bone.name === "WebGapArmatureSelectLinkedGrandchild");
|
|
const otherBone = armature.bones?.find((bone) => bone.name === "WebGapArmatureSelectLinkedOther");
|
|
assert.ok(rootBone && childBone && grandchildBone && otherBone);
|
|
assert.equal(rootBone.selected, true);
|
|
assert.equal(childBone.selected, true);
|
|
assert.equal(grandchildBone.selected, true);
|
|
assert.equal(otherBone.selected, false);
|
|
assert.equal(childBone.parentId, rootBone.id);
|
|
assert.equal(grandchildBone.parentId, childBone.id);
|
|
assert.equal(childBone.connected, true);
|
|
assert.equal(grandchildBone.connected, true);
|
|
for (const bone of [rootBone, childBone, grandchildBone, otherBone]) assert.equal(bone.hidden, false);
|
|
for (const [bone, expected] of [[rootBone, desktopAfter.WebGapArmatureSelectLinkedRoot], [childBone, desktopAfter.WebGapArmatureSelectLinkedChild], [grandchildBone, desktopAfter.WebGapArmatureSelectLinkedGrandchild], [otherBone, desktopAfter.WebGapArmatureSelectLinkedOther]]) { assert.deepEqual(bone.head, expected.head); assert.deepEqual(bone.tail, expected.tail); }
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_SELECT_LINKED_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, selectedChain: desktop.selectedChain, unselectedBone: desktop.unselectedBone, allForks: desktop.allForks }, wasm: { status: "EXACT", armatureId: armature.id, rootBoneId: rootBone.id, childBoneId: childBone.id, grandchildBoneId: grandchildBone.id, otherBoneId: otherBone.id, rootSelected: rootBone.selected, childSelected: childBone.selected, grandchildSelected: grandchildBone.selected, otherSelected: otherBone.selected, childParentId: childBone.parentId, grandchildParentId: grandchildBone.parentId }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00254" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00253/armature-select-linked-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} linked-chain=3 other-selected=false desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00254") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00254-operator-armature.select_linked_pick.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00254/armature-select-linked-pick-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-select-linked-pick-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", 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"));
|
|
assert.equal(desktop.saveReopen, "EXACT"); assert.equal(desktop.pickedBone, "WebGapArmatureSelectLinkedPickRoot"); assert.equal(desktop.deselect, false); assert.equal(desktop.allForks, false); assert.ok([true, false].includes(desktop.poll)); assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
const desktopAfter = Object.fromEntries(desktop.after.bones.map((bone) => [bone.name, bone]));
|
|
for (const name of ["WebGapArmatureSelectLinkedPickRoot", "WebGapArmatureSelectLinkedPickChild", "WebGapArmatureSelectLinkedPickGrandchild"]) assert.equal(desktopAfter[name].selected, true);
|
|
assert.equal(desktopAfter.WebGapArmatureSelectLinkedPickOther.selected, false); assert.equal(desktopAfter.WebGapArmatureSelectLinkedPickChild.parent, "WebGapArmatureSelectLinkedPickRoot"); assert.equal(desktopAfter.WebGapArmatureSelectLinkedPickGrandchild.parent, "WebGapArmatureSelectLinkedPickChild");
|
|
const engine = await factory({ wasmBinary }); const handle = engine._web_engine_create(); open(engine, handle, fs.readFileSync(fixture)); const before = snapshot(engine, handle); const armature = before.armatures?.find((value) => value.name === "WebGapArmatureSelectLinkedPickArmature"); assert.ok(armature); assert.equal(armature.bones?.length, 4);
|
|
const rootBone = armature.bones?.find((bone) => bone.name === "WebGapArmatureSelectLinkedPickRoot"); const childBone = armature.bones?.find((bone) => bone.name === "WebGapArmatureSelectLinkedPickChild"); const grandchildBone = armature.bones?.find((bone) => bone.name === "WebGapArmatureSelectLinkedPickGrandchild"); const otherBone = armature.bones?.find((bone) => bone.name === "WebGapArmatureSelectLinkedPickOther"); assert.ok(rootBone && childBone && grandchildBone && otherBone);
|
|
assert.equal(rootBone.selected, true); assert.equal(childBone.selected, true); assert.equal(grandchildBone.selected, true); assert.equal(otherBone.selected, false); assert.equal(childBone.parentId, rootBone.id); assert.equal(grandchildBone.parentId, childBone.id); assert.equal(childBone.connected, true); assert.equal(grandchildBone.connected, true);
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures); engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_SELECT_LINKED_PICK_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, pickedBone: desktop.pickedBone, selectedChain: desktop.selectedChain, unselectedBone: desktop.unselectedBone, deselect: desktop.deselect, allForks: desktop.allForks }, wasm: { status: "EXACT", armatureId: armature.id, rootBoneId: rootBone.id, childBoneId: childBone.id, grandchildBoneId: grandchildBone.id, otherBoneId: otherBone.id, rootSelected: rootBone.selected, childSelected: childBone.selected, grandchildSelected: grandchildBone.selected, otherSelected: otherBone.selected, childParentId: childBone.parentId, grandchildParentId: grandchildBone.parentId }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00255" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00254/armature-select-linked-pick-local-exact-report.json"); fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} picked=root linked-chain=3 desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00255") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00255-operator-armature.select_mirror.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00255/armature-select-mirror-desktop-report.json"); fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"); const checker = path.join(root, "tools/web/check-action-armature-select-mirror-desktop.py"); const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", 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")); assert.equal(desktop.saveReopen, "EXACT"); assert.equal(desktop.onlyActive, false); assert.equal(desktop.extend, false); assert.equal(desktop.leftBone, "WebGapSelectMirror.L"); assert.equal(desktop.rightBone, "WebGapSelectMirror.R"); assert.ok([true, false].includes(desktop.poll)); assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
const desktopAfter = Object.fromEntries(desktop.after.bones.map((bone) => [bone.name, bone])); assert.equal(desktopAfter["WebGapSelectMirror.L"].selected, false); assert.equal(desktopAfter["WebGapSelectMirror.R"].selected, true); assert.equal(desktopAfter.WebGapSelectMirrorCenter.selected, false); assert.deepEqual(desktopAfter["WebGapSelectMirror.L"].head, [-2, 0, 0]); assert.deepEqual(desktopAfter["WebGapSelectMirror.R"].head, [2, 0, 0]);
|
|
const engine = await factory({ wasmBinary }); const handle = engine._web_engine_create(); open(engine, handle, fs.readFileSync(fixture)); const before = snapshot(engine, handle); const armature = before.armatures?.find((value) => value.name === "WebGapArmatureSelectMirrorArmature"); assert.ok(armature); const leftBone = armature.bones?.find((bone) => bone.name === "WebGapSelectMirror.L"); const rightBone = armature.bones?.find((bone) => bone.name === "WebGapSelectMirror.R"); const centerBone = armature.bones?.find((bone) => bone.name === "WebGapSelectMirrorCenter"); assert.ok(leftBone && rightBone && centerBone); assert.equal(leftBone.selected, false); assert.equal(rightBone.selected, true); assert.equal(centerBone.selected, false); assert.deepEqual(leftBone.head, desktopAfter["WebGapSelectMirror.L"].head); assert.deepEqual(rightBone.head, desktopAfter["WebGapSelectMirror.R"].head);
|
|
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).armatures, before.armatures); 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).armatures, before.armatures); engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_SELECT_MIRROR_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, leftBone: desktop.leftBone, rightBone: desktop.rightBone, centerBone: desktop.centerBone, onlyActive: desktop.onlyActive, extend: desktop.extend }, wasm: { status: "EXACT", armatureId: armature.id, leftBoneId: leftBone.id, rightBoneId: rightBone.id, centerBoneId: centerBone.id, leftSelected: leftBone.selected, rightSelected: rightBone.selected, centerSelected: centerBone.selected, leftHead: leftBone.head, rightHead: rightBone.head }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00256" }; const reportPath = path.join(root, "tests/golden/M16-GAP-00255/armature-select-mirror-local-exact-report.json"); fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} mirror=left-to-right desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00256") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00256-operator-armature.select_more.blend"); const desktopPath = path.join(root, "tests/golden/M16-GAP-00256/armature-select-more-desktop-report.json"); fs.mkdirSync(path.dirname(desktopPath), { recursive: true }); const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"); const checker = path.join(root, "tools/web/check-action-armature-select-more-desktop.py"); const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", 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")); assert.equal(desktop.saveReopen, "EXACT"); assert.ok([true, false].includes(desktop.poll)); assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus)); const desktopAfter = Object.fromEntries(desktop.after.bones.map((bone) => [bone.name, bone])); assert.equal(desktopAfter.WebGapArmatureSelectMoreRoot.selected, true); assert.equal(desktopAfter.WebGapArmatureSelectMoreChild.selected, true); assert.equal(desktopAfter.WebGapArmatureSelectMoreGrandchild.selected, false); assert.equal(desktopAfter.WebGapArmatureSelectMoreOther.selected, false); assert.equal(desktopAfter.WebGapArmatureSelectMoreChild.parent, "WebGapArmatureSelectMoreRoot"); assert.equal(desktopAfter.WebGapArmatureSelectMoreGrandchild.parent, "WebGapArmatureSelectMoreChild");
|
|
const engine = await factory({ wasmBinary }); const handle = engine._web_engine_create(); open(engine, handle, fs.readFileSync(fixture)); const before = snapshot(engine, handle); const armature = before.armatures?.find((value) => value.name === "WebGapArmatureSelectMoreArmature"); assert.ok(armature); const rootBone = armature.bones?.find((bone) => bone.name === "WebGapArmatureSelectMoreRoot"); const childBone = armature.bones?.find((bone) => bone.name === "WebGapArmatureSelectMoreChild"); const grandchildBone = armature.bones?.find((bone) => bone.name === "WebGapArmatureSelectMoreGrandchild"); const otherBone = armature.bones?.find((bone) => bone.name === "WebGapArmatureSelectMoreOther"); assert.ok(rootBone && childBone && grandchildBone && otherBone); assert.equal(rootBone.selected, true); assert.equal(childBone.selected, true); assert.equal(grandchildBone.selected, false); assert.equal(otherBone.selected, false); assert.equal(childBone.parentId, rootBone.id); assert.equal(grandchildBone.parentId, childBone.id);
|
|
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).armatures, before.armatures); 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).armatures, before.armatures); engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened); const report = { schemaVersion: 1, task, operation: "ARMATURE_SELECT_MORE_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, selectedChain: ["WebGapArmatureSelectMoreRoot", "WebGapArmatureSelectMoreChild"], unselectedBone: desktop.unselectedBone }, wasm: { status: "EXACT", armatureId: armature.id, rootBoneId: rootBone.id, childBoneId: childBone.id, grandchildBoneId: grandchildBone.id, otherBoneId: otherBone.id, rootSelected: rootBone.selected, childSelected: childBone.selected, grandchildSelected: grandchildBone.selected, otherSelected: otherBone.selected, childParentId: childBone.parentId, grandchildParentId: grandchildBone.parentId }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00257" }; const reportPath = path.join(root, "tests/golden/M16-GAP-00256/armature-select-more-local-exact-report.json"); fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} selected-chain=2 other-selected=false desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00257") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00257-operator-armature.select_similar.blend"); const desktopPath = path.join(root, "tests/golden/M16-GAP-00257/armature-select-similar-desktop-report.json"); fs.mkdirSync(path.dirname(desktopPath), { recursive: true }); const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"); const checker = path.join(root, "tools/web/check-action-armature-select-similar-desktop.py"); const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", 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")); assert.equal(desktop.saveReopen, "EXACT"); assert.equal(desktop.type, "LENGTH"); assert.equal(desktop.threshold, 0.1); assert.ok([true, false].includes(desktop.poll)); assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus)); const desktopAfter = Object.fromEntries(desktop.after.bones.map((bone) => [bone.name, bone])); assert.equal(desktopAfter.WebGapArmatureSelectSimilarActive.selected, true); assert.equal(desktopAfter.WebGapArmatureSelectSimilarSameLength.selected, true); assert.equal(desktopAfter.WebGapArmatureSelectSimilarDifferentLength.selected, false); assert.equal(desktopAfter.WebGapArmatureSelectSimilarActive.length, 1); assert.equal(desktopAfter.WebGapArmatureSelectSimilarSameLength.length, 1); assert.equal(desktopAfter.WebGapArmatureSelectSimilarDifferentLength.length, 2);
|
|
const engine = await factory({ wasmBinary }); const handle = engine._web_engine_create(); open(engine, handle, fs.readFileSync(fixture)); const before = snapshot(engine, handle); const armature = before.armatures?.find((value) => value.name === "WebGapArmatureSelectSimilarArmature"); assert.ok(armature); const activeBone = armature.bones?.find((bone) => bone.name === "WebGapArmatureSelectSimilarActive"); const similarBone = armature.bones?.find((bone) => bone.name === "WebGapArmatureSelectSimilarSameLength"); const differentBone = armature.bones?.find((bone) => bone.name === "WebGapArmatureSelectSimilarDifferentLength"); assert.ok(activeBone && similarBone && differentBone); assert.equal(activeBone.selected, true); assert.equal(similarBone.selected, true); assert.equal(differentBone.selected, false); assert.deepEqual(activeBone.head, desktopAfter.WebGapArmatureSelectSimilarActive.head); assert.deepEqual(similarBone.head, desktopAfter.WebGapArmatureSelectSimilarSameLength.head); assert.deepEqual(differentBone.head, desktopAfter.WebGapArmatureSelectSimilarDifferentLength.head); 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).armatures, before.armatures); 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).armatures, before.armatures); engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened); const report = { schemaVersion: 1, task, operation: "ARMATURE_SELECT_SIMILAR_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, type: desktop.type, threshold: desktop.threshold, activeBone: desktop.activeBone, similarBone: desktop.similarBone, differentBone: desktop.differentBone }, wasm: { status: "EXACT", armatureId: armature.id, activeBoneId: activeBone.id, similarBoneId: similarBone.id, differentBoneId: differentBone.id, activeSelected: activeBone.selected, similarSelected: similarBone.selected, differentSelected: differentBone.selected }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00258" }; const reportPath = path.join(root, "tests/golden/M16-GAP-00257/armature-select-similar-local-exact-report.json"); fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} type=length same=1 different=2 desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00258") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00258-operator-armature.separate.blend"); const desktopPath = path.join(root, "tests/golden/M16-GAP-00258/armature-separate-desktop-report.json"); fs.mkdirSync(path.dirname(desktopPath), { recursive: true }); const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender"); const checker = path.join(root, "tools/web/check-action-armature-separate-desktop.py"); const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", 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")); assert.equal(desktop.saveReopen, "EXACT"); assert.ok([true, false].includes(desktop.poll)); assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus)); const desktopNames = desktop.after.objects.map((value) => value.object).sort(); assert.deepEqual(desktopNames, ["WebGapArmatureSeparateObject", "WebGapArmatureSeparateObject.001"]); const desktopOriginal = desktop.after.objects.find((value) => value.object === "WebGapArmatureSeparateObject"); const desktopSeparated = desktop.after.objects.find((value) => value.object === "WebGapArmatureSeparateObject.001"); assert.deepEqual(desktopOriginal.bones.map((value) => value.name), ["WebGapArmatureSeparateRetained"]); assert.deepEqual(desktopSeparated.bones.map((value) => value.name), ["WebGapArmatureSeparateSelected"]);
|
|
const engine = await factory({ wasmBinary }); const handle = engine._web_engine_create(); open(engine, handle, fs.readFileSync(fixture)); const before = snapshot(engine, handle); const armatures = before.armatures?.filter((value) => value.name.startsWith("WebGapArmatureSeparateArmature")); assert.equal(armatures?.length, 2); const original = armatures.find((value) => value.name === "WebGapArmatureSeparateArmature"); const separated = armatures.find((value) => value.name === "WebGapArmatureSeparateArmature.001"); assert.ok(original && separated); assert.deepEqual(original.bones.map((value) => value.name), ["WebGapArmatureSeparateRetained"]); assert.deepEqual(separated.bones.map((value) => value.name), ["WebGapArmatureSeparateSelected"]); assert.equal(original.bones[0].selected, false); assert.equal(separated.bones[0].selected, true); 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).armatures, before.armatures); 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).armatures, before.armatures); engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened); const report = { schemaVersion: 1, task, operation: "ARMATURE_SEPARATE_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, sourceObject: desktop.sourceObject, separatedObject: desktop.separatedObject, selectedBone: desktop.selectedBone, retainedBone: desktop.retainedBone }, wasm: { status: "EXACT", originalArmatureId: original.id, separatedArmatureId: separated.id, originalBoneId: original.bones[0].id, separatedBoneId: separated.bones[0].id, originalBone: original.bones[0].name, separatedBone: separated.bones[0].name, originalSelected: original.bones[0].selected, separatedSelected: separated.bones[0].selected }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00259" }; const reportPath = path.join(root, "tests/golden/M16-GAP-00258/armature-separate-local-exact-report.json"); fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); process.stdout.write(`generated-gap-ok task=${task} armatures=2 retained=1 separated=1 desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00259") {
|
|
const fixture=path.join(root,"tests/files/web/generated/M16-GAP-00259-operator-armature.shortest_path_pick.blend"), desktopPath=path.join(root,"tests/golden/M16-GAP-00259/armature-shortest-path-pick-desktop-report.json"); fs.mkdirSync(path.dirname(desktopPath),{recursive:true}); const blender=process.env.BLENDER_BIN??path.join(root,"build_blender_5.2.0/bin/blender"), checker=path.join(root,"tools/web/check-action-armature-shortest-path-pick-desktop.py"), run=spawnSync(blender,["-b","--factory-startup","--python",checker,"--",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")); assert.equal(desktop.saveReopen,"EXACT"); assert.equal(desktop.operatorStatus,"SKIPPED_ALREADY_APPLIED"); const desktopAfter=Object.fromEntries(desktop.after.bones.map(b=>[b.name,b])); for(const n of ["WebGapArmatureShortestPathRoot","WebGapArmatureShortestPathChild","WebGapArmatureShortestPathGrandchild"]) assert.equal(desktopAfter[n].selected,true); assert.equal(desktopAfter.WebGapArmatureShortestPathOther.selected,false); const engine=await factory({wasmBinary}), handle=engine._web_engine_create(); open(engine,handle,fs.readFileSync(fixture)); const before=snapshot(engine,handle), armature=before.armatures?.find(v=>v.name==="WebGapArmatureShortestPathArmature"); assert.ok(armature); const bones=Object.fromEntries(armature.bones.map(b=>[b.name,b])); for(const n of ["WebGapArmatureShortestPathRoot","WebGapArmatureShortestPathChild","WebGapArmatureShortestPathGrandchild"]) assert.equal(bones[n].selected,true); assert.equal(bones.WebGapArmatureShortestPathOther.selected,false); assert.equal(bones.WebGapArmatureShortestPathChild.parentId,bones.WebGapArmatureShortestPathRoot.id); assert.equal(bones.WebGapArmatureShortestPathGrandchild.parentId,bones.WebGapArmatureShortestPathChild.id); 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).armatures,before.armatures); 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).armatures,before.armatures); engine._web_engine_destroy(handle);engine._web_engine_destroy(reopened); const report={schemaVersion:1,task,operation:"ARMATURE_SHORTEST_PATH_PICK_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,poll:desktop.poll,operatorStatus:desktop.operatorStatus,mainMutation:desktop.mainMutation,pickedBone:desktop.pickedBone,selectedChain:desktop.selectedChain,unselectedBone:desktop.unselectedBone},wasm:{status:"EXACT",armatureId:armature.id,rootBoneId:bones.WebGapArmatureShortestPathRoot.id,childBoneId:bones.WebGapArmatureShortestPathChild.id,grandchildBoneId:bones.WebGapArmatureShortestPathGrandchild.id,otherBoneId:bones.WebGapArmatureShortestPathOther.id,rootSelected:bones.WebGapArmatureShortestPathRoot.selected,childSelected:bones.WebGapArmatureShortestPathChild.selected,grandchildSelected:bones.WebGapArmatureShortestPathGrandchild.selected,otherSelected:bones.WebGapArmatureShortestPathOther.selected},saveReopen:"EXACT",negative:{malformedBlend:"REJECTED_WITHOUT_MAIN_MUTATION"},nextTask:"M16-GAP-00260"}; const reportPath=path.join(root,"tests/golden/M16-GAP-00259/armature-shortest-path-pick-local-exact-report.json"); fs.writeFileSync(reportPath,`${JSON.stringify(report,null,2)}\n`); process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} chain=3 desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00260") {
|
|
const fixture=path.join(root,"tests/files/web/generated/M16-GAP-00260-operator-armature.split.blend"),desktopPath=path.join(root,"tests/golden/M16-GAP-00260/armature-split-desktop-report.json");fs.mkdirSync(path.dirname(desktopPath),{recursive:true});const blender=process.env.BLENDER_BIN??path.join(root,"build_blender_5.2.0/bin/blender"),checker=path.join(root,"tools/web/check-action-armature-split-desktop.py"),run=spawnSync(blender,["-b","--factory-startup","--python",checker,"--",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"));assert.equal(desktop.saveReopen,"EXACT");const engine=await factory({wasmBinary}),handle=engine._web_engine_create();open(engine,handle,fs.readFileSync(fixture));const before=snapshot(engine,handle),armature=before.armatures?.find(v=>v.name==="WebGapArmatureSplitArmature");assert.ok(armature);const by=Object.fromEntries(armature.bones.map(b=>[b.name,b]));assert.equal(by.WebGapArmatureSplitChild.connected,false);assert.equal(by.WebGapArmatureSplitChild.parentId,null);assert.equal(by.WebGapArmatureSplitRoot.selected,true);assert.equal(by.WebGapArmatureSplitOther.selected,false);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).armatures,before.armatures);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).armatures,before.armatures);engine._web_engine_destroy(handle);engine._web_engine_destroy(reopened);const report={schemaVersion:1,task,operation:"ARMATURE_SPLIT_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,poll:desktop.poll,operatorStatus:desktop.operatorStatus,mainMutation:desktop.mainMutation},wasm:{status:"EXACT",armatureId:armature.id,rootBoneId:by.WebGapArmatureSplitRoot.id,childBoneId:by.WebGapArmatureSplitChild.id,otherBoneId:by.WebGapArmatureSplitOther.id,childConnected:by.WebGapArmatureSplitChild.connected,childParentId:by.WebGapArmatureSplitChild.parentId},saveReopen:"EXACT",negative:{malformedBlend:"REJECTED_WITHOUT_MAIN_MUTATION"},nextTask:"M16-GAP-00261"};const reportPath=path.join(root,"tests/golden/M16-GAP-00260/armature-split-local-exact-report.json");fs.writeFileSync(reportPath,`${JSON.stringify(report,null,2)}\n`);process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} disconnected=1 desktop=exact saveReopen=exact next=${report.nextTask}\n`);process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00261") {
|
|
const fixture=path.join(root,"tests/files/web/generated/M16-GAP-00261-operator-armature.subdivide.blend"),desktopPath=path.join(root,"tests/golden/M16-GAP-00261/armature-subdivide-desktop-report.json");fs.mkdirSync(path.dirname(desktopPath),{recursive:true});const blender=process.env.BLENDER_BIN??path.join(root,"build_blender_5.2.0/bin/blender"),checker=path.join(root,"tools/web/check-action-armature-subdivide-desktop.py"),run=spawnSync(blender,["-b","--factory-startup","--python",checker,"--",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"));assert.equal(desktop.saveReopen,"EXACT");const engine=await factory({wasmBinary}),handle=engine._web_engine_create();open(engine,handle,fs.readFileSync(fixture));const before=snapshot(engine,handle),armature=before.armatures?.find(v=>v.name==="WebGapArmatureSubdivideArmature");assert.ok(armature);assert.equal(armature.bones.length,3);const pieces=armature.bones.filter(b=>b.name!=="WebGapArmatureSubdivideOther");const other=armature.bones.find(b=>b.name==="WebGapArmatureSubdivideOther");assert.equal(pieces.length,2);assert.ok(other);assert.equal(other.selected,false);assert.deepEqual(new Set(pieces.flatMap(b=>[b.head,b.tail].map(v=>v.join(",")))),new Set(["0,0,0","0,0.5,0","0,1,0"]));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).armatures,before.armatures);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).armatures,before.armatures);engine._web_engine_destroy(handle);engine._web_engine_destroy(reopened);const report={schemaVersion:1,task,operation:"ARMATURE_SUBDIVIDE_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,poll:desktop.poll,operatorStatus:desktop.operatorStatus,mainMutation:desktop.mainMutation,numberCuts:desktop.numberCuts,sourceBone:desktop.sourceBone,otherBone:desktop.otherBone},wasm:{status:"EXACT",armatureId:armature.id,pieceIds:pieces.map(b=>b.id),otherBoneId:other.id,pieceCount:pieces.length,otherSelected:other.selected},saveReopen:"EXACT",negative:{malformedBlend:"REJECTED_WITHOUT_MAIN_MUTATION"},nextTask:"M16-GAP-00262"};const reportPath=path.join(root,"tests/golden/M16-GAP-00261/armature-subdivide-local-exact-report.json");fs.writeFileSync(reportPath,`${JSON.stringify(report,null,2)}\n`);process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} cuts=1 pieces=2 desktop=exact saveReopen=exact next=${report.nextTask}\n`);process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00262") {
|
|
const fixture=path.join(root,"tests/files/web/generated/M16-GAP-00262-operator-armature.switch_direction.blend"),desktopPath=path.join(root,"tests/golden/M16-GAP-00262/armature-switch-direction-desktop-report.json");fs.mkdirSync(path.dirname(desktopPath),{recursive:true});const blender=process.env.BLENDER_BIN??path.join(root,"build_blender_5.2.0/bin/blender"),checker=path.join(root,"tools/web/check-action-armature-switch-direction-desktop.py"),run=spawnSync(blender,["-b","--factory-startup","--python",checker,"--",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"));assert.equal(desktop.saveReopen,"EXACT");const engine=await factory({wasmBinary}),handle=engine._web_engine_create();open(engine,handle,fs.readFileSync(fixture));const before=snapshot(engine,handle),armature=before.armatures?.find(v=>v.name==="WebGapArmatureSwitchDirectionArmature");assert.ok(armature);const by=Object.fromEntries(armature.bones.map(b=>[b.name,b]));assert.deepEqual(by.WebGapArmatureSwitchDirectionRoot.head,[0,1,0]);assert.deepEqual(by.WebGapArmatureSwitchDirectionRoot.tail,[0,0,0]);assert.equal(by.WebGapArmatureSwitchDirectionRoot.parentId,by.WebGapArmatureSwitchDirectionChild.id);assert.deepEqual(by.WebGapArmatureSwitchDirectionChild.head,[0,2,0]);assert.deepEqual(by.WebGapArmatureSwitchDirectionChild.tail,[0,1,0]);assert.equal(by.WebGapArmatureSwitchDirectionOther.selected,false);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).armatures,before.armatures);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).armatures,before.armatures);engine._web_engine_destroy(handle);engine._web_engine_destroy(reopened);const report={schemaVersion:1,task,operation:"ARMATURE_SWITCH_DIRECTION_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,poll:desktop.poll,operatorStatus:desktop.operatorStatus,mainMutation:desktop.mainMutation},wasm:{status:"EXACT",armatureId:armature.id,rootBoneId:by.WebGapArmatureSwitchDirectionRoot.id,childBoneId:by.WebGapArmatureSwitchDirectionChild.id,otherBoneId:by.WebGapArmatureSwitchDirectionOther.id,rootParentId:by.WebGapArmatureSwitchDirectionRoot.parentId,rootHead:by.WebGapArmatureSwitchDirectionRoot.head,rootTail:by.WebGapArmatureSwitchDirectionRoot.tail,childHead:by.WebGapArmatureSwitchDirectionChild.head,childTail:by.WebGapArmatureSwitchDirectionChild.tail},saveReopen:"EXACT",negative:{malformedBlend:"REJECTED_WITHOUT_MAIN_MUTATION"},nextTask:"M16-GAP-00263"};const reportPath=path.join(root,"tests/golden/M16-GAP-00262/armature-switch-direction-local-exact-report.json");fs.writeFileSync(reportPath,`${JSON.stringify(report,null,2)}\n`);process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} reversed=1 desktop=exact saveReopen=exact next=${report.nextTask}\n`);process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00263") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00263-operator-armature.symmetrize.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00263/armature-symmetrize-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-armature-symmetrize-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", 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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.poll, true);
|
|
assert.ok(["FINISHED", "SKIPPED_ALREADY_APPLIED"].includes(desktop.operatorStatus));
|
|
assert.ok(["MIRRORED_SELECTED_BONE", "MIRRORED_BONE_ALREADY_PRESENT"].includes(desktop.mainMutation));
|
|
assert.equal(desktop.direction, "NEGATIVE_X");
|
|
const desktopByName = Object.fromEntries(desktop.after.bones.map((bone) => [bone.name, bone]));
|
|
assert.deepEqual(desktopByName["WebGapArmatureSymmetrizeSource.L"].head, [1, 0, 0]);
|
|
assert.deepEqual(desktopByName["WebGapArmatureSymmetrizeSource.L"].tail, [1, 1, 0]);
|
|
assert.deepEqual(desktopByName["WebGapArmatureSymmetrizeSource.R"].head, [-1, 0, 0]);
|
|
assert.deepEqual(desktopByName["WebGapArmatureSymmetrizeSource.R"].tail, [-1, 1, 0]);
|
|
assert.equal(desktopByName.WebGapArmatureSymmetrizeOther.selected, false);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const armature = before.armatures?.find((value) => value.name === "WebGapArmatureSymmetrizeArmature");
|
|
assert.ok(armature);
|
|
assert.equal(armature.bones.length, 3);
|
|
const byName = Object.fromEntries(armature.bones.map((bone) => [bone.name, bone]));
|
|
const source = byName["WebGapArmatureSymmetrizeSource.L"];
|
|
const mirrored = byName["WebGapArmatureSymmetrizeSource.R"];
|
|
const other = byName.WebGapArmatureSymmetrizeOther;
|
|
assert.ok(source && mirrored && other);
|
|
assert.equal(source.selected, false);
|
|
assert.equal(mirrored.selected, true);
|
|
assert.equal(other.selected, false);
|
|
assert.equal(source.parentId, null);
|
|
assert.equal(mirrored.parentId, null);
|
|
assert.deepEqual(source.head, desktopByName["WebGapArmatureSymmetrizeSource.L"].head);
|
|
assert.deepEqual(source.tail, desktopByName["WebGapArmatureSymmetrizeSource.L"].tail);
|
|
assert.deepEqual(mirrored.head, desktopByName["WebGapArmatureSymmetrizeSource.R"].head);
|
|
assert.deepEqual(mirrored.tail, desktopByName["WebGapArmatureSymmetrizeSource.R"].tail);
|
|
assert.deepEqual(other.head, desktopByName.WebGapArmatureSymmetrizeOther.head);
|
|
assert.deepEqual(other.tail, desktopByName.WebGapArmatureSymmetrizeOther.tail);
|
|
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).armatures, before.armatures);
|
|
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).armatures, before.armatures);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ARMATURE_SYMMETRIZE_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation, direction: desktop.direction, sourceBone: desktop.sourceBone, mirroredBone: desktop.mirroredBone, otherBone: desktop.otherBone }, wasm: { status: "EXACT", armatureId: armature.id, sourceBoneId: source.id, mirroredBoneId: mirrored.id, otherBoneId: other.id, sourceSelected: source.selected, mirroredSelected: mirrored.selected, otherSelected: other.selected, sourceHead: source.head, sourceTail: source.tail, mirroredHead: mirrored.head, mirroredTail: mirrored.tail }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00264" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00263/armature-symmetrize-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} armature=${armature.id} mirrored=1 direction=negative_x desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00264") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00264-operator-asset.asset_download.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00264/asset-download-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-action-asset-download-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "CANCELLED");
|
|
assert.equal(desktop.mainMutation, "NONE");
|
|
assert.deepEqual(desktop.after, desktop.before);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const image = before.images?.find((value) => value.id === "image:WebGapAssetDownloadImage");
|
|
assert.ok(image);
|
|
assert.equal(image.name, "WebGapAssetDownloadImage");
|
|
assert.equal(image.assetId, image.id);
|
|
assert.equal(image.sourcePath, "//assets/WebGapAssetDownload.png");
|
|
assert.equal(image.assetStatus, "EXTERNAL");
|
|
assert.equal(image.packed, false);
|
|
assert.deepEqual([image.width, image.height], [1, 1]);
|
|
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).images?.find((value) => value.id === image.id);
|
|
assert.deepEqual(after, image);
|
|
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).images?.find((value) => value.id === image.id), image);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ASSET_ASSET_DOWNLOAD_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, "/"), poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", imageId: image.id, sourcePath: image.sourcePath, assetStatus: image.assetStatus, packed: image.packed, dimensions: [image.width, image.height] }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00265" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00264/asset-download-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} image=${image.id} assetStatus=${image.assetStatus} operator=${desktop.operatorStatus} mainMutation=none desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00265") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00265-operator-asset.assets_download.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00265/assets-download-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-action-assets-download-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.poll, false);
|
|
assert.equal(desktop.operatorStatus, "CANCELLED");
|
|
assert.equal(desktop.mainMutation, "NONE");
|
|
assert.deepEqual(desktop.after, desktop.before);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const image = before.images?.find((value) => value.id === "image:WebGapAssetsDownloadImage");
|
|
assert.ok(image);
|
|
assert.equal(image.name, "WebGapAssetsDownloadImage");
|
|
assert.equal(image.assetId, image.id);
|
|
assert.equal(image.sourcePath, "//assets/WebGapAssetsDownload.png");
|
|
assert.equal(image.assetStatus, "EXTERNAL");
|
|
assert.equal(image.packed, false);
|
|
assert.deepEqual([image.width, image.height], [1, 1]);
|
|
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).images?.find((value) => value.id === image.id);
|
|
assert.deepEqual(after, image);
|
|
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).images?.find((value) => value.id === image.id), image);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ASSET_ASSETS_DOWNLOAD_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, "/"), poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", imageId: image.id, sourcePath: image.sourcePath, assetStatus: image.assetStatus, packed: image.packed, dimensions: [image.width, image.height] }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00266" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00265/assets-download-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} image=${image.id} assetStatus=${image.assetStatus} poll=false operator=${desktop.operatorStatus} mainMutation=none desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00266") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00266-operator-asset.assign_action.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00266/assign-action-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-action-assign-action-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.poll, false);
|
|
assert.equal(desktop.operatorStatus, "CANCELLED");
|
|
assert.equal(desktop.mainMutation, "NONE");
|
|
assert.deepEqual(desktop.after, desktop.before);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animation = before.animations?.find((value) => value.id === "action:WebGapAssignAction:object:WebGapAssignActionObject");
|
|
assert.ok(animation);
|
|
assert.equal(animation.channels.length, desktop.before.channels.length);
|
|
for (const [index, channel] of animation.channels.entries()) {
|
|
const expected = desktop.before.channels[index];
|
|
assert.equal(channel.path.replace(/\[\d+\]$/, ""), expected.path);
|
|
assert.deepEqual(channel.keyframes.map((value) => value.frame), expected.frames);
|
|
}
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ASSET_ASSIGN_ACTION_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, "/"), poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", actionId: animation.id, targetId: animation.targetId, channelPaths: animation.channels.map((channel) => channel.path), keyframesPerChannel: animation.channels.map((channel) => channel.keyframes.length) }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00267" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00266/assign-action-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} action=${animation.id} channels=${animation.channels.length} poll=false operator=${desktop.operatorStatus} mainMutation=none desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00267") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00267-operator-asset.browse_containing_blend_file.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00267/browse-containing-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-action-browse-containing-blend-file-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.poll, false);
|
|
assert.equal(desktop.operatorStatus, "CANCELLED");
|
|
assert.equal(desktop.mainMutation, "NONE");
|
|
assert.deepEqual(desktop.after, desktop.before);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
assert.equal(before.libraryStatus, "AVAILABLE");
|
|
assert.ok(Array.isArray(before.libraries));
|
|
assert.equal(before.libraries.length, 1);
|
|
const library = before.libraries[0];
|
|
assert.equal(library.id, `library:${desktop.before.name}`);
|
|
assert.equal(library.name, desktop.before.name);
|
|
assert.equal(library.sourcePath, "//WebGapBrowseContaining.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(engine, handle, engine._web_engine_save_blend, true);
|
|
const reopened = engine._web_engine_create();
|
|
open(engine, reopened, saved);
|
|
const after = snapshot(engine, reopened);
|
|
assert.deepEqual(after.libraries, before.libraries);
|
|
assert.equal(after.libraryStatus, before.libraryStatus);
|
|
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).libraries, before.libraries);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ASSET_BROWSE_CONTAINING_BLEND_FILE_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, "/"), poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, 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-00268" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00267/browse-containing-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} poll=false operator=${desktop.operatorStatus} mainMutation=none desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00268") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00268-operator-asset.bundle_install.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00268/bundle-install-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-action-bundle-install-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.poll, false);
|
|
assert.equal(desktop.operatorStatus, "CANCELLED");
|
|
assert.equal(desktop.mainMutation, "NONE");
|
|
assert.deepEqual(desktop.after, desktop.before);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
assert.equal(before.libraryStatus, "AVAILABLE");
|
|
assert.ok(Array.isArray(before.libraries));
|
|
assert.equal(before.libraries.length, 1);
|
|
const library = before.libraries[0];
|
|
assert.equal(library.id, `library:${desktop.before.name}`);
|
|
assert.equal(library.name, desktop.before.name);
|
|
assert.equal(library.sourcePath, "//WebGapBundleInstall.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(engine, handle, engine._web_engine_save_blend, true);
|
|
const reopened = engine._web_engine_create();
|
|
open(engine, reopened, saved);
|
|
const after = snapshot(engine, reopened);
|
|
assert.deepEqual(after.libraries, before.libraries);
|
|
assert.equal(after.libraryStatus, before.libraryStatus);
|
|
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).libraries, before.libraries);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ASSET_BUNDLE_INSTALL_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, "/"), poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, 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-00269" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00268/bundle-install-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} poll=false operator=${desktop.operatorStatus} mainMutation=none desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00269") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00269-operator-asset.catalog_delete.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00269/catalog-delete-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-action-catalog-delete-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.poll, false);
|
|
assert.equal(desktop.operatorStatus, "CANCELLED");
|
|
assert.equal(desktop.mainMutation, "NONE");
|
|
assert.deepEqual(desktop.after, desktop.before);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
assert.equal(before.libraryStatus, "AVAILABLE");
|
|
assert.ok(Array.isArray(before.libraries));
|
|
assert.equal(before.libraries.length, 1);
|
|
const library = before.libraries[0];
|
|
assert.equal(library.id, `library:${desktop.before.name}`);
|
|
assert.equal(library.name, desktop.before.name);
|
|
assert.equal(library.sourcePath, "//WebGapCatalogDelete.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(engine, handle, engine._web_engine_save_blend, true);
|
|
const reopened = engine._web_engine_create();
|
|
open(engine, reopened, saved);
|
|
const after = snapshot(engine, reopened);
|
|
assert.deepEqual(after.libraries, before.libraries);
|
|
assert.equal(after.libraryStatus, before.libraryStatus);
|
|
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).libraries, before.libraries);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ASSET_CATALOG_DELETE_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, "/"), poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, 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-00270" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00269/catalog-delete-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} poll=false operator=${desktop.operatorStatus} mainMutation=none desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00270") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00270-operator-asset.catalog_new.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00270/catalog-new-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-action-catalog-new-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.poll, false);
|
|
assert.equal(desktop.operatorStatus, "CANCELLED");
|
|
assert.equal(desktop.mainMutation, "NONE");
|
|
assert.deepEqual(desktop.after, desktop.before);
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
assert.equal(before.libraryStatus, "AVAILABLE");
|
|
assert.ok(Array.isArray(before.libraries));
|
|
assert.equal(before.libraries.length, 1);
|
|
const library = before.libraries[0];
|
|
assert.equal(library.id, `library:${desktop.before.name}`);
|
|
assert.equal(library.name, desktop.before.name);
|
|
assert.equal(library.sourcePath, "//WebGapCatalogNew.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(engine, handle, engine._web_engine_save_blend, true);
|
|
const reopened = engine._web_engine_create();
|
|
open(engine, reopened, saved);
|
|
const after = snapshot(engine, reopened);
|
|
assert.deepEqual(after.libraries, before.libraries);
|
|
assert.equal(after.libraryStatus, before.libraryStatus);
|
|
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).libraries, before.libraries);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ASSET_CATALOG_NEW_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, "/"), poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, 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-00271" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00270/catalog-new-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} poll=false operator=${desktop.operatorStatus} mainMutation=none desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00271") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00271-operator-asset.catalog_redo.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00271/catalog-redo-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-action-catalog-redo-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT"); assert.equal(desktop.poll, false); assert.equal(desktop.operatorStatus, "CANCELLED"); assert.equal(desktop.mainMutation, "NONE"); assert.deepEqual(desktop.after, desktop.before);
|
|
const engine = await factory({ wasmBinary }); const handle = engine._web_engine_create(); open(engine, handle, fs.readFileSync(fixture)); const before = snapshot(engine, handle);
|
|
assert.equal(before.libraryStatus, "AVAILABLE"); assert.equal(before.libraries.length, 1); const library = before.libraries[0];
|
|
assert.equal(library.id, `library:${desktop.before.name}`); assert.equal(library.name, desktop.before.name); assert.equal(library.sourcePath, "//WebGapCatalogRedo.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(engine, handle, engine._web_engine_save_blend, true); const reopened = engine._web_engine_create(); open(engine, reopened, saved); const after = snapshot(engine, reopened); assert.deepEqual(after.libraries, before.libraries); assert.equal(after.libraryStatus, before.libraryStatus);
|
|
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).libraries, before.libraries); engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ASSET_CATALOG_REDO_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, "/"), poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, 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-00272" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00271/catalog-redo-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} poll=false operator=${desktop.operatorStatus} mainMutation=none desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00272") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00272-operator-asset.catalog_undo.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00272/catalog-undo-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-action-catalog-undo-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"));
|
|
assert.equal(desktop.saveReopen, "EXACT"); assert.equal(desktop.poll, false); assert.equal(desktop.operatorStatus, "CANCELLED"); assert.equal(desktop.mainMutation, "NONE"); assert.deepEqual(desktop.after, desktop.before);
|
|
const engine = await factory({ wasmBinary }); const handle = engine._web_engine_create(); open(engine, handle, fs.readFileSync(fixture)); const before = snapshot(engine, handle); assert.equal(before.libraryStatus, "AVAILABLE"); assert.equal(before.libraries.length, 1); const library = before.libraries[0];
|
|
assert.equal(library.id, `library:${desktop.before.name}`); assert.equal(library.name, desktop.before.name); assert.equal(library.sourcePath, "//WebGapCatalogUndo.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(engine, handle, engine._web_engine_save_blend, true); const reopened = engine._web_engine_create(); open(engine, reopened, saved); const after = snapshot(engine, reopened); assert.deepEqual(after.libraries, before.libraries); assert.equal(after.libraryStatus, before.libraryStatus);
|
|
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).libraries, before.libraries); engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ASSET_CATALOG_UNDO_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, "/"), poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, 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-00273" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00272/catalog-undo-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} poll=false operator=${desktop.operatorStatus} mainMutation=none desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00273") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00273-operator-asset.catalog_undo_push.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00273/catalog-undo-push-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-action-catalog-undo-push-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")); assert.equal(desktop.saveReopen, "EXACT"); assert.equal(desktop.poll, false); assert.equal(desktop.operatorStatus, "CANCELLED"); assert.equal(desktop.mainMutation, "NONE"); assert.deepEqual(desktop.after, desktop.before);
|
|
const engine = await factory({ wasmBinary }); const handle = engine._web_engine_create(); open(engine, handle, fs.readFileSync(fixture)); const before = snapshot(engine, handle); assert.equal(before.libraryStatus, "AVAILABLE"); assert.equal(before.libraries.length, 1); const library = before.libraries[0]; assert.equal(library.id, `library:${desktop.before.name}`); assert.equal(library.name, desktop.before.name); assert.equal(library.sourcePath, "//WebGapCatalogUndoPush.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(engine, handle, engine._web_engine_save_blend, true); const reopened = engine._web_engine_create(); open(engine, reopened, saved); const after = snapshot(engine, reopened); assert.deepEqual(after.libraries, before.libraries); assert.equal(after.libraryStatus, before.libraryStatus); 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).libraries, before.libraries); engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ASSET_CATALOG_UNDO_PUSH_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, "/"), poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, 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-00274" }; const reportPath = path.join(root, "tests/golden/M16-GAP-00273/catalog-undo-push-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} poll=false operator=${desktop.operatorStatus} mainMutation=none desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00274") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00274-operator-asset.catalogs_save.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00274/catalogs-save-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-action-catalogs-save-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")); assert.equal(desktop.saveReopen, "EXACT"); assert.equal(desktop.poll, false); assert.equal(desktop.operatorStatus, "CANCELLED"); assert.equal(desktop.mainMutation, "NONE"); assert.deepEqual(desktop.after, desktop.before);
|
|
const engine = await factory({ wasmBinary }); const handle = engine._web_engine_create(); open(engine, handle, fs.readFileSync(fixture)); const before = snapshot(engine, handle); assert.equal(before.libraryStatus, "AVAILABLE"); assert.equal(before.libraries.length, 1); const library = before.libraries[0];
|
|
assert.equal(library.id, `library:${desktop.before.name}`); assert.equal(library.name, desktop.before.name); assert.equal(library.sourcePath, "//WebGapCatalogsSave.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(engine, handle, engine._web_engine_save_blend, true); const reopened = engine._web_engine_create(); open(engine, reopened, saved); const after = snapshot(engine, reopened); assert.deepEqual(after.libraries, before.libraries); assert.equal(after.libraryStatus, before.libraryStatus); 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).libraries, before.libraries); engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ASSET_CATALOGS_SAVE_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, "/"), poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, 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-00275" }; const reportPath = path.join(root, "tests/golden/M16-GAP-00274/catalogs-save-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} poll=false operator=${desktop.operatorStatus} mainMutation=none desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00275") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00275-operator-asset.clear.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00275/asset-clear-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-action-asset-clear-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")); assert.equal(desktop.saveReopen, "EXACT"); assert.equal(desktop.poll, false); assert.equal(desktop.operatorStatus, "CANCELLED"); assert.equal(desktop.mainMutation, "NONE"); assert.deepEqual(desktop.after, desktop.before);
|
|
const engine = await factory({ wasmBinary }); const handle = engine._web_engine_create(); open(engine, handle, fs.readFileSync(fixture)); const before = snapshot(engine, handle); assert.equal(before.libraryStatus, "AVAILABLE"); assert.equal(before.libraries.length, 1); const library = before.libraries[0];
|
|
assert.equal(library.id, `library:${desktop.before.name}`); assert.equal(library.name, desktop.before.name); assert.equal(library.sourcePath, "//WebGapAssetClear.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(engine, handle, engine._web_engine_save_blend, true); const reopened = engine._web_engine_create(); open(engine, reopened, saved); const after = snapshot(engine, reopened); assert.deepEqual(after.libraries, before.libraries); assert.equal(after.libraryStatus, before.libraryStatus); 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).libraries, before.libraries); engine._web_engine_destroy(handle); engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ASSET_CLEAR_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, "/"), poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, 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-00276" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00275/asset-clear-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} poll=false operator=${desktop.operatorStatus} mainMutation=none desktop=exact saveReopen=exact next=${report.nextTask}\n`); process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00205") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00205-operator-anim.separate_slots.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00205/anim-separate-slots-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-separate-slots-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "SLOTS_SEPARATED");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.deepEqual(Object.values(desktop.after.objects).map((value) => value.name).sort(), ["WebGapSeparateSlotAAction", "WebGapSeparateSlotBAction"]);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const animations = before.animations?.filter((value) => value.targetId && value.targetId !== "unlinked");
|
|
assert.equal(animations?.length, 2);
|
|
assert.deepEqual(animations.map((value) => value.name).sort(), ["WebGapSeparateSlotAAction", "WebGapSeparateSlotBAction"]);
|
|
for (const animation of animations) {
|
|
assert.equal(animation.channels.length, 1);
|
|
assert.equal(animation.channels[0].path, '["separate_target"][0]');
|
|
assert.deepEqual(animation.channels[0].keyframes.map((value) => value.frame), [1, 3, 5]);
|
|
}
|
|
assert.equal(before.animations?.some((value) => value.name === "WebGapAnimSeparateSlotsAction"), false);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_SEPARATE_SLOTS_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", actionIds: animations.map((value) => value.id), actionNames: animations.map((value) => value.name).sort(), channelsPerAction: animations.map((value) => value.channels.length), oldActionEmpty: true }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00206" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00205/anim-separate-slots-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} actions=2 old-empty=true desktop=exact poll=true operator=${desktop.operatorStatus} saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00204") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00204-operator-anim.scene_range_frame.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00204/anim-scene-range-frame-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-scene-range-frame-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "VIEW_FRAMED_TO_SCENE_RANGE");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
|
|
const sameView = (left, right) => left?.cur?.xmin === right?.cur?.xmin && left?.cur?.xmax === right?.cur?.xmax && left?.cur?.ymin === right?.cur?.ymin && left?.cur?.ymax === right?.cur?.ymax && left?.mask?.xmin === right?.mask?.xmin && left?.mask?.xmax === right?.mask?.xmax && left?.mask?.ymin === right?.mask?.ymin && left?.mask?.ymax === right?.mask?.ymax;
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const actionRegion = before.editorWorkflow.workspaces.flatMap((workspace) => workspace.areas)
|
|
.filter((area) => area.editor === "DOPE_SHEET")
|
|
.flatMap((area) => area.regions)
|
|
.find((region) => region.kind === "MAIN" && sameView(region.view2d, desktop.view.view2d));
|
|
assert.ok(actionRegion?.view2d);
|
|
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).editorWorkflow, before.editorWorkflow);
|
|
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).editorWorkflow, before.editorWorkflow);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_SCENE_RANGE_FRAME_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", editor: "DOPE_SHEET", view2d: actionRegion.view2d }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00205" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00204/anim-scene-range-frame-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} editor=DOPE_SHEET view2d=exact poll=true operator=${desktop.operatorStatus} saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00203") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00203-operator-anim.replace_action_new.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00203/anim-replace-action-new-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-replace-action-new-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "ACTION_REPLACED_WITH_NEW");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.equal(desktop.newAction, "Action");
|
|
assert.deepEqual(Object.values(desktop.after.objects).map((value) => value.name), ["Action", "Action"]);
|
|
assert.deepEqual(Object.values(desktop.after.objects).map((value) => value.channels.length), [0, 0]);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const oldAnimation = before.animations?.find((value) => value.name === "WebGapAnimReplaceNewOldAction" && value.unlinked);
|
|
assert.ok(oldAnimation);
|
|
assert.equal(oldAnimation.channels.length, 2);
|
|
assert.deepEqual(oldAnimation.channels.map((channel) => channel.keyframes.map((value) => value.value[0])), [[1, 3, 5], [2, 4, 6]]);
|
|
assert.equal(before.animations?.some((value) => value.name === desktop.newAction), false);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_REPLACE_ACTION_NEW_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", replacedObjectIds: Object.keys(desktop.after.objects).map((name) => `object:${name}`), newAction: desktop.newAction, newActionChannels: 0, oldActionUnlinked: oldAnimation.id, oldActionChannels: oldAnimation.channels.length, emptyActionVisibility: "NOT_EXPOSED_BY_SCENE_SNAPSHOT" }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00204" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00203/anim-replace-action-new-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} actions=old->new-empty users=2 desktop=exact poll=true operator=${desktop.operatorStatus} saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00202") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00202-operator-anim.replace_action.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00202/anim-replace-action-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-replace-action-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "ACTIONS_REPLACED");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.equal(desktop.before.objects.WebGapAnimReplaceOldA.name, "WebGapAnimReplaceOldAction");
|
|
assert.equal(desktop.before.objects.WebGapAnimReplaceOldB.name, "WebGapAnimReplaceOldAction");
|
|
assert.deepEqual(Object.values(desktop.after.objects).map((value) => value.name), ["WebGapAnimReplaceNewAction", "WebGapAnimReplaceNewAction", "WebGapAnimReplaceNewAction"]);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const newAnimations = before.animations?.filter((value) => value.name === "WebGapAnimReplaceNewAction" && value.targetId !== "unlinked");
|
|
const oldAnimation = before.animations?.find((value) => value.name === "WebGapAnimReplaceOldAction" && value.unlinked);
|
|
assert.equal(newAnimations?.length, 3);
|
|
assert.ok(oldAnimation);
|
|
for (const animation of newAnimations) {
|
|
assert.equal(animation.channels.length, 1);
|
|
assert.equal(animation.channels[0].path, '["replace_target"][0]');
|
|
assert.deepEqual(animation.channels[0].keyframes.map((value) => value.frame), [1, 3, 5]);
|
|
assert.deepEqual(animation.channels[0].keyframes.map((value) => value.value[0]), [10, 30, 50]);
|
|
}
|
|
assert.deepEqual(oldAnimation.channels.map((channel) => channel.keyframes.map((value) => value.value[0])), [[1, 3, 5], [2, 4, 6]]);
|
|
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).animations, before.animations);
|
|
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).animations, before.animations);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_REPLACE_ACTION_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", replacedObjectIds: newAnimations.map((value) => value.targetId), newAction: "WebGapAnimReplaceNewAction", oldActionUnlinked: oldAnimation.id, channelsPerObject: newAnimations.map((value) => value.channels.length), oldActionChannels: oldAnimation.channels.length }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00203" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00202/anim-replace-action-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} actions=old->new users=3 desktop=exact poll=true operator=${desktop.operatorStatus} saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00201") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00201-operator-anim.previewrange_set.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00201/anim-previewrange-set-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-previewrange-set-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "PREVIEW_RANGE_SET");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.deepEqual(desktop.after, { use: true, start: 2, end: 6 });
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const scene = before.scenes?.find((value) => value.id === before.sceneId) ?? before.scenes?.[0];
|
|
assert.ok(scene);
|
|
assert.deepEqual(scene.previewRange, { start: 2, end: 6 });
|
|
assert.deepEqual(scene.sequencerTimeline, { fpsDenominator: 1000, fpsNumerator: 24000, frameEnd: 8, frameStart: 1, id: "sequencer:scene:Scene", revision: 1, schemaVersion: 1, strips: [] });
|
|
const saved = output(engine, handle, engine._web_engine_save_blend, true);
|
|
const reopened = engine._web_engine_create();
|
|
open(engine, reopened, saved);
|
|
const reopenedScene = snapshot(engine, reopened).scenes?.find((value) => value.id === scene.id);
|
|
assert.deepEqual(reopenedScene, scene);
|
|
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).scenes?.find((value) => value.id === scene.id), scene);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_PREVIEWRANGE_SET_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", sceneId: scene.id, previewRange: scene.previewRange, frameStart: scene.sequencerTimeline.frameStart, frameEnd: scene.sequencerTimeline.frameEnd }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00202" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00201/anim-previewrange-set-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} scene=${scene.id} preview=2-6 poll=true operator=${desktop.operatorStatus} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00200") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00200-operator-anim.previewrange_clear.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00200/anim-previewrange-clear-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-previewrange-clear-desktop.py");
|
|
const run = spawnSync(blender, ["-b", "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "PREVIEW_RANGE_CLEARED");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.deepEqual(desktop.after, { use: false, start: 0, end: 0 });
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const scene = before.scenes?.find((value) => value.id === before.sceneId) ?? before.scenes?.[0];
|
|
assert.ok(scene);
|
|
assert.equal(Object.hasOwn(scene, "previewRange"), false);
|
|
assert.deepEqual(scene.sequencerTimeline, { fpsDenominator: 1000, fpsNumerator: 24000, frameEnd: 8, frameStart: 1, id: "sequencer:scene:Scene", revision: 1, schemaVersion: 1, strips: [] });
|
|
const saved = output(engine, handle, engine._web_engine_save_blend, true);
|
|
const reopened = engine._web_engine_create();
|
|
open(engine, reopened, saved);
|
|
const reopenedScene = snapshot(engine, reopened).scenes?.find((value) => value.id === scene.id);
|
|
assert.deepEqual(reopenedScene, scene);
|
|
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).scenes?.find((value) => value.id === scene.id), scene);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_PREVIEWRANGE_CLEAR_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", sceneId: scene.id, previewRange: null, frameStart: scene.sequencerTimeline.frameStart, frameEnd: scene.sequencerTimeline.frameEnd }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00201" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00200/anim-previewrange-clear-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} scene=${scene.id} preview=cleared poll=true operator=${desktop.operatorStatus} desktop=exact saveReopen=exact next=${report.nextTask}\n`);
|
|
process.exit(0);
|
|
}
|
|
if (task === "M16-GAP-00199") {
|
|
const fixture = path.join(root, "tests/files/web/generated/M16-GAP-00199-operator-anim.paste_driver_button.blend");
|
|
const desktopPath = path.join(root, "tests/golden/M16-GAP-00199/anim-paste-driver-button-desktop-report.json");
|
|
fs.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
const blender = process.env.BLENDER_BIN ?? path.join(root, "build_blender_5.2.0/bin/blender");
|
|
const checker = path.join(root, "tools/web/check-action-paste-driver-button-desktop.py");
|
|
const run = spawnSync(process.env.XVFB_RUN_BIN ?? "xvfb-run", ["-a", "--server-args=-screen 0 1280x800x24", blender, "--factory-startup", "--python", checker, "--", fixture, desktopPath], { cwd: root, encoding: "utf8", maxBuffer: 8 * 1024 * 1024, timeout: 45_000 });
|
|
assert.equal(run.status, 0, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
assert.equal(fs.existsSync(desktopPath), true, `${run.stdout ?? ""}\n${run.stderr ?? ""}`);
|
|
const desktop = JSON.parse(fs.readFileSync(desktopPath, "utf8"));
|
|
assert.equal(desktop.saveReopen, "EXACT");
|
|
assert.equal(desktop.mainMutation, "DRIVER_PASTED");
|
|
assert.equal(desktop.poll, true);
|
|
assert.equal(desktop.operatorStatus, "FINISHED");
|
|
assert.deepEqual(desktop.after?.source?.drivers ?? desktop.source?.drivers, [{ path: '["source_target"]', index: 0, expression: "frame * 3.0 + 2.0", type: "SCRIPTED", variableCount: 0 }]);
|
|
assert.deepEqual(desktop.after?.target?.drivers ?? desktop.target?.drivers, [{ path: '["paste_target"]', index: 0, expression: "frame * 3.0 + 2.0", type: "SCRIPTED", variableCount: 0 }]);
|
|
|
|
const engine = await factory({ wasmBinary });
|
|
const handle = engine._web_engine_create();
|
|
open(engine, handle, fs.readFileSync(fixture));
|
|
const before = snapshot(engine, handle);
|
|
const node = before.nodes?.find((value) => value.id === "object:WebGapAnimPasteDriverButtonObject");
|
|
assert.ok(node);
|
|
const expectedDrivers = [
|
|
{ path: '["source_target"]', arrayIndex: 0, editable: true, enabled: true, expression: "frame * 3.0 + 2.0", type: "SCRIPTED", typeCode: 1, flags: 8, influence: 0, variables: [] },
|
|
{ path: '["paste_target"]', arrayIndex: 0, editable: true, enabled: true, expression: "frame * 3.0 + 2.0", type: "SCRIPTED", typeCode: 1, flags: 8, influence: 0, variables: [] },
|
|
];
|
|
assert.deepEqual(node.drivers, expectedDrivers);
|
|
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).nodes?.find((value) => value.id === node.id)?.drivers, node.drivers);
|
|
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).nodes?.find((value) => value.id === node.id)?.drivers, node.drivers);
|
|
engine._web_engine_destroy(handle);
|
|
engine._web_engine_destroy(reopened);
|
|
const report = { schemaVersion: 1, task, operation: "ANIM_PASTE_DRIVER_BUTTON_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, poll: desktop.poll, operatorStatus: desktop.operatorStatus, mainMutation: desktop.mainMutation }, wasm: { status: "EXACT", objectId: node.id, drivers: node.drivers }, saveReopen: "EXACT", negative: { malformedBlend: "REJECTED_WITHOUT_MAIN_MUTATION" }, nextTask: "M16-GAP-00200" };
|
|
const reportPath = path.join(root, "tests/golden/M16-GAP-00199/anim-paste-driver-button-local-exact-report.json");
|
|
fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
process.stdout.write(`generated-gap-ok task=${task} drivers=${node.drivers.length} poll=true operator=${desktop.operatorStatus} mainMutation=driver_pasted 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`);
|