Skip to content

Commit 91b0954

Browse files
author
MesTTo
committed
test(core): fast-check property tests (parser round-trip, alpha laws, matcher soundness)
1 parent 1b0c679 commit 91b0954

2 files changed

Lines changed: 79 additions & 5 deletions

File tree

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { describe, it } from "vitest";
2+
import fc from "fast-check";
3+
import { type Atom, sym, variable, expr, gint, atomEq, atomVars } from "./atom";
4+
import { parse, format } from "./parser";
5+
import { standardTokenizer } from "./runner";
6+
import { alphaEq } from "./alpha";
7+
import { matchAtoms } from "./match";
8+
import { instantiate } from "./instantiate";
9+
10+
// A safe symbol name: starts with a letter, not "True"/"False", not all-digits (those tokenize).
11+
const name = fc
12+
.stringMatching(/^[a-z][a-z0-9-]{0,6}$/)
13+
.filter((s) => s !== "True" && s !== "False");
14+
15+
const atomArb: fc.Arbitrary<Atom> = fc.letrec<{ atom: Atom }>((tie) => ({
16+
atom: fc.oneof(
17+
{ depthSize: "small", withCrossShrink: true },
18+
name.map(sym),
19+
name.map(variable),
20+
fc.integer({ min: -999, max: 999 }).map(gint),
21+
fc.array(tie("atom"), { maxLength: 3 }).map((xs) => expr(xs)),
22+
),
23+
})).atom;
24+
25+
const tk = standardTokenizer();
26+
27+
describe("properties (fast-check)", () => {
28+
it("parser round-trip: parse(format(a)) ≡ a", () => {
29+
fc.assert(
30+
fc.property(atomArb, (a) => {
31+
const r = parse(format(a), tk);
32+
return r !== undefined && atomEq(r, a);
33+
}),
34+
);
35+
});
36+
37+
it("alphaEq is reflexive and symmetric", () => {
38+
fc.assert(fc.property(atomArb, (a) => alphaEq(a, a)));
39+
fc.assert(fc.property(atomArb, atomArb, (a, b) => alphaEq(a, b) === alphaEq(b, a)));
40+
});
41+
42+
it("matcher soundness: a binding set instantiates the pattern to the ground target", () => {
43+
fc.assert(
44+
fc.property(atomArb, atomArb, (pattern, ground0) => {
45+
// Make the target ground by substituting any vars with a constant.
46+
const subst = (x: Atom): Atom =>
47+
x.kind === "var" ? sym("k") : x.kind === "expr" ? expr(x.items.map(subst)) : x;
48+
const ground = subst(ground0);
49+
for (const b of matchAtoms(pattern, ground)) {
50+
if (!atomEq(instantiate(b, pattern), ground)) return false;
51+
}
52+
return true;
53+
}),
54+
);
55+
});
56+
57+
it("a matched pattern with no extra vars resolves all its variables", () => {
58+
fc.assert(
59+
fc.property(atomArb, (ground0) => {
60+
const subst = (x: Atom): Atom =>
61+
x.kind === "var" ? sym("k") : x.kind === "expr" ? expr(x.items.map(subst)) : x;
62+
const ground = subst(ground0);
63+
// pattern = ground with one leaf turned into a fresh var still matches and resolves.
64+
const pat = expr([variable("p"), ground]);
65+
const tgt = expr([sym("anchor"), ground]);
66+
const res = matchAtoms(pat, tgt);
67+
if (res.length === 0) return true;
68+
return atomVars(instantiate(res[0]!, pat)).length === 0;
69+
}),
70+
);
71+
});
72+
});

packages/das-gateway/src/index.test.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@ describe("das-gateway wire format", () => {
88
const transport: GatewayTransport = {
99
query: () =>
1010
Promise.resolve({
11-
bindings: [
12-
[["s", "chimp"]],
13-
[["s", "monkey"]],
14-
],
11+
bindings: [[["s", "chimp"]], [["s", "monkey"]]],
1512
}),
1613
};
1714
const pattern = expr([sym("Similarity"), sym("human"), variable("s")]);
@@ -21,6 +18,11 @@ describe("das-gateway wire format", () => {
2118
const got = sols.map((b) => format(instantiate(b, variable("s"))));
2219
expect(got).toEqual(["chimp", "monkey"]);
2320
expect(decodeBindings({ bindings: [[["s", "chimp"]]] }).length).toBe(1);
24-
expect(atomEq(instantiate(decodeBindings({ bindings: [[["s", "chimp"]]] })[0]!, variable("s")), sym("chimp"))).toBe(true);
21+
expect(
22+
atomEq(
23+
instantiate(decodeBindings({ bindings: [[["s", "chimp"]]] })[0]!, variable("s")),
24+
sym("chimp"),
25+
),
26+
).toBe(true);
2527
});
2628
});

0 commit comments

Comments
 (0)