Test the family fence over replayed selectors, a name without a key, a name shadowing a key, a table reading its own family, an ancestor's column clash, a BOM header and five-deep pins

This commit is contained in:
2026-09-17 12:45:17 +02:00
parent 60c102336d
commit fb83aa6178
2 changed files with 108 additions and 45 deletions
+19
View File
@@ -96,6 +96,25 @@ func TestNoTableAllocRegression(t *testing.T) {
t.Errorf("%s: %.1f allocs/op regressed past %.1f (baseline %.1f + 10%%); a row index built per draw is the usual cause", s.name, allocs, s.base*1.10, s.base)
}
}
// Five linked tables, the depth a country's geo tree has: every pin must stay inline.
deep := fstest.MapFS{"addr.json": {Data: []byte(`"{/e.v} {/d.v} {/c.v} {/b.v} {/a.v}"`)}}
for i, name := range []string{"a", "b", "c", "d", "e"} {
rows, category := "k\tv\n1\tx\n2\ty\n", `{"format":"{v}","rows":"`+name+`.tsv","key":"k"}`
if i > 0 {
parent := string(rune('a' + i - 1))
rows = "k\tv\t" + parent + "\n1\tx\t1\n2\ty\t2\n"
category = `{"format":"{v}","rows":"` + name + `.tsv","key":"k","parent":"` + parent + `"}`
}
deep[name+".json"], deep[name+".tsv"] = &fstest.MapFile{Data: []byte(category)}, &fstest.MapFile{Data: []byte(rows)}
}
f, err = New(WithoutShippedData(), WithDataFS(deep))
if err != nil {
t.Fatal(err)
}
const base = 13.0
if allocs := testing.AllocsPerRun(10000, func() { f.Fake("addr") }); allocs > base*1.10 {
t.Errorf("five linked tables: %.1f allocs/op regressed past %.1f (baseline %.1f + 10%%); a pin spilling past the inline set is the usual cause", allocs, base*1.10, base)
}
}
// A record's fences read the compiled tree, so they belong to New, not to a draw.
+49 -5
View File
@@ -141,7 +141,7 @@ func TestTableCellsAreStringNodes(t *testing.T) {
func TestTableSelectsARowByKeyOrName(t *testing.T) {
files := with(geo(), map[string]string{
"city.json": `{"format":"{name}","rows":"city.tsv","key":"code","name":"name"}`,
"city.tsv": "code\tname\nSpringfield\tSt. Louis\nSTL\tSpringfield\n",
"city.tsv": "code\tname\nSTL\tSt. Louis\nSPI\tSpringfield\n",
})
f := newGenerator(t, writeFiles(t, files), WithSeed(1))
for path, want := range map[string]string{
@@ -150,8 +150,7 @@ func TestTableSelectsARowByKeyOrName(t *testing.T) {
"municipality[1281].locality[Sandby]": "Sandby",
"municipality[1281].locality[Sandby].code": "L7",
"municipality[0184].locality[Sandby].code": "L8",
"city[St. Louis].code": "Springfield",
"city[Springfield].name": "St. Louis", // a key wins over a name
"city[St. Louis].code": "STL",
} {
if got := fake(t, f, path); got != want {
t.Errorf("Fake(%q) = %q, want %q", path, got, want)
@@ -173,7 +172,7 @@ func TestTableSelectsARowByKeyOrName(t *testing.T) {
t.Fatalf("municipality[0180].region = %q, want the link column's cell", v)
}
noKey := writeFiles(t, map[string]string{"t.json": `{"format":"{a}","rows":"t.tsv"}`, "t.tsv": "a\nx\ny\n"})
if _, err := newGenerator(t, noKey).Fake("t[x]"); err == nil || !strings.Contains(err.Error(), "no key or name") {
if _, err := newGenerator(t, noKey).Fake("t[x]"); err == nil || !strings.Contains(err.Error(), "no key") {
t.Fatalf("Fake(t[x]) = %v, want no column to select by", err)
}
}
@@ -333,6 +332,42 @@ func TestTableSelectionFences(t *testing.T) {
}
}
// The family fence replays each read's selectors through the walk the render uses, so
// spellings that pin the same rows by different routes agree, and two rows of one
// table are refused at load rather than found at render.
func TestTableFamilyFenceReplaysPins(t *testing.T) {
accepted := map[string]string{
"skip-level selectors": `"{/region[12].locality[L4].name}|{/region[12].name}"`,
"descendant then ancestor": `"{/locality[L4].name}|{/region[12].name}"`,
"two selected levels": `"{/municipality[1281].name}|{/locality[L4].name}"`,
"nested prefixes": `"{/region[12].municipality[1281].name}|{/municipality[1281].locality[L4].name}"`,
"selected above a draw": `"{/region[12].locality[L4].name}|{/region[12].municipality.code}"`,
}
for name, json := range accepted {
f, err := New(WithoutShippedData(), WithDataPath(writeFiles(t, with(geo(), map[string]string{"x.json": json}))), WithSeed(1))
if err != nil {
t.Errorf("%s: New = %v, want it accepted", name, err)
continue
}
for i := 0; i < 50; i++ {
v := fake(t, f, "x")
if strings.Contains(v, "Sandby") || strings.Contains(v, "Malmö") || strings.Contains(v, "1280") || !strings.Contains(v, "Lund") && !strings.Contains(v, "Skåne") {
t.Fatalf("%s: x = %q, want every part inside region 12 and Lund", name, v)
}
}
}
rejected := map[string]struct{ json, want string }{
"two rows of one table": {`"{/region[12].locality[L4].name} {/region[12].locality[L7].name}"`, "drawGroup"},
"a row outside a selected ancestor": {`"{/region[14].name} {/locality[L4].name}"`, "not inside"},
}
for name, c := range rejected {
_, err := New(WithoutShippedData(), WithDataPath(writeFiles(t, with(geo(), map[string]string{"x.json": c.json}))))
if err == nil || !strings.Contains(err.Error(), c.want) {
t.Errorf("%s: New = %v, want it rejected mentioning %q", name, err, c.want)
}
}
}
func TestTableFences(t *testing.T) {
base := map[string]string{
"region.json": `{"format":"{name}","rows":"region.tsv","key":"code","name":"name"}`,
@@ -354,7 +389,12 @@ func TestTableFences(t *testing.T) {
"duplicate key": {map[string]string{"t.json": `{"format":"{a}","rows":"t.tsv","key":"a"}`, "t.tsv": "a\nx\nx\n"}, `"x"`},
"empty key": {map[string]string{"t.json": `{"format":"{a}","rows":"t.tsv","key":"a"}`, "t.tsv": "a\tb\n\ty\nx\tz\n"}, "empty"},
"a bracket in a key": {map[string]string{"t.json": `{"format":"{a}","rows":"t.tsv","key":"a"}`, "t.tsv": "a\nx[1]\ny\n"}, `"["`},
"a brace in a name": {map[string]string{"t.json": `{"format":"{a}","rows":"t.tsv","name":"a"}`, "t.tsv": "a\nx{1}\ny\n"}, `"{"`},
"a brace in a name": {map[string]string{"t.json": `{"format":"{a}","rows":"t.tsv","key":"a","name":"n"}`, "t.tsv": "a\tn\nx\tx{1}\ny\ty\n"}, `"{"`},
"name without a key": {map[string]string{"t.json": `{"format":"{a}","rows":"t.tsv","name":"a"}`, "t.tsv": "a\nx\nx\n"}, "key"},
"a name that is another row's key": {map[string]string{"t.json": `{"format":"{a}","rows":"t.tsv","key":"a","name":"n"}`, "t.tsv": "a\tn\nx\ty\ny\tz\n"}, `"y"`},
"a cell reading its family": {with(geo(), map[string]string{"locality.tsv": "code\tname\tmunicipality\nL1\t{/municipality.code}\t0180\nL2\tSolna\t0184\nL3\tMalmö\t1280\nL4\tLund\t1281\nL5\tGöteborg\t1480\n"}), "family"},
"a format reading its family": {with(geo(), map[string]string{"locality.json": `{"format":"{name} {/region.name}","rows":"locality.tsv","key":"code","name":"name","parent":"municipality"}`}), "family"},
"a descendant named like an ancestor's column": {with(geo(), map[string]string{"region.tsv": "code\tname\tpopulation\tlocality\n01\tStockholms län\t2400000\tx\n12\tSkåne län\t1400000\ty\n14\tVästra Götalands län\t1750000\tz\n"}), `"locality"`},
"weight not a number": {map[string]string{"t.json": `{"format":"{a}","rows":"t.tsv","weight":"w"}`, "t.tsv": "a\tw\nx\tmany\ny\t2\n"}, `"many"`},
"weight zero": {map[string]string{"t.json": `{"format":"{a}","rows":"t.tsv","weight":"w"}`, "t.tsv": "a\tw\nx\t0\ny\t2\n"}, "0"},
"weight negative": {map[string]string{"t.json": `{"format":"{a}","rows":"t.tsv","weight":"w"}`, "t.tsv": "a\tw\nx\t-1\ny\t2\n"}, "-1"},
@@ -401,6 +441,10 @@ func TestTableFences(t *testing.T) {
if _, err := inline.NewTemplate(`{"format":"{a}","rows":"t.tsv"}`); err == nil || !strings.Contains(err.Error(), "inline") {
t.Fatalf("NewTemplate(rows) = %v, want a table refused inline", err)
}
bom := newGenerator(t, writeFiles(t, map[string]string{"t.json": `{"format":"{a}","rows":"t.tsv","key":"a"}`, "t.tsv": "\xEF\xBB\xBFa\tb\nx\t1\ny\t2\n"}), WithSeed(1))
if v := fake(t, bom, "t[x].b"); v != "1" {
t.Fatalf("t[x].b under a BOM header = %q, want the mark stripped", v)
}
empty := newGenerator(t, writeFiles(t, map[string]string{"t.json": `{"format":"","rows":"t.tsv","key":"a"}`, "t.tsv": "a\tb\nx\t1\ny\t2\n"}), WithSeed(1))
if v := fake(t, empty, "t"); v != "" {
t.Fatalf("a record-only table renders %q, want \"\"", v)