66 lines
3.5 KiB
JavaScript
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 }));
|