153 lines
4.2 KiB
Go
153 lines
4.2 KiB
Go
package fejkdata
|
|
|
|
import (
|
|
"os"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
const shapePin = "testdata/shipped_shape.txt"
|
|
|
|
// REPIN=1 rewrites the pin.
|
|
func TestShippedShapeIsPinned(t *testing.T) {
|
|
f, err := New(WithSeed(1))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got := shippedShape(f)
|
|
if os.Getenv("REPIN") == "1" {
|
|
if err := os.WriteFile(shapePin, []byte(got), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return
|
|
}
|
|
want, err := os.ReadFile(shapePin)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got != string(want) {
|
|
t.Fatalf("the shipped shape differs from %s: a removed, renamed or retyped line is a breaking change; add the CHANGELOG.md entry, then repin with REPIN=1", shapePin)
|
|
}
|
|
}
|
|
|
|
func shippedShape(f *Generator) string {
|
|
facts := map[string]string{}
|
|
var walk func(prefix string, n node)
|
|
walk = func(prefix string, n node) {
|
|
switch n := n.(type) {
|
|
case *folder:
|
|
for _, name := range sortedNames(n.children) {
|
|
walk(join(prefix, name), n.children[name])
|
|
}
|
|
case *choice:
|
|
facts[prefix] = reads(n)
|
|
case *table:
|
|
facts[prefix] = "\tformat " + strconv.Quote(n.format.format) + tableFacts(n) + reads(n)
|
|
for _, name := range n.columns {
|
|
facts[join(prefix, name)] = "\tstring"
|
|
}
|
|
case *template:
|
|
facts[prefix] = "\tformat " + strconv.Quote(n.format) + reads(n)
|
|
if _, columns, err := recordOf(n); err == nil {
|
|
for _, c := range columns {
|
|
fact := "\t" + c.DataType.String()
|
|
if _, nullable := columnItems(n.fields[c.Name]); nullable {
|
|
fact += " null"
|
|
}
|
|
facts[join(prefix, c.Name)] = fact
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for _, name := range sortedNames(f.categories) {
|
|
walk(name, f.categories[name])
|
|
}
|
|
var b strings.Builder
|
|
for _, p := range f.List() {
|
|
b.WriteString(p + facts[p] + "\n")
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
// tableFacts names the columns a table's options read.
|
|
func tableFacts(t *table) string {
|
|
var b strings.Builder
|
|
for _, o := range []struct {
|
|
name string
|
|
col int
|
|
}{{"key", t.key}, {"name", t.name}, {"weight", t.weight}, {"parent", t.parent}} {
|
|
if o.col >= 0 {
|
|
b.WriteString("\t" + o.name + " " + t.columns[o.col])
|
|
}
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
// reads names the categories any template under n references, sorted.
|
|
func reads(n node) string {
|
|
set := map[string]bool{}
|
|
var collect func(node)
|
|
collect = func(n node) {
|
|
switch n := n.(type) {
|
|
case *choice:
|
|
for _, it := range n.items {
|
|
collect(it)
|
|
}
|
|
case *table:
|
|
collect(n.format)
|
|
for _, cell := range n.tokens {
|
|
collect(cell)
|
|
}
|
|
case *template:
|
|
for _, b := range n.refs {
|
|
set[strings.TrimPrefix(b.key, "/")] = true
|
|
}
|
|
for name, field := range n.fields {
|
|
if !isRef(name) {
|
|
collect(field)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
collect(n)
|
|
if len(set) == 0 {
|
|
return ""
|
|
}
|
|
keys := make([]string, 0, len(set))
|
|
for k := range set {
|
|
keys = append(keys, k)
|
|
}
|
|
sort.Strings(keys)
|
|
return "\treads " + strings.Join(keys, " ")
|
|
}
|
|
|
|
func TestShippedShapeNamesReads(t *testing.T) {
|
|
f := newGenerator(t, writeData(t, map[string]string{
|
|
"a": `{"format":"{x}","x":["{/b}",{"format":"{/c.v}","weight":2}]}`,
|
|
"b": `"y"`,
|
|
"c": `{"format":"{v} {n}","n":[null,{"format":"{int(1,9)}","datatype":"integer"}],"v":["z","w"]}`,
|
|
"d/pos": `["{.q}","{/b}"]`,
|
|
"d/q": `"r"`,
|
|
}))
|
|
want := "a\tformat \"{x}\"\treads b c\na.x\tstring\nb\tformat \"y\"\nc\tformat \"{v} {n}\"\nc.n\tinteger null\nc.v\tstring\nd.pos\treads b d.q\nd.q\tformat \"r\"\n"
|
|
if got := shippedShape(f); got != want {
|
|
t.Fatalf("shippedShape =\n%s\nwant\n%s", got, want)
|
|
}
|
|
}
|
|
|
|
func TestShippedShapeNamesTables(t *testing.T) {
|
|
f := newGenerator(t, writeFiles(t, map[string]string{
|
|
"w.json": `"x"`,
|
|
"r.json": `{"format":"{name}","rows":"r.tsv","key":"code","name":"name","weight":"w"}`,
|
|
"r.tsv": "code\tname\tw\n1\ta\t2\n2\tb\t3\n",
|
|
"m.json": `{"format":"{name} {/w}","rows":"m.tsv","key":"code","parent":"r"}`,
|
|
"m.tsv": "code\tname\tr\n10\tc\t1\n20\td\t2\n",
|
|
}))
|
|
want := "m\tformat \"{name} {/w}\"\tkey code\tparent r\treads w\nm.code\tstring\nm.name\tstring\nm.r\tstring\nr\tformat \"{name}\"\tkey code\tname name\tweight w\nr.code\tstring\nr.m\nr.m.code\nr.m.name\nr.m.r\nr.name\tstring\nr.w\tstring\nw\tformat \"x\"\n"
|
|
if got := shippedShape(f); got != want {
|
|
t.Fatalf("shippedShape =\n%s\nwant\n%s", got, want)
|
|
}
|
|
}
|