Test that each spelling goal 5.7 no longer refuses loads as its shorter form
This commit is contained in:
+15
-12
@@ -398,8 +398,6 @@ func TestBuiltinCompileErrors(t *testing.T) {
|
||||
`"{float(Inf,Inf,2)}"`, // same-sign infinities
|
||||
`"{float(1,NaN,2)}"`, // one NaN bound
|
||||
`"{float(-Inf,1,2)}"`, // one infinite bound
|
||||
`"{digits(05)}"`, // no leading zero
|
||||
`"{int(+1,5)}"`, // a bound is a plain integer
|
||||
`"{int(5,5)}"`, // a constant is written as text
|
||||
`"{float(1,1,2)}"`, // a constant is written as text
|
||||
`"{iban(US)}"`, // unsupported country
|
||||
@@ -412,15 +410,20 @@ func TestBuiltinCompileErrors(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestArgErrorsNameTheSpelling(t *testing.T) {
|
||||
for src, want := range map[string]string{
|
||||
`"{float(1,2,02)}"`: "write 2",
|
||||
`{"format":"{calc(a,02)}","a":"1"}`: "write 2",
|
||||
`"{digits(+5)}"`: "write 5",
|
||||
`"{hex(99999999999999999999)}"`: "exceeds the maximum",
|
||||
`"{int(007,9)}"`: "write 7",
|
||||
} {
|
||||
if _, err := resolved(t, src); err == nil || !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("compile(%s) = %v, want an error saying %q", src, err, want)
|
||||
}
|
||||
if _, err := resolved(t, `"{hex(99999999999999999999)}"`); err == nil || !strings.Contains(err.Error(), "exceeds the maximum") {
|
||||
t.Errorf("compile(hex past the range) = %v, want the maximum named", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestASignOrLeadingZeroIsTheInteger(t *testing.T) {
|
||||
for long, short := range map[string]string{
|
||||
`"{float(1,2,02)}"`: `"{float(1,2,2)}"`,
|
||||
`{"format":"{calc(a,02)}","a":"1"}`: `{"format":"{calc(a,2)}","a":"1"}`,
|
||||
`"{digits(+5)}"`: `"{digits(5)}"`,
|
||||
`"{digits(05)}"`: `"{digits(5)}"`,
|
||||
`"{int(007,9)}"`: `"{int(7,9)}"`,
|
||||
`"{int(+1,5)}"`: `"{int(1,5)}"`,
|
||||
} {
|
||||
sameRenders(t, long, short)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -475,10 +475,20 @@ func TestRunInlineTemplate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunFormatTakesOnlyALoneReferenceAsARecord(t *testing.T) {
|
||||
for _, stdin := range []string{"{//sv_SE.person}", "{/sv_SE.person|/misc.uuid}", "{/sv_SE.person as p}", "{/}", "{/.sv_SE.person}"} {
|
||||
if code, out, errb := runOut(stdin, "--format", "json"); code != 2 || out != "" {
|
||||
t.Errorf("run(%q, --format json) = %d, %q, %q; want misuse", stdin, code, out, errb)
|
||||
}
|
||||
}
|
||||
if code, out, errb := runOut(`{"format":"x"}`); code != 0 || out != "x" {
|
||||
t.Errorf(`run({"format":"x"}) = %d, %q, %q; want x`, code, out, errb)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunTemplateMisuse(t *testing.T) {
|
||||
for stdin, want := range map[string]string{
|
||||
"{bad": "unterminated",
|
||||
`{"format":"x"}`: "is a string",
|
||||
"name: {/no.such.path}": "no entry",
|
||||
"a } b": "}}",
|
||||
"42": "number",
|
||||
|
||||
+15
-6
@@ -9,7 +9,7 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDatatypeAndNullSitOnlyInAColumn(t *testing.T) {
|
||||
func TestDatatypeSitsOnlyInAColumn(t *testing.T) {
|
||||
for _, src := range []string{
|
||||
`{"format":"","age":{"format":"{int(18,99)}","datatype":"integer"}}`,
|
||||
`{"format":"","n":{"format":"42","datatype":"integer"}}`,
|
||||
@@ -17,20 +17,20 @@ func TestDatatypeAndNullSitOnlyInAColumn(t *testing.T) {
|
||||
`{"format":"","middle":[null,"Ann","Eva"]}`,
|
||||
`{"format":"","age":[null,{"format":"{int(18,99)}","datatype":"integer","weight":9}]}`,
|
||||
`{"format":"","pick":[[null,"a"],"b"]}`,
|
||||
`{"format":"","n":{"format":"-0","datatype":"integer"}}`,
|
||||
`{"format":"","n":{"format":"-0.00","datatype":"number"}}`,
|
||||
} {
|
||||
if _, err := resolved(t, src); err != nil {
|
||||
t.Errorf("compile(%s) = %v, want a column to take a datatype and null", src, err)
|
||||
}
|
||||
}
|
||||
for src, want := range map[string]string{
|
||||
`{"format":"","n":{"format":"1","datatype":"int"}}`: `datatype takes "integer", "number" or "boolean", got "int"`,
|
||||
`{"format":"","n":{"format":"1","datatype":"int"}}`: `datatype takes "string", "integer", "number" or "boolean", got "int"`,
|
||||
`{"format":"","n":{"format":"1","datatype":1}}`: "datatype must be a string",
|
||||
`{"format":"{int(1,9)}","datatype":"integer"}`: "datatype only types a record column",
|
||||
`[{"format":"1","datatype":"integer"},"x"]`: "datatype only types a record column",
|
||||
`{"format":"{p}","p":{"format":"{n}","n":{"format":"1","datatype":"integer"}}}`: "datatype only types a record column",
|
||||
`{"format":"{n}","repeat":2,"n":{"format":"1","datatype":"integer"}}`: "datatype only types a record column",
|
||||
`null`: `so write ""`,
|
||||
`{"format":"{p}","p":{"format":"{x}","x":[null,"a"]}}`: `so write ""`,
|
||||
} {
|
||||
if _, err := resolved(t, src); err == nil || !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("compile(%s) = %v, want an error containing %q", src, err, want)
|
||||
@@ -89,8 +89,6 @@ func TestDatatypeRejectsAValueItsTypeRejects(t *testing.T) {
|
||||
{"an operand past the limit", `{"format":"{calc(a)}","a":"{digits(400)}","datatype":"number"}`, "is not proven within 1e300"},
|
||||
{"a whole calc past int64", `{"format":"{calc(a * 2)}","a":"{seq()}","datatype":"integer"}`, "{calc(a * 2)} is not proven within int64"},
|
||||
{"a whole float past int64", `{"format":"{float(0,1e19,0)}","datatype":"integer"}`, "{float(0,1e19,0)} is not proven within int64"},
|
||||
{"a signed zero integer", `{"format":"-0","datatype":"integer"}`, `"-0" is zero written with a sign; write "0"`},
|
||||
{"a signed zero number", `{"format":"-0.00","datatype":"number"}`, `"-0.00" is zero written with a sign; write "0.00"`},
|
||||
} {
|
||||
row := `{"format":"","col":` + c.column + `}`
|
||||
files := map[string]string{"row": row}
|
||||
@@ -224,3 +222,14 @@ func TestNullColumn(t *testing.T) {
|
||||
func TestDisagreementRefusesTwoTextItems(t *testing.T) {
|
||||
mustPanic(t, "two text items", func() { _ = disagreement(&template{}, DataTypeString, &template{}, DataTypeString) })
|
||||
}
|
||||
|
||||
func TestASignedZeroIsZero(t *testing.T) {
|
||||
f := newGenerator(t, writeData(t, map[string]string{"row": `{"format":"","i":{"format":"-0","datatype":"integer"},"n":{"format":"-0.00","datatype":"number"}}`}))
|
||||
r, err := f.FakeRecord("row")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if c := r.Columns(); c[0].Value != "-0" || c[1].Value != "-0.00" {
|
||||
t.Errorf("columns = %+v, want each written as its format", c)
|
||||
}
|
||||
}
|
||||
|
||||
+16
-16
@@ -107,7 +107,6 @@ func TestFakeTemplateErrors(t *testing.T) {
|
||||
{`"{digits(0)}"`, "must be positive"},
|
||||
{`name: {/no.such.path}`, "no entry"},
|
||||
{`name: {..nope}`, "write {/nope}"},
|
||||
{`{"format":"x"}`, "is a string"},
|
||||
} {
|
||||
_, err := f.FakeTemplate(c.input)
|
||||
if err == nil || !strings.Contains(err.Error(), c.want) {
|
||||
@@ -124,12 +123,15 @@ func TestFakeTemplateJSONString(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPaddedJSONIsRejected(t *testing.T) {
|
||||
func TestPaddedJSONIsTheJSON(t *testing.T) {
|
||||
f := shipped(t)
|
||||
in := `{"format":"{x}","x":["a","b"]}`
|
||||
_, err := f.NewTemplate(" " + in + " ")
|
||||
if err == nil || !strings.Contains(err.Error(), "write "+in) {
|
||||
t.Fatalf("NewTemplate(padded JSON) = %v, want an error naming the unpadded spelling", err)
|
||||
padded, err := f.NewTemplate(" \t" + in + "\n ")
|
||||
if err != nil {
|
||||
t.Fatalf("NewTemplate(padded JSON) = %v, want the JSON it pads", err)
|
||||
}
|
||||
if got := padded.Fake(); got != "a" && got != "b" {
|
||||
t.Fatalf("padded JSON rendered %q, want a or b with no padding", got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,6 +167,15 @@ func TestIsTemplate(t *testing.T) {
|
||||
`"{/a} x"`: true,
|
||||
"x[1]": false, // a row selected by key or name
|
||||
"x[St. Louis].y": false,
|
||||
"{/sv_SE.person.last}": true,
|
||||
`"{/sv_SE.person}"`: true,
|
||||
"{.person.last}": true,
|
||||
` "{/sv_SE.person}"`: true,
|
||||
`{"format":"{/sv_SE.person}"}`: true,
|
||||
"{//sv_SE.person}": true,
|
||||
"{/misc.currency[US Dollar (Next day)]}": true,
|
||||
"/sv_SE.person": false,
|
||||
"//sv_SE.person": false,
|
||||
} {
|
||||
if got, err := IsTemplate(arg); err != nil || got != want {
|
||||
t.Errorf("IsTemplate(%q) = %v, %v; want %v", arg, got, err, want)
|
||||
@@ -180,17 +191,6 @@ func TestIsTemplate(t *testing.T) {
|
||||
"a}b": `holds a "}"`,
|
||||
`"abc`: `holds a "\""`,
|
||||
`"a]b`: `holds a "\""`, // the opener the reader typed, not the bracket behind it
|
||||
"{/sv_SE.person.last}": "{/sv_SE.person.last} is the path sv_SE.person.last written as a template; write sv_SE.person.last",
|
||||
`"{/sv_SE.person}"`: "write sv_SE.person",
|
||||
"{.person.last}": "write person.last",
|
||||
"/sv_SE.person": "write sv_SE.person",
|
||||
` "{/sv_SE.person}"`: "write sv_SE.person",
|
||||
`{"format":"{/sv_SE.person}"}`: "write sv_SE.person",
|
||||
"//sv_SE.person": "write sv_SE.person",
|
||||
"{//sv_SE.person}": "{//sv_SE.person} is the path sv_SE.person written as a template; write sv_SE.person",
|
||||
"/sv_SE/person": `path "sv_SE/person" contains "/"`,
|
||||
"{/-}": `path "-" is reserved`,
|
||||
"{/misc.currency[US Dollar (Next day)]}": "write misc.currency[US Dollar (Next day)]",
|
||||
} {
|
||||
if _, err := IsTemplate(arg); err == nil || !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("IsTemplate(%q) = %v; want it rejected naming %s", arg, err, want)
|
||||
|
||||
@@ -17,7 +17,7 @@ func TestLiteralSaysWhichDatatypesItsTextIs(t *testing.T) {
|
||||
{"1.5", [datatype.Count]bool{datatype.Integer: true, datatype.Boolean: true}},
|
||||
{"true", [datatype.Count]bool{datatype.Integer: true, datatype.Number: true}},
|
||||
{"007", [datatype.Count]bool{datatype.Integer: true, datatype.Number: true, datatype.Boolean: true}},
|
||||
{"-0", [datatype.Count]bool{datatype.Integer: true, datatype.Number: true, datatype.Boolean: true}},
|
||||
{"-0", [datatype.Count]bool{datatype.Boolean: true}},
|
||||
} {
|
||||
v := Literal(c.text)
|
||||
for d := datatype.Integer; d < datatype.Count; d++ {
|
||||
@@ -26,9 +26,6 @@ func TestLiteralSaysWhichDatatypesItsTextIs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if v := Literal("-0"); v.Not[datatype.Integer] != `"-0" is zero written with a sign; write "0"` {
|
||||
t.Errorf("Literal(-0) = %q, want the unsigned spelling named", v.Not[datatype.Integer])
|
||||
}
|
||||
}
|
||||
|
||||
func TestOrKeepsTheBoundsOfBothAndTheFirstReason(t *testing.T) {
|
||||
|
||||
@@ -34,10 +34,9 @@ func TestParseRefuses(t *testing.T) {
|
||||
want string
|
||||
}{
|
||||
{"no header", "", Options{}, "no header line"},
|
||||
{"one row", "a\n1\n", Options{}, "has one row"},
|
||||
{"no rows", "a\n", Options{}, "has no rows below its header"},
|
||||
{"a short row", "a\tb\n1\t2\n3\n", Options{}, "line 3 has fewer cells"},
|
||||
{"a repeated key", "k\nx\nx\n", Options{Key: "k"}, `key "x" repeats line 2`},
|
||||
{"the key named as name", "k\nx\ny\n", Options{Key: "k", Name: "k"}, "name names the key column"},
|
||||
{"a weight of zero", "k\tw\nx\t1\ny\t0\n", Options{Weight: "w"}, `weight "0" is not a positive number`},
|
||||
{"a key a selector cannot spell", "k\nx\ny|z\n", Options{Key: "k"}, `contains "|"`},
|
||||
{"an option naming no column", "k\nx\ny\n", Options{Key: "nope"}, `key names no column "nope"`},
|
||||
@@ -48,6 +47,16 @@ func TestParseRefuses(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAccepts(t *testing.T) {
|
||||
if tb := parse(t, "t", "a\n1\n", Options{}); tb.Len() != 1 {
|
||||
t.Errorf("one row: Len = %d, want 1", tb.Len())
|
||||
}
|
||||
tb := parse(t, "t", "k\nx\ny\n", Options{Key: "k", Name: "k"})
|
||||
if r, err := tb.find("y", nil); err != nil || r != 1 {
|
||||
t.Errorf("the key named as name: find(y) = %d, %v, want row 1", r, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLinkRefusesAParentRowNoChildLinksTo(t *testing.T) {
|
||||
country := parse(t, "country", "code\nSE\nNO\n", Options{Key: "code"})
|
||||
city := parse(t, "city", "country\tname\nSE\tLund\nSE\tBorg\n", Options{Parent: "country"})
|
||||
|
||||
+6
-6
@@ -187,7 +187,7 @@ func TestNameErrors(t *testing.T) {
|
||||
{"no reference or field", map[string]string{"card": `{"format":"{word as w}{w}{w}","words":["a","b"]}`},
|
||||
`no field "word"; a binding names a field, or a reference`},
|
||||
{"read once through a field", map[string]string{"card": `{"format":"{place as p}{p.x}","place":{"format":"{x}","x":["a","b"]}}`},
|
||||
`name "p" is read once, so it keeps no pick for another read; write {place.x} where it is read`},
|
||||
""},
|
||||
{"read once through a path that does not resolve", map[string]string{"word": `{"format":"{w}","w":["a","b"]}`, "card": `"{/word as a}{a.zz}"`},
|
||||
`no field "zz"`},
|
||||
{"read once through a field path that does not resolve", map[string]string{"card": `{"format":"{place as p}{p.zz}","place":{"format":"{x}","x":["a","b"]}}`},
|
||||
@@ -199,11 +199,11 @@ func TestNameErrors(t *testing.T) {
|
||||
{"a misspelt name", map[string]string{"word": `["a","b"]`, "card": `"{/word as p}{p}{p}{pp}"`},
|
||||
`no field or name "pp"; the names bound here are "p"`},
|
||||
{"read once through a path", map[string]string{"word": `{"format":"{w}","w":["a","b"]}`, "card": `"{/word as a}{a.w} {/word.w}"`},
|
||||
`write {/word.w} where it is read`},
|
||||
""},
|
||||
{"read once whole, bound to a reference path", map[string]string{"word": `{"format":"{w}","w":["a","b"]}`, "card": `"{/word.w as c}{c} {/word.w}"`},
|
||||
`write {/word.w} where it is read`},
|
||||
""},
|
||||
{"read once through a step up", map[string]string{"region.json": nameTables()["region.json"], "region.tsv": nameTables()["region.tsv"], "municipality.json": nameTables()["municipality.json"], "municipality.tsv": nameTables()["municipality.tsv"], "card": `"{/municipality as m}{m..region.name}"`},
|
||||
`write {/municipality..region.name} where it is read`},
|
||||
""},
|
||||
{"read once as a calc operand", map[string]string{"word": `["1","2"]`, "card": `"{/word as w}{calc(w * 2)}"`},
|
||||
""},
|
||||
{"read outside a repeat in the root choice", map[string]string{"word": `["a","b"]`, "card": `[{"format":"{/word as p}{p}{p}","repeat":2},"{p}"]`},
|
||||
@@ -211,9 +211,9 @@ func TestNameErrors(t *testing.T) {
|
||||
{"an empty name", map[string]string{"word": `["a","b"]`, "card": `"{/word as }"`},
|
||||
"a binding names nothing"},
|
||||
{"read once whole", map[string]string{"word": `["a","b"]`, "card": `{"format":"{/word as w}{x}","x":"{w}"}`},
|
||||
`write {/word} where it is read`},
|
||||
""},
|
||||
{"read once whole, as a transform's operand", map[string]string{"word": `["a","b"]`, "card": `"{/word as w}{uppercase(w)}"`},
|
||||
`write /word where it is read`},
|
||||
""},
|
||||
{"rendered twice where a path reads it", map[string]string{"word": `{"format":"{w}-{w}","w":["a","b"]}`, "card": `"{/word as n}{n}|{n.w}"`},
|
||||
`{n} renders field "w" twice`},
|
||||
{"a binding in a cell", map[string]string{"word": `["a","b"]`, "t.json": `{"format":"{k}","rows":"t.tsv","key":"k"}`, "t.tsv": "k\tc\nx\t{/word as w}{w}{w}\ny\tz\n"},
|
||||
|
||||
@@ -59,11 +59,7 @@ func TestNodeCompileErrors(t *testing.T) {
|
||||
`[1, 2]`, // a choice of numbers
|
||||
`5`, // unsupported node type
|
||||
`[]`, // empty choice
|
||||
`["x"]`, // a one-item choice is its item
|
||||
`["a","a"]`, // a repeated item is a weight
|
||||
`{"format":"x"}`, // an object holding only a format is a string
|
||||
`[{"format":"a","weight":1},"b"]`, // weight 1 is the default
|
||||
`{"format":"{x}","x":"v","repeat":1}`, // repeat 1 is the default
|
||||
`[{"format":"A","weight":-1},"B"]`, // negative weight
|
||||
`[{"format":"A","weight":0},{"format":"B","weight":0}]`, // weights sum to zero
|
||||
`[{"format":"A","weight":1e308},{"format":"B","weight":1e308}]`, // weights overflow to +Inf
|
||||
@@ -73,7 +69,6 @@ func TestNodeCompileErrors(t *testing.T) {
|
||||
`{"format":"x","repeat":1.5}`, // non-integer repeat
|
||||
`{"format":"x","repeat":"two"}`, // non-numeric repeat
|
||||
`{"format":"x","repeat":2,"separator":5}`, // non-string separator
|
||||
`{"format":"{x}","x":"v","repeat":2,"separator":""}`, // separator "" is the default
|
||||
} {
|
||||
if _, err := resolved(t, bad); err == nil {
|
||||
t.Errorf("compile(%s) = nil error, want error", bad)
|
||||
|
||||
+38
-13
@@ -5,14 +5,32 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestOneItemChoiceIsRejected(t *testing.T) {
|
||||
for _, src := range []string{`["x"]`, `[{"format":"{d}","d":"x"}]`, `{"format":"{w}","w":["only"]}`} {
|
||||
if _, err := resolved(t, src); err == nil || !strings.Contains(err.Error(), "one-item choice") {
|
||||
t.Errorf("compile(%s) = %v, want the one-item choice rejected", src, err)
|
||||
// sameRenders holds long to render as short does, draw for draw under one seed.
|
||||
func sameRenders(t *testing.T, long, short string) {
|
||||
t.Helper()
|
||||
l, s := compiled(t, long), compiled(t, short)
|
||||
fl, fs := engine(1), engine(1)
|
||||
for i := 0; i < 20; i++ {
|
||||
if a, b := renderOnce(fl.drawState, l), renderOnce(fs.drawState, s); a != b {
|
||||
t.Fatalf("%s rendered %q where %s rendered %q", long, a, short, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestOneItemChoiceIsItsItem(t *testing.T) {
|
||||
for long, short := range map[string]string{
|
||||
`["x"]`: `"x"`,
|
||||
`[{"format":"{d}","d":["x","y"]}]`: `{"format":"{d}","d":["x","y"]}`,
|
||||
`{"format":"{w}","w":["only"]}`: `{"format":"{w}","w":"only"}`,
|
||||
`{"format":"{w}","w":[["a","b"]]}`: `{"format":"{w}","w":["a","b"]}`,
|
||||
} {
|
||||
sameRenders(t, long, short)
|
||||
}
|
||||
if r, isTemplate := compiled(t, `[{"format":"","d":"x"}]`).(*template); !isTemplate || !r.isRecord {
|
||||
t.Error("a one-item choice of a record is no record, want the record it holds")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRepeatedChoiceItemIsRejected(t *testing.T) {
|
||||
for src, want := range map[string]string{
|
||||
`["a", "a", "b"]`: `{ "format": "a", "weight": 2 }`,
|
||||
@@ -31,22 +49,29 @@ func TestRepeatedChoiceItemIsRejected(t *testing.T) {
|
||||
|
||||
func TestInertObjectIsRejected(t *testing.T) {
|
||||
for src, want := range map[string]string{
|
||||
`{"format":"Malmö"}`: `write "Malmö"`,
|
||||
`{"format":"{digits(3)}"}`: `write "{digits(3)}"`,
|
||||
`[{"format":"a","weight":1},"b"]`: "weight 1",
|
||||
`{"format":"{x}","x":"v","repeat":1}`: "repeat 1",
|
||||
`{"format":"{x}","x":"v","separator":","}`: "separator",
|
||||
`{"format":"{x}","x":"v","repeat":2,"separator":""}`: "default",
|
||||
`{"format":"","n":{"format":"1","datatype":"string"}}`: `datatype "string" is the default`,
|
||||
} {
|
||||
if _, err := resolved(t, src); err == nil || !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("compile(%s) = %v, want an error mentioning %s", src, err, want)
|
||||
}
|
||||
}
|
||||
for _, ok := range []string{`[{"format":"a","weight":2},"b"]`, `{"format":"ab","repeat":2}`, `{"format":"{x}","x":"v"}`, `"{digits(3)}"`} {
|
||||
if _, err := compile(parse(t, ok)); err != nil {
|
||||
t.Errorf("compile(%s) = %v", ok, err)
|
||||
}
|
||||
|
||||
func TestADefaultWrittenOutIsTheDefault(t *testing.T) {
|
||||
for long, short := range map[string]string{
|
||||
`{"format":"Malmö"}`: `"Malmö"`,
|
||||
`{"format":"{digits(3)}"}`: `"{digits(3)}"`,
|
||||
`[{"format":"a","weight":1},"b"]`: `["a","b"]`,
|
||||
`{"format":"{x}","x":["a","b"],"repeat":1}`: `{"format":"{x}","x":["a","b"]}`,
|
||||
`{"format":"{x}","x":["a","b"],"repeat":2,"separator":""}`: `{"format":"{x}","x":["a","b"],"repeat":2}`,
|
||||
`{"format":"","n":{"format":"{x}","x":["1","2"],"datatype":"string"}}`: `{"format":"","n":{"format":"{x}","x":["1","2"]}}`,
|
||||
`null`: `""`,
|
||||
`{"format":"{p}","p":{"format":"{x}","x":[null,"a"]}}`: `{"format":"{p}","p":{"format":"{x}","x":["","a"]}}`,
|
||||
} {
|
||||
sameRenders(t, long, short)
|
||||
}
|
||||
if r := compiled(t, `{"format":"","n":{"format":"1","datatype":"string"}}`).(*template); !r.isRecord || r.fields["n"].(*template).datatype != DataTypeString {
|
||||
t.Error("a column of datatype string is no string column of a record")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -184,3 +184,16 @@ func TestFakePathNavigation(t *testing.T) {
|
||||
t.Error("Fake(missing) = nil error, want unknown-category error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestALeadingSlashIsThePath(t *testing.T) {
|
||||
f := newGenerator(t, writeData(t, map[string]string{"person": `{"format":"{first}","first":"Ada"}`}), WithSeed(1))
|
||||
for _, path := range []string{"/person", "//person.first"} {
|
||||
if v := fake(t, f, path); v != "Ada" {
|
||||
t.Errorf("Fake(%s) = %q, want Ada", path, v)
|
||||
}
|
||||
}
|
||||
r, err := f.FakeRecord("/person")
|
||||
if err != nil || len(r.Columns()) != 1 || r.Columns()[0].Value != "Ada" {
|
||||
t.Errorf("FakeRecord(/person) = %+v, %v, want the record person", r, err)
|
||||
}
|
||||
}
|
||||
|
||||
+13
-4
@@ -118,10 +118,6 @@ func TestANamedFieldInARepeatPicksEachLine(t *testing.T) {
|
||||
func TestAFieldBindingIsRefusedWhereNoFieldIs(t *testing.T) {
|
||||
for src, want := range map[string]string{
|
||||
`{"format":"{nope as p}{p}{p}","place":"x"}`: `no field "nope"`,
|
||||
`{"format":"{place as p}{p.x}","place":{"format":"{x}","x":["a","b"]}}`: `write {place.x} where it is read`,
|
||||
`{"format":"{place as p}{p}","place":["x","y"]}`: `write {place} where it is read`,
|
||||
`{"format":"{n}|{x}","x":{"format":"{a as n}{a}","a":["1","2"]}}`: `write {x.a} where it is read`,
|
||||
`{"format":"{place as p}{calc(p * 2)}","place":["1","2"]}`: `write place where it is read`,
|
||||
`{"format":"{place as place}{place}{place}","place":["x","y"]}`: `name "place" is a field of the root template too`,
|
||||
} {
|
||||
if _, err := resolved(t, src); err == nil || !strings.Contains(err.Error(), want) {
|
||||
@@ -130,6 +126,19 @@ func TestAFieldBindingIsRefusedWhereNoFieldIs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestANameReadOnceIsWhatItBinds(t *testing.T) {
|
||||
for long, short := range map[string]string{
|
||||
`{"format":"{place as p}{p.x}","place":{"format":"{x}","x":["a","b"]}}`: `{"format":"{place.x}","place":{"format":"{x}","x":["a","b"]}}`,
|
||||
`{"format":"{place as p}{p}","place":["x","y"]}`: `{"format":"{place}","place":["x","y"]}`,
|
||||
`{"format":"{place as p}{calc(p * 2)}","place":["1","2"]}`: `{"format":"{calc(place * 2)}","place":["1","2"]}`,
|
||||
} {
|
||||
sameRenders(t, long, short)
|
||||
}
|
||||
if _, err := resolved(t, `{"format":"{n}|{x}","x":{"format":"{a as n}{a}","a":["1","2"]}}`); err != nil {
|
||||
t.Errorf("a name bound in a field and read once above it = %v, want it loaded", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCycleReachedOnlyByAPathTokenIsRejected(t *testing.T) {
|
||||
// A path token renders what it lands on, not the level it started from, so the
|
||||
// cycle walk has to follow it there.
|
||||
|
||||
+21
-12
@@ -222,6 +222,27 @@ func TestFakeStructLeavesAPointerBackAlone(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestFakeStructReadsAPathInEachSpelling(t *testing.T) {
|
||||
v := struct {
|
||||
Path string `fake:"person.first"`
|
||||
Ref string `fake:"{/person.first}"`
|
||||
Quoted string `fake:"\"{/person.first}\""`
|
||||
Slash string `fake:"/person.first"`
|
||||
Kept string `fake:"-"`
|
||||
}{Kept: "kept"}
|
||||
if err := structData(t).FakeStruct(&v); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for name, got := range map[string]string{"Path": v.Path, "Ref": v.Ref, "Quoted": v.Quoted, "Slash": v.Slash} {
|
||||
if got != "Ada" && got != "Bo" {
|
||||
t.Errorf("%s = %q, want a first name", name, got)
|
||||
}
|
||||
}
|
||||
if v.Kept != "kept" {
|
||||
t.Errorf(`Kept = %q, want fake:"-" to leave it as it was`, v.Kept)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFakeStructErrors(t *testing.T) {
|
||||
f := structData(t)
|
||||
for _, c := range []struct {
|
||||
@@ -287,12 +308,6 @@ func TestFakeStructErrors(t *testing.T) {
|
||||
{&struct {
|
||||
A string `fake:"nope.x"`
|
||||
}{}, `no entry "nope"`},
|
||||
{&struct {
|
||||
A string `fake:"{/person.first}"`
|
||||
}{}, "is the path person.first written as a template; write person.first"},
|
||||
{&struct {
|
||||
A string `fake:"\"{/person.first}\""`
|
||||
}{}, "is the path person.first written as a template; write person.first"},
|
||||
{&struct {
|
||||
A string `fake:"a|b"`
|
||||
}{}, `contains "|"`},
|
||||
@@ -305,12 +320,6 @@ func TestFakeStructErrors(t *testing.T) {
|
||||
{&struct {
|
||||
A string `fake:"{.person.first} x"`
|
||||
}{}, "write {/person.first}"},
|
||||
{&struct {
|
||||
A string `fake:"/person.first"`
|
||||
}{}, "write person.first"},
|
||||
{&struct {
|
||||
A string `fake:"-"`
|
||||
}{}, `fake:"-" leaves a struct field unfilled`},
|
||||
{&struct{ *structGiven }{}, "an unexported embedded pointer field cannot be set"},
|
||||
{&struct {
|
||||
structGiven
|
||||
|
||||
+19
-27
@@ -518,6 +518,19 @@ func TestSelectedRowsAgreeAcrossSpellings(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestATableOfOneRowOrNamedByItsKeyLoads(t *testing.T) {
|
||||
f := newGenerator(t, writeFiles(t, map[string]string{
|
||||
"one.json": `{"format":"{a}","rows":"one.tsv"}`, "one.tsv": "a\nx\n",
|
||||
"kn.json": `{"format":"{a}","rows":"kn.tsv","key":"a","name":"a"}`, "kn.tsv": "a\nx\ny\n",
|
||||
}), WithSeed(1))
|
||||
if v := fake(t, f, "one"); v != "x" {
|
||||
t.Errorf("one = %q, want its one row", v)
|
||||
}
|
||||
if v := fake(t, f, "kn[y]"); v != "y" {
|
||||
t.Errorf("kn[y] = %q, want the row keyed y", v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableFences(t *testing.T) {
|
||||
base := map[string]string{
|
||||
"region.json": `{"format":"{name}","rows":"region.tsv","key":"code","name":"name"}`,
|
||||
@@ -535,7 +548,6 @@ func TestTableFences(t *testing.T) {
|
||||
"name names no column": {map[string]string{"t.json": `{"format":"{a}","rows":"t.tsv","name":"b"}`, "t.tsv": "a\nx\ny\n"}, `"b"`},
|
||||
"weight names no column": {map[string]string{"t.json": `{"format":"{a}","rows":"t.tsv","weight":"b"}`, "t.tsv": "a\nx\ny\n"}, `"b"`},
|
||||
"parent names no column": {map[string]string{"t.json": `{"format":"{a}","rows":"t.tsv","parent":"b"}`, "t.tsv": "a\nx\ny\n"}, `"b"`},
|
||||
"key equals name": {map[string]string{"t.json": `{"format":"{a}","rows":"t.tsv","key":"a","name":"a"}`, "t.tsv": "a\nx\ny\n"}, "drop"},
|
||||
"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"}, `"["`},
|
||||
@@ -554,7 +566,6 @@ func TestTableFences(t *testing.T) {
|
||||
"no rows": {map[string]string{"t.json": `{"format":"{a}","rows":"t.tsv"}`, "t.tsv": "a\n"}, "no rows"},
|
||||
"a blank line after the rows": {map[string]string{"t.json": `{"format":"{a}","rows":"t.tsv"}`, "t.tsv": "a\tb\nx\t1\ny\t2\n\n"}, "line 4"},
|
||||
"a blank line in one column": {map[string]string{"t.json": `{"format":"{a}","rows":"t.tsv"}`, "t.tsv": "a\nx\n\ny\n"}, "line 3"},
|
||||
"one row": {map[string]string{"t.json": `{"format":"{a}","rows":"t.tsv"}`, "t.tsv": "a\nx\n"}, "one row"},
|
||||
"empty file": {map[string]string{"t.json": `{"format":"{a}","rows":"t.tsv"}`, "t.tsv": ""}, "header"},
|
||||
"parent is not a table": {map[string]string{"p.json": `"x"`, "t.json": `{"format":"{a}","rows":"t.tsv","parent":"p"}`, "t.tsv": "a\tp\nx\tx\ny\tx\n"}, "not a table"},
|
||||
"parent does not exist": {map[string]string{"t.json": `{"format":"{a}","rows":"t.tsv","parent":"p"}`, "t.tsv": "a\tp\nx\tx\ny\tx\n"}, `"p"`},
|
||||
@@ -647,34 +658,15 @@ func TestTableFences(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSameShapedChoiceIsATable(t *testing.T) {
|
||||
func TestSameShapedChoiceIsAChoice(t *testing.T) {
|
||||
rows := `[{"format":"{name}","name":"Sweden","alpha2":"SE"},{"format":"{name}","name":"Norway","alpha2":"NO"},{"format":"{name}","name":"Denmark","alpha2":"DK"}]`
|
||||
_, err := New(WithoutShippedData(), WithDataPath(writeData(t, map[string]string{"country": rows})))
|
||||
for _, want := range []string{"country.tsv", `"rows"`, `alpha2\tname`, "3 "} {
|
||||
if err == nil || !strings.Contains(err.Error(), want) {
|
||||
t.Fatalf("New(same-shaped choice) = %v, want it refused mentioning %q", err, want)
|
||||
}
|
||||
}
|
||||
weighted := `[{"format":"{name}","name":"Sweden","weight":2},{"format":"{name}","name":"Norway"}]`
|
||||
if _, err := New(WithoutShippedData(), WithDataPath(writeData(t, map[string]string{"country": weighted}))); err == nil || !strings.Contains(err.Error(), "weight") {
|
||||
t.Fatalf("New(weighted same-shaped choice) = %v, want it refused naming a weight column", err)
|
||||
f := newGenerator(t, writeData(t, map[string]string{"country": rows, "weighted": weighted}), WithSeed(1))
|
||||
if v := fake(t, f, "country"); v != "Sweden" && v != "Norway" && v != "Denmark" {
|
||||
t.Fatalf("country = %q, want one of its items", v)
|
||||
}
|
||||
accepted := map[string]string{
|
||||
"nested": `{"format":"{c}","c":` + rows + `}`,
|
||||
"a choice field": `[{"format":"{maker} {model}","maker":"BMW","model":["X3","X5"]},{"format":"{maker} {model}","maker":"Ford","model":["Focus","Fiesta"]}]`,
|
||||
"different formats": `[{"format":"{a}-{b}","a":"1","b":"2"},{"format":"{b}-{a}","a":"3","b":"4"}]`,
|
||||
"different fields": `[{"format":"{a}","a":"1","b":"2"},{"format":"{a}","a":"3"}]`,
|
||||
"a string item": `[{"format":"{a}","a":"1"},"x"]`,
|
||||
"strings": `["a","b","c"]`,
|
||||
}
|
||||
for name, json := range accepted {
|
||||
if _, err := New(WithoutShippedData(), WithDataPath(writeData(t, map[string]string{"x": json}))); err != nil {
|
||||
t.Errorf("%s: New = %v, want it accepted", name, err)
|
||||
}
|
||||
}
|
||||
f := newGenerator(t, writeData(t, map[string]string{"w": `"x"`}))
|
||||
if _, err := f.NewTemplate(rows); err != nil {
|
||||
t.Fatalf("NewTemplate(same-shaped choice) = %v, want an inline template exempt", err)
|
||||
if v := fake(t, f, "weighted"); v != "Sweden" && v != "Norway" {
|
||||
t.Fatalf("weighted = %q, want one of its items", v)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user