100 lines
3.5 KiB
TypeScript
100 lines
3.5 KiB
TypeScript
import { describe, it } from "@suaveplan/testing/runner";
|
|
import * as fc from "fast-check";
|
|
import { createCommand } from "../../src/command/command.js";
|
|
import { UndoStack } from "../../src/stack/stack.js";
|
|
|
|
interface Counter {
|
|
value: number;
|
|
}
|
|
|
|
describe("property: UndoStack invariants", () => {
|
|
it("push N, undo all, redo all: final state = after-push state", {
|
|
timeout: 30000,
|
|
}, () => {
|
|
fc.assert(
|
|
fc.property(
|
|
fc.array(fc.integer({ min: 1, max: 10 }), {
|
|
minLength: 1,
|
|
maxLength: 20,
|
|
}),
|
|
(deltas) => {
|
|
const s = new UndoStack<Counter>({ value: 0 });
|
|
for (const d of deltas) {
|
|
s.push(
|
|
createCommand(
|
|
"D",
|
|
(st) => ({ value: st.value + d }),
|
|
(st) => ({ value: st.value - d })
|
|
)
|
|
);
|
|
}
|
|
const afterPush = s.current.value;
|
|
for (let i = 0; i < deltas.length; i++) s.undo();
|
|
for (let i = 0; i < deltas.length; i++) s.redo();
|
|
return s.current.value === afterPush;
|
|
}
|
|
),
|
|
{ numRuns: 200 }
|
|
);
|
|
});
|
|
|
|
it("undoSize never exceeds maxEntries", { timeout: 30000 }, () => {
|
|
fc.assert(
|
|
fc.property(
|
|
fc.integer({ min: 1, max: 20 }),
|
|
fc.array(fc.integer({ min: 1, max: 5 }), {
|
|
minLength: 0,
|
|
maxLength: 50,
|
|
}),
|
|
(maxEntries, deltas) => {
|
|
const s = new UndoStack<Counter>(
|
|
{ value: 0 },
|
|
{ maxEntries }
|
|
);
|
|
for (const d of deltas) {
|
|
s.push(
|
|
createCommand(
|
|
"D",
|
|
(st) => ({ value: st.value + d }),
|
|
(st) => ({ value: st.value - d })
|
|
)
|
|
);
|
|
}
|
|
return s.undoSize <= maxEntries;
|
|
}
|
|
),
|
|
{ numRuns: 200 }
|
|
);
|
|
});
|
|
|
|
it("undoSize + redoSize = total pushed (within maxEntries limit)", {
|
|
timeout: 30000,
|
|
}, () => {
|
|
fc.assert(
|
|
fc.property(
|
|
fc.array(fc.integer({ min: 1, max: 5 }), {
|
|
minLength: 0,
|
|
maxLength: 15,
|
|
}),
|
|
fc.integer({ min: 0, max: 15 }),
|
|
(deltas, undoSteps) => {
|
|
const s = new UndoStack<Counter>({ value: 0 });
|
|
for (const d of deltas) {
|
|
s.push(
|
|
createCommand(
|
|
"D",
|
|
(st) => ({ value: st.value + d }),
|
|
(st) => ({ value: st.value - d })
|
|
)
|
|
);
|
|
}
|
|
const steps = Math.min(undoSteps, deltas.length);
|
|
for (let i = 0; i < steps; i++) s.undo();
|
|
return s.undoSize + s.redoSize === deltas.length;
|
|
}
|
|
),
|
|
{ numRuns: 200 }
|
|
);
|
|
});
|
|
});
|