Files
linuxcncWebWASM/web/worker/test_opfs_ui_state.mjs

66 lines
3.5 KiB
JavaScript

import assert from "node:assert/strict";
import { clearUiState, loadPowerOffCheckpoint, loadUiState, savePowerOffCheckpoint, saveUiState } from "./opfs-ui-state.mjs";
class FakeAccess {
constructor(file) { this.file = file; }
truncate(size) { this.file.bytes = this.file.bytes.slice(0, size); }
write(bytes, { at = 0 } = {}) { const next = new Uint8Array(Math.max(this.file.bytes.length, at + bytes.byteLength)); next.set(this.file.bytes); next.set(bytes, at); this.file.bytes = next; return bytes.byteLength; }
flush() {}
close() {}
}
class FakeFile {
constructor() { this.bytes = new Uint8Array(); }
async getFile() { const bytes = this.bytes; return { text: async () => new TextDecoder().decode(bytes) }; }
async createSyncAccessHandle() { return new FakeAccess(this); }
}
class FakeDirectory {
constructor() { this.directories = new Map(); this.files = new Map(); }
async getDirectoryHandle(name, { create = false } = {}) {
if (!this.directories.has(name)) {
if (!create) { const error = new Error("not found"); error.name = "NotFoundError"; throw error; }
this.directories.set(name, new FakeDirectory());
}
return this.directories.get(name);
}
async getFileHandle(name, { create = false } = {}) {
if (!this.files.has(name)) {
if (!create) { const error = new Error("not found"); error.name = "NotFoundError"; throw error; }
this.files.set(name, new FakeFile());
}
return this.files.get(name);
}
async removeEntry(name) { if (!this.files.delete(name) && !this.directories.delete(name)) { const error = new Error("not found"); error.name = "NotFoundError"; throw error; } }
}
class FakeStorage {
constructor() { this.root = new FakeDirectory(); }
async getDirectory() { return this.root; }
}
const storage = new FakeStorage();
const contextId = "linuxcnc-2.10.0-pre0-wasm-m1-py-c1";
const first = await saveUiState({ storage, contextId, key: "xyzac-trt", state: { view: "p", zoom: 1, program: { path: "demo.ngc", source: "G21\nM2\n" } } });
assert.equal(first.status, "verified");
assert.equal(first.storage, "opfs");
assert.equal((await loadUiState({ storage, contextId, key: "xyzac-trt" })).state.view, "p");
const second = await saveUiState({ storage, contextId, key: "xyzac-trt", state: { view: "z", zoom: 1.2 } });
assert.equal(second.generation, 2);
assert.equal((await loadUiState({ storage, contextId, key: "xyzac-trt" })).state.view, "z");
const root = storage.root.directories.get("linuxcnc-web-wasm");
const project = root.directories.get("ui-state").directories.get("xyzac-trt");
project.files.get("state-a.json").bytes = new TextEncoder().encode("{corrupt");
const fallback = await loadUiState({ storage, contextId, key: "xyzac-trt" });
assert.equal(fallback.status, "verified");
assert.equal(fallback.state.view, "p");
const checkpoint = { schema: "xyzac-power-off-checkpoint.v1", simulation_tick: 4, state: { program: { source_text: "G21\nM2\n" } }, state_hash: "test" };
assert.equal((await savePowerOffCheckpoint({ storage, contextId, checkpoint })).status, "verified");
assert.deepEqual((await loadPowerOffCheckpoint({ storage, contextId })).checkpoint, checkpoint);
assert.equal((await clearUiState({ storage, contextId, key: "xyzac-trt" })).status, "verified");
assert.equal((await loadUiState({ storage, contextId, key: "xyzac-trt" })).status, "empty");
console.log(JSON.stringify({ schema: "opfs-ui-state.v1", status: "verified", storage: "dedicated_worker_fake_opfs", fallback_after_corruption: true, power_off_checkpoint: true }));