Struct-filling #14
@@ -305,42 +305,22 @@ func TestRunShippedDataByDefault(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestClassify(t *testing.T) {
|
func TestClassify(t *testing.T) {
|
||||||
for arg, want := range map[string]argKind{
|
for arg, want := range map[string]argKind{"sv_SE.person": argPath, "name: {x}": argTemplate} {
|
||||||
"sv_SE.person": argPath,
|
if got, err := classify(arg); err != nil || got != want {
|
||||||
"person.last": argPath,
|
|
||||||
"name: {x}": argTemplate, // a { token: a path can never carry a brace
|
|
||||||
`{"format":"x"}`: argTemplate,
|
|
||||||
`["a","b"]`: argTemplate, // a JSON array carries no brace
|
|
||||||
`[1, 2]`: argTemplate,
|
|
||||||
` ["a","b"]`: argTemplate, // padding is the template's own error, not a shape verdict
|
|
||||||
`"hello"`: argTemplate, // a JSON string, the spelling a format-only object names
|
|
||||||
} {
|
|
||||||
got, err := classify(arg)
|
|
||||||
if err != nil || got != want {
|
|
||||||
t.Errorf("classify(%q) = %v, %v; want %v", arg, got, err, want)
|
t.Errorf("classify(%q) = %v, %v; want %v", arg, got, err, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for arg, want := range map[string]string{
|
if _, err := classify("[abc]"); err == nil || !strings.Contains(err.Error(), `holds a "["`) {
|
||||||
"[abc]": `holds a "["`,
|
t.Errorf("classify([abc]) = %v; want it rejected naming the bracket", err)
|
||||||
"[abc].field": `holds a "["`,
|
|
||||||
"x[1]": `holds a "["`,
|
|
||||||
"a]b": `holds a "]"`,
|
|
||||||
"a}b": `holds a "}"`,
|
|
||||||
`"abc`: `holds a "\""`,
|
|
||||||
`"a]b`: `holds a "\""`, // the opener the reader typed, not the bracket behind it
|
|
||||||
} {
|
|
||||||
_, err := classify(arg)
|
|
||||||
if err == nil || !strings.Contains(err.Error(), want) {
|
|
||||||
t.Errorf("classify(%q) = %v; want it rejected naming %s", arg, err, want)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUsageReferencesResolve(t *testing.T) {
|
func TestUsageReferencesResolve(t *testing.T) {
|
||||||
for _, token := range regexp.MustCompile(`\{/[^}]+\}`).FindAllString(usage, -1) {
|
for _, token := range regexp.MustCompile(`\{/[^}]+\}`).FindAllString(usage, -1) {
|
||||||
code, out, errb := runOut("--seed", "1", token)
|
path := token[2 : len(token)-1]
|
||||||
|
code, out, errb := runOut("--seed", "1", path)
|
||||||
if code != 0 || strings.TrimSpace(out) == "" {
|
if code != 0 || strings.TrimSpace(out) == "" {
|
||||||
t.Errorf("usage advertises %s: run = %d, %q, stderr %q", token, code, out, errb)
|
t.Errorf("usage advertises %s: run %s = %d, %q, stderr %q", token, path, code, out, errb)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -376,7 +356,8 @@ func TestRunTemplateMisuse(t *testing.T) {
|
|||||||
"{bad": "unterminated",
|
"{bad": "unterminated",
|
||||||
"[red,green]": "names no template either",
|
"[red,green]": "names no template either",
|
||||||
`{"format":"x"}`: "is a string",
|
`{"format":"x"}`: "is a string",
|
||||||
"{/no.such.path}": "no entry",
|
"name: {/no.such.path}": "no entry",
|
||||||
|
"{/sv_SE.person}": "write sv_SE.person",
|
||||||
"x[1]": "names no template either",
|
"x[1]": "names no template either",
|
||||||
` ["a","b"] `: "may not be padded",
|
` ["a","b"] `: "may not be padded",
|
||||||
} {
|
} {
|
||||||
|
|||||||
@@ -150,6 +150,44 @@ func TestNewTemplateReusable(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIsTemplate(t *testing.T) {
|
||||||
|
for arg, want := range map[string]bool{
|
||||||
|
"sv_SE.person": false,
|
||||||
|
"person.last": false,
|
||||||
|
"name: {x}": true,
|
||||||
|
`{"format":"x"}`: true,
|
||||||
|
`["a","b"]`: true,
|
||||||
|
`[1, 2]`: true,
|
||||||
|
` ["a","b"]`: true, // padding is the template's own error, not a shape verdict
|
||||||
|
`"hello"`: true,
|
||||||
|
"{/a}{/b}": true,
|
||||||
|
"{/a|/b}": true,
|
||||||
|
"{uppercase(/a)}": true,
|
||||||
|
"{{/a}}": true,
|
||||||
|
`"{/a} x"`: true,
|
||||||
|
} {
|
||||||
|
if got, err := IsTemplate(arg); err != nil || got != want {
|
||||||
|
t.Errorf("IsTemplate(%q) = %v, %v; want %v", arg, got, err, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for arg, want := range map[string]string{
|
||||||
|
"[abc]": `holds a "["`,
|
||||||
|
"[abc].field": `holds a "["`,
|
||||||
|
"x[1]": `holds a "["`,
|
||||||
|
"a]b": `holds a "]"`,
|
||||||
|
"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",
|
||||||
|
} {
|
||||||
|
if _, err := IsTemplate(arg); err == nil || !strings.Contains(err.Error(), want) {
|
||||||
|
t.Errorf("IsTemplate(%q) = %v; want it rejected naming %s", arg, err, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestFakeTemplateRepeatBound(t *testing.T) {
|
func TestFakeTemplateRepeatBound(t *testing.T) {
|
||||||
f := shipped(t)
|
f := shipped(t)
|
||||||
_, err := f.FakeTemplate(`{"format":"{x}","repeat":200,"x":{"format":"{y}","repeat":200,"y":{"format":"z","repeat":200}}}`)
|
_, err := f.FakeTemplate(`{"format":"{x}","repeat":200,"x":{"format":"{y}","repeat":200,"y":{"format":"z","repeat":200}}}`)
|
||||||
|
|||||||
+52
-8
@@ -19,14 +19,30 @@ type structUser struct {
|
|||||||
Home structPlace
|
Home structPlace
|
||||||
ID int64 `fake:"{seq()}"`
|
ID int64 `fake:"{seq()}"`
|
||||||
Last string `fake:"person.last"`
|
Last string `fake:"person.last"`
|
||||||
|
Level uint8 `fake:"{float(0,255,0)}"`
|
||||||
Nick *string `fake:"[null,\"bo\"]"`
|
Nick *string `fake:"[null,\"bo\"]"`
|
||||||
Note string
|
Note string
|
||||||
Rank *int `fake:"{\"format\":\"{r}\",\"r\":[\"1\",\"2\"]}"`
|
Rank *int `fake:"{\"format\":\"{r}\",\"r\":[\"1\",\"2\"]}"`
|
||||||
Score float32 `fake:"{float(0,1,2)}"`
|
Score float32 `fake:"{float(0,1,2)}"`
|
||||||
|
Skip *structPlace `fake:"-"`
|
||||||
Work *structPlace
|
Work *structPlace
|
||||||
hidden structPlace
|
hidden structPlace
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type structGiven struct {
|
||||||
|
First string `fake:"person.first"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StructFamily struct {
|
||||||
|
Last string `fake:"person.last"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type structEmployee struct {
|
||||||
|
structGiven
|
||||||
|
*StructFamily
|
||||||
|
Email string `fake:"{lowercase(/person.first)}@example.com"`
|
||||||
|
}
|
||||||
|
|
||||||
type structLink struct {
|
type structLink struct {
|
||||||
Name string `fake:"person.first"`
|
Name string `fake:"person.first"`
|
||||||
Next *structLink
|
Next *structLink
|
||||||
@@ -63,7 +79,7 @@ func TestFakeStructFillsTaggedFields(t *testing.T) {
|
|||||||
t.Fatalf("places %+v, %+v, want each nested struct one place, the pointer allocated", u.Home, u.Work)
|
t.Fatalf("places %+v, %+v, want each nested struct one place, the pointer allocated", u.Home, u.Work)
|
||||||
case u.ID != int64(i+1) || u.Age < 18 || u.Age > 99 || u.Score < 0 || u.Score > 1 || u.Rank == nil || (*u.Rank != 1 && *u.Rank != 2):
|
case u.ID != int64(i+1) || u.Age < 18 || u.Age > 99 || u.Score < 0 || u.Score > 1 || u.Rank == nil || (*u.Rank != 1 && *u.Rank != 2):
|
||||||
t.Fatalf("typed fields %+v, want each the value its tag draws", u)
|
t.Fatalf("typed fields %+v, want each the value its tag draws", u)
|
||||||
case u.Nick != nil && *u.Nick != "bo", u.Note != "keep", u.hidden != (structPlace{}):
|
case u.Nick != nil && *u.Nick != "bo", u.Note != "keep", u.hidden != (structPlace{}), u.Skip != nil:
|
||||||
t.Fatalf("%+v: want Nick nil or bo, and the untagged fields left as they were", u)
|
t.Fatalf("%+v: want Nick nil or bo, and the untagged fields left as they were", u)
|
||||||
}
|
}
|
||||||
if u.Active {
|
if u.Active {
|
||||||
@@ -78,6 +94,20 @@ func TestFakeStructFillsTaggedFields(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFakeStructFillsEmbeddedFieldsIntoItsRecord(t *testing.T) {
|
||||||
|
f := structData(t)
|
||||||
|
people := map[string]string{"Ada": "Lovelace", "Bo": "Ek"}
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
var e structEmployee
|
||||||
|
if err := f.FakeStruct(&e); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if e.StructFamily == nil || people[e.First] != e.Last || e.Email != strings.ToLower(e.First)+"@example.com" {
|
||||||
|
t.Fatalf("%+v, %+v: want the promoted fields one person with the struct's own, the embedded pointer allocated", e, e.StructFamily)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestFakeStructDrawsANestedStructApart(t *testing.T) {
|
func TestFakeStructDrawsANestedStructApart(t *testing.T) {
|
||||||
f := structData(t)
|
f := structData(t)
|
||||||
for i := 0; i < 100; i++ {
|
for i := 0; i < 100; i++ {
|
||||||
@@ -136,13 +166,16 @@ func TestFakeStructErrors(t *testing.T) {
|
|||||||
}{}, `"Ada" is not an integer`},
|
}{}, `"Ada" is not an integer`},
|
||||||
{&struct {
|
{&struct {
|
||||||
A int8 `fake:"{int(0,300)}"`
|
A int8 `fake:"{int(0,300)}"`
|
||||||
}{}, `"{int(0,300)}" is not proven within int8`},
|
}{}, `"{int(0,300)}" can reach 300, past int8; make it int64`},
|
||||||
{&struct {
|
{&struct {
|
||||||
A uint `fake:"{int(-1,5)}"`
|
A uint `fake:"{int(-1,5)}"`
|
||||||
}{}, `"{int(-1,5)}" is not proven within uint`},
|
}{}, `"{int(-1,5)}" can reach -1, past uint; make it int64`},
|
||||||
{&struct {
|
{&struct {
|
||||||
A float32 `fake:"[\"1\",\"1e39\"]"`
|
A float32 `fake:"[\"1\",\"1e39\"]"`
|
||||||
}{}, `"1e39" is not proven within float32`},
|
}{}, `"1e39" can reach 1e+39, past float32; make it float64`},
|
||||||
|
{&struct {
|
||||||
|
A int32 `fake:"{seq()}"`
|
||||||
|
}{}, `"{seq()}" can reach 9.223372036854776e+18, past int32; make it int64`},
|
||||||
{&struct {
|
{&struct {
|
||||||
A bool `fake:"{int(0,1)}"`
|
A bool `fake:"{int(0,1)}"`
|
||||||
}{}, "prints an integer, not a boolean"},
|
}{}, "prints an integer, not a boolean"},
|
||||||
@@ -151,10 +184,10 @@ func TestFakeStructErrors(t *testing.T) {
|
|||||||
}{}, `no entry "nope"`},
|
}{}, `no entry "nope"`},
|
||||||
{&struct {
|
{&struct {
|
||||||
A string `fake:"{/person.first}"`
|
A string `fake:"{/person.first}"`
|
||||||
}{}, `write fake:"person.first"`},
|
}{}, "is the path person.first written as a template; write person.first"},
|
||||||
{&struct {
|
{&struct {
|
||||||
A string `fake:"\"{/person.first}\""`
|
A string `fake:"\"{/person.first}\""`
|
||||||
}{}, `write fake:"person.first"`},
|
}{}, "is the path person.first written as a template; write person.first"},
|
||||||
{&struct {
|
{&struct {
|
||||||
A string `fake:"a|b"`
|
A string `fake:"a|b"`
|
||||||
}{}, `contains "|"`},
|
}{}, `contains "|"`},
|
||||||
@@ -165,8 +198,19 @@ func TestFakeStructErrors(t *testing.T) {
|
|||||||
A string `fake:"[abc]"`
|
A string `fake:"[abc]"`
|
||||||
}{}, `holds a "["`},
|
}{}, `holds a "["`},
|
||||||
{&struct {
|
{&struct {
|
||||||
A string `fake:"{.person.first}"`
|
A string `fake:"{.person.first} x"`
|
||||||
}{}, "write {/person.first}"},
|
}{}, "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 embedded pointer to an unexported type"},
|
||||||
|
{&struct {
|
||||||
|
structGiven
|
||||||
|
First string `fake:"person.last"`
|
||||||
|
}{}, "struct.structGiven.First: hidden by another field named First"},
|
||||||
{&struct {
|
{&struct {
|
||||||
A string `fake:"{x}"`
|
A string `fake:"{x}"`
|
||||||
}{}, `no field "x"`},
|
}{}, `no field "x"`},
|
||||||
@@ -178,7 +222,7 @@ func TestFakeStructErrors(t *testing.T) {
|
|||||||
Trip struct {
|
Trip struct {
|
||||||
A int `fake:"{digits(3)}"`
|
A int `fake:"{digits(3)}"`
|
||||||
}
|
}
|
||||||
}{}, ".Trip.A (int): {digits(3)} prints text"},
|
}{}, ": struct.Trip.A (int): {digits(3)} prints text"},
|
||||||
} {
|
} {
|
||||||
if err := f.FakeStruct(c.v); err == nil || !strings.Contains(err.Error(), c.want) {
|
if err := f.FakeStruct(c.v); err == nil || !strings.Contains(err.Error(), c.want) {
|
||||||
t.Errorf("FakeStruct(%T) = %v, want an error containing %q", c.v, err, c.want)
|
t.Errorf("FakeStruct(%T) = %v, want an error containing %q", c.v, err, c.want)
|
||||||
|
|||||||
Reference in New Issue
Block a user