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:
@@ -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
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user