Files
Azornes-Comfyui-LayerForge/tests/test_stability_characterization.mjs

125 lines
3.6 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import {HistoryStack} from '../js/canvas/canvas_history.js';
import {ImageReferenceManager} from '../js/persistence/image_reference_manager.js';
function createReferenceCanvas(layers = []) {
return {
layers,
canvasState: {
layersUndoStack: [],
layersRedoStack: [],
},
imageCache: {
has: () => false,
delete: () => false,
},
};
}
test('history stack remains bounded during a large edit sequence', () => {
const history = new HistoryStack({
clone: value => ({ ...value }),
historyLimit: 64,
});
for (let index = 0; index < 5000; index += 1) {
assert.equal(history.push({ index }), true);
}
assert.equal(history.undoStack.length, 64);
assert.equal(history.undoStack[0].index, 4936);
assert.equal(history.undoStack.at(-1).index, 4999);
assert.equal(history.redoStack.length, 0);
});
test('reference manager rebuilds unique ids across current and historical layers', () => {
const currentLayers = Array.from({ length: 2000 }, (_, index) => ({
imageId: `current-${index % 100}`,
}));
const canvas = createReferenceCanvas(currentLayers);
canvas.canvasState.layersUndoStack = [[{ imageId: 'undo-only' }]];
canvas.canvasState.layersRedoStack = [[{ imageId: 'redo-only' }]];
const manager = new ImageReferenceManager(canvas);
const usedImageIds = manager.collectAllUsedImageIds();
assert.equal(usedImageIds.size, 102);
assert.equal(usedImageIds.has('undo-only'), true);
assert.equal(usedImageIds.has('redo-only'), true);
});
test('reference manager cleanup stops its timer and releases tracked state', () => {
const originalWindow = Object.getOwnPropertyDescriptor(globalThis, 'window');
const originalClearInterval = Object.getOwnPropertyDescriptor(globalThis, 'clearInterval');
const timers = [];
const clearedTimers = [];
Object.defineProperty(globalThis, 'window', {
configurable: true,
value: {
setInterval(callback, delay) {
const timer = { callback, delay };
timers.push(timer);
return timer;
},
clearInterval(timer) {
clearedTimers.push(timer);
},
},
});
Object.defineProperty(globalThis, 'clearInterval', {
configurable: true,
value: timer => clearedTimers.push(timer),
});
try {
const manager = new ImageReferenceManager(createReferenceCanvas());
manager.addReference('image-1');
manager.addReference('image-1');
manager.startGarbageCollection();
assert.equal(timers.length, 1);
assert.equal(manager.getStats().totalReferences, 2);
manager.destroy();
assert.deepEqual(clearedTimers, [timers[0]]);
assert.deepEqual(manager.getStats(), {
trackedImages: 0,
totalReferences: 0,
isRunning: false,
gcInterval: 5 * 60 * 1000,
maxAge: 30 * 60 * 1000,
});
} finally {
if (originalWindow) {
Object.defineProperty(globalThis, 'window', originalWindow);
} else {
delete globalThis.window;
}
if (originalClearInterval) {
Object.defineProperty(globalThis, 'clearInterval', originalClearInterval);
} else {
delete globalThis.clearInterval;
}
}
});
test('garbage collection ignores reentrant calls while one run is active', async () => {
const manager = new ImageReferenceManager(createReferenceCanvas());
let nestedRunCompleted = false;
manager.findUnusedImages = async () => {
await manager.performGarbageCollection();
nestedRunCompleted = true;
return [];
};
await manager.performGarbageCollection();
assert.equal(nestedRunCompleted, true);
assert.equal(manager.getStats().isRunning, false);
});