Accumulated uncommitted work: GHOST Web backend, WebGL/GPU web layer, web workflow engine and protocol, tooling, docs and task records. Large generated evidence, user-testing archives, build snapshots and imported test assets remain untracked. Co-Authored-By: Claude Code <noreply@anthropic.com>
78 lines
3.6 KiB
JavaScript
78 lines
3.6 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import createEngine from '../build_web_blender6/bin/web_engine.js';
|
|
|
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
const stages = [];
|
|
const engine = await createEngine({ wasmBinary: fs.readFileSync(path.join(root, 'build_web_blender6/bin/web_engine.wasm')) });
|
|
const handle = engine._web_engine_create();
|
|
const slots = engine._malloc(8);
|
|
const read = () => {
|
|
const pointer = engine.HEAPU32[slots / 4], length = engine.HEAPU32[slots / 4 + 1];
|
|
return JSON.parse(new TextDecoder().decode(engine.HEAPU8.slice(pointer, pointer + length)));
|
|
};
|
|
const snapshot = () => {
|
|
assert.equal(engine._web_engine_get_scene_snapshot(handle, slots, slots + 4), 0);
|
|
return read();
|
|
};
|
|
const apply = (sceneId, timeline, expected = true) => {
|
|
const start = performance.now();
|
|
const bytes = Buffer.from(JSON.stringify({ cmd: 'applyCommand', params: {
|
|
command: { type: 'setSequencerStrips', sceneId, timeline },
|
|
} }));
|
|
const pointer = engine._malloc(bytes.length);
|
|
try {
|
|
engine.HEAPU8.set(bytes, pointer);
|
|
assert.equal(engine._web_engine_call_json(handle, pointer, bytes.length, slots, slots + 4), 0);
|
|
const result = read();
|
|
assert.equal(result.ok, expected, result.error);
|
|
return result;
|
|
} finally {
|
|
engine._free(pointer);
|
|
stages.push({ stage: expected ? 'edit' : 'reject', seconds: (performance.now() - start) / 1000 });
|
|
}
|
|
};
|
|
try {
|
|
const start = performance.now();
|
|
const bytes = fs.readFileSync(path.join(root, 'tests/files/web/sequencer_scene_source.blend'));
|
|
const pointer = engine._malloc(bytes.length);
|
|
try {
|
|
engine.HEAPU8.set(bytes, pointer);
|
|
assert.equal(engine._web_engine_open_blend(handle, pointer, bytes.length), 0);
|
|
} finally { engine._free(pointer); }
|
|
stages.push({ stage: 'open', seconds: (performance.now() - start) / 1000 });
|
|
const initial = snapshot();
|
|
const scene = initial.scenes.find(scene => scene.name === 'SequencerScene');
|
|
const source = initial.scenes.find(scene => scene.name === 'SourceScene');
|
|
assert(scene && source);
|
|
const timeline = structuredClone(scene.sequencerTimeline);
|
|
timeline.strips.push({ id: 'strip:Web SCENE', name: 'Web SCENE', type: 'SCENE', sourceId: source.id,
|
|
frameStart: 1, frameEnd: 25, sourceStart: 0, sourceEnd: 24, channel: 5,
|
|
speed: 1, muted: false, locked: false });
|
|
apply(scene.id, timeline);
|
|
const accepted = snapshot();
|
|
const strips = accepted.scenes.find(item => item.id === scene.id).sequencerTimeline.strips;
|
|
assert.equal(strips.find(strip => strip.name === 'Web SCENE').sourceId, source.id);
|
|
assert.deepEqual(accepted.scenes.find(item => item.id === source.id), source);
|
|
for (const sourceId of [scene.id, 'scene:MissingLocalScene']) {
|
|
const invalid = structuredClone(timeline);
|
|
invalid.strips.at(-1).sourceId = sourceId;
|
|
const result = apply(scene.id, invalid, false);
|
|
assert.match(result.error, /own scene|missing scene/);
|
|
assert.deepEqual(snapshot().scenes, accepted.scenes);
|
|
}
|
|
apply(scene.id, scene.sequencerTimeline);
|
|
assert.deepEqual(snapshot().scenes.find(item => item.id === scene.id).sequencerTimeline.strips,
|
|
scene.sequencerTimeline.strips);
|
|
console.log(JSON.stringify({ status: 'PASS', stages }));
|
|
} catch (error) {
|
|
console.log(JSON.stringify({ status: 'FAIL', error: error.message, stages,
|
|
frames: String(error.stack).split('\n').filter(line => /wasm-function|wasm:/.test(line)).slice(0, 8) }));
|
|
process.exitCode = 1;
|
|
} finally {
|
|
engine._free(slots);
|
|
engine._web_engine_destroy(handle);
|
|
}
|