Tests for literal format text, brace escapes and the class builtins
This commit is contained in:
+5
-5
@@ -48,7 +48,7 @@ func BenchmarkCalc(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkLongLiteral(b *testing.B) {
|
func BenchmarkLongLiteral(b *testing.B) {
|
||||||
dir := tmpData(b, "sql", `{"format":"INSERT INTO customers (id, name, city) V#ALUES (#1#2#3, '{word}', '{word}');","word":["alpha","beta","gamma","delta"]}`)
|
dir := tmpData(b, "sql", `{"format":"INSERT INTO customers (id, name, city) VALUES (123, '{word}', '{word}');","word":["alpha","beta","gamma","delta"]}`)
|
||||||
benchPath(b, dir, "sql")
|
benchPath(b, dir, "sql")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,14 +62,14 @@ func BenchmarkRepeat(b *testing.B) {
|
|||||||
// two independent fields.
|
// two independent fields.
|
||||||
func BenchmarkBound(b *testing.B) {
|
func BenchmarkBound(b *testing.B) {
|
||||||
dir := tmpData(b, "addr", `{"format":"{place.postal-code} {place.locality}","place":[
|
dir := tmpData(b, "addr", `{"format":"{place.postal-code} {place.locality}","place":[
|
||||||
{"format":"{locality}","locality":"Stockholm","postal-code":{"format":"#100 00"}},
|
{"format":"{locality}","locality":"Stockholm","postal-code":{"format":"1{digits(2)} {digits(2)}"}},
|
||||||
{"format":"{locality}","locality":"Tranås","postal-code":{"format":"#5#7#3 00"}}]}`)
|
{"format":"{locality}","locality":"Tranås","postal-code":{"format":"573 {digits(2)}"}}]}`)
|
||||||
benchPath(b, dir, "addr")
|
benchPath(b, dir, "addr")
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkUnbound(b *testing.B) {
|
func BenchmarkUnbound(b *testing.B) {
|
||||||
dir := tmpData(b, "addr", `{"format":"{postal-code} {locality}",
|
dir := tmpData(b, "addr", `{"format":"{postal-code} {locality}",
|
||||||
"postal-code":[{"format":"#100 00"},{"format":"#5#7#3 00"}],
|
"postal-code":[{"format":"1{digits(2)} {digits(2)}"},{"format":"573 {digits(2)}"}],
|
||||||
"locality":["Stockholm","Tranås"]}`)
|
"locality":["Stockholm","Tranås"]}`)
|
||||||
benchPath(b, dir, "addr")
|
benchPath(b, dir, "addr")
|
||||||
}
|
}
|
||||||
@@ -78,7 +78,7 @@ func BenchmarkUnbound(b *testing.B) {
|
|||||||
// depth question is about.
|
// depth question is about.
|
||||||
func BenchmarkBoundDeep(b *testing.B) {
|
func BenchmarkBoundDeep(b *testing.B) {
|
||||||
dir := tmpData(b, "addr", `{"format":"{p.addr.city} {p.addr.zip}","p":[
|
dir := tmpData(b, "addr", `{"format":"{p.addr.city} {p.addr.zip}","p":[
|
||||||
{"format":"{addr}","addr":{"format":"{city}","city":["Stockholm","Tranås"],"zip":{"format":"#100 00"}}}]}`)
|
{"format":"{addr}","addr":{"format":"{city}","city":["Stockholm","Tranås"],"zip":{"format":"1{digits(2)} {digits(2)}"}}}]}`)
|
||||||
benchPath(b, dir, "addr")
|
benchPath(b, dir, "addr")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -16,8 +16,8 @@ import (
|
|||||||
// swedishPlaces is a two-variant sibling whose variants pair a locality with the
|
// swedishPlaces is a two-variant sibling whose variants pair a locality with the
|
||||||
// postal-code prefix that really belongs to it.
|
// postal-code prefix that really belongs to it.
|
||||||
const swedishPlaces = `{"format":"%s","place":[
|
const swedishPlaces = `{"format":"%s","place":[
|
||||||
{"format":"{locality}","locality":"Stockholm","postal-code":{"format":"#100 00"}},
|
{"format":"{locality}","locality":"Stockholm","postal-code":{"format":"1{digits(2)} {digits(2)}"}},
|
||||||
{"format":"{locality}","locality":"Tranås","postal-code":{"format":"#5#7#3 00"}}
|
{"format":"{locality}","locality":"Tranås","postal-code":{"format":"573 {digits(2)}"}}
|
||||||
]}`
|
]}`
|
||||||
|
|
||||||
// agree reports whether a rendered "postcode locality" pair is a real pairing.
|
// agree reports whether a rendered "postcode locality" pair is a real pairing.
|
||||||
|
|||||||
+16
-3
@@ -83,9 +83,9 @@ func TestBuiltinBase64(t *testing.T) {
|
|||||||
// hand-computed check, like the luhn test. mod-11 emits X when it would be 10.
|
// hand-computed check, like the luhn test. mod-11 emits X when it would be 10.
|
||||||
func TestBuiltinChecksums(t *testing.T) {
|
func TestBuiltinChecksums(t *testing.T) {
|
||||||
cases := map[string]string{
|
cases := map[string]string{
|
||||||
`{"format":"#1#2#3#4#5#6#7#8{mod11()}"}`: "123456785", // weights 2..7 from the right
|
`{"format":"12345678{mod11()}"}`: "123456785", // weights 2..7 from the right
|
||||||
`{"format":"#6{mod11()}"}`: "6X", // remainder 10 -> X
|
`{"format":"6{mod11()}"}`: "6X", // remainder 10 -> X
|
||||||
`{"format":"#4#0#0#6#3#8#1#3#3#3#9#3{ean()}"}`: "4006381333931", // EAN-13 (= ISBN-13) check digit
|
`{"format":"400638133393{ean()}"}`: "4006381333931", // EAN-13 (= ISBN-13) check digit
|
||||||
}
|
}
|
||||||
f := engine(1)
|
f := engine(1)
|
||||||
for tmpl, want := range cases {
|
for tmpl, want := range cases {
|
||||||
@@ -217,3 +217,16 @@ func mustPanic(t *testing.T, name string, call func()) {
|
|||||||
}()
|
}()
|
||||||
call()
|
call()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestClassBuiltinArgs(t *testing.T) {
|
||||||
|
for _, bad := range []string{`"{digits(0)}"`, `"{digits(2000000000)}"`, `"{upper(-1)}"`, `"{lower(x)}"`, `"{digits()}"`, `"{upper(1,2)}"`} {
|
||||||
|
if _, err := compile(parse(t, bad)); err == nil {
|
||||||
|
t.Errorf("compile(%s) = nil error, want the arg rejected", bad)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, ok := range []string{`"{digits(1048576)}"`, `"{upper(1)}"`, `"{lower(26)}"`} {
|
||||||
|
if _, err := compile(parse(t, ok)); err != nil {
|
||||||
|
t.Errorf("compile(%s) = %v", ok, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+12
-13
@@ -13,15 +13,14 @@ import (
|
|||||||
|
|
||||||
// --- format string edge cases ---
|
// --- format string edge cases ---
|
||||||
|
|
||||||
func TestEscapeEdgeCases(t *testing.T) {
|
func TestHashIsLiteral(t *testing.T) {
|
||||||
f := engine(1)
|
f := engine(1)
|
||||||
cases := map[string]string{
|
cases := map[string]string{
|
||||||
`{"format":""}`: "", // empty format
|
`{"format":"","x":["v"]}`: "",
|
||||||
`{"format":"#"}`: "#", // trailing escape is a literal #
|
`{"format":"#","x":["v"]}`: "#",
|
||||||
`{"format":"##"}`: "#", // escaped hash
|
`{"format":"##","x":["v"]}`: "##",
|
||||||
`{"format":"#0#1#A#a"}`: "01Aa", // escaped class chars stay literal
|
`{"format":"#0#1#A#a","x":["v"]}`: "#0#1#A#a",
|
||||||
`{"format":"#{x#}"}`: "{x}", // escaping braces disables tokens
|
`{"format":"#{x}","x":["v"]}`: "#v",
|
||||||
`{"format":"x}y"}`: "x}y", // an unmatched } is literal (x, y aren't classes)
|
|
||||||
}
|
}
|
||||||
for tmpl, want := range cases {
|
for tmpl, want := range cases {
|
||||||
if got := mustRender(t, f, tmpl); got != want {
|
if got := mustRender(t, f, tmpl); got != want {
|
||||||
@@ -33,7 +32,7 @@ func TestEscapeEdgeCases(t *testing.T) {
|
|||||||
func TestMultibyteFormat(t *testing.T) {
|
func TestMultibyteFormat(t *testing.T) {
|
||||||
// Scanning is rune-aware: multibyte literals coexist with class chars and
|
// Scanning is rune-aware: multibyte literals coexist with class chars and
|
||||||
// tokens without corrupting indices.
|
// tokens without corrupting indices.
|
||||||
got := mustRender(t, engine(2), `{"format":"Öster{x}-0å","x":["väg"]}`)
|
got := mustRender(t, engine(2), `{"format":"Öster{x}-{digits(1)}å","x":["väg"]}`)
|
||||||
if !regexp.MustCompile(`^Österväg-[0-9]å$`).MatchString(got) {
|
if !regexp.MustCompile(`^Österväg-[0-9]å$`).MatchString(got) {
|
||||||
t.Fatalf("multibyte format = %q", got)
|
t.Fatalf("multibyte format = %q", got)
|
||||||
}
|
}
|
||||||
@@ -224,11 +223,11 @@ func TestDeepDottedPath(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDescendIntoLiteralErrors(t *testing.T) {
|
func TestDescendIntoStringErrors(t *testing.T) {
|
||||||
f := engine(1)
|
f := engine(1)
|
||||||
f.categories = map[string]node{"greeting": compiled(t, `["hej"]`)}
|
f.categories = map[string]node{"greeting": compiled(t, `["hej"]`)}
|
||||||
if _, err := f.Fake("greeting.extra"); err == nil {
|
if _, err := f.Fake("greeting.extra"); err == nil || !strings.Contains(err.Error(), `no field "extra"`) {
|
||||||
t.Fatal("Fake(greeting.extra) = nil error, want descend-into-literal error")
|
t.Fatalf("Fake(greeting.extra) = %v, want a no-field error", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,8 +302,8 @@ func TestMissingFieldNamesItself(t *testing.T) {
|
|||||||
|
|
||||||
func TestCategoryRootShapes(t *testing.T) {
|
func TestCategoryRootShapes(t *testing.T) {
|
||||||
dir := writeData(t, map[string]string{
|
dir := writeData(t, map[string]string{
|
||||||
"obj": `{"format":"00"}`, // object root
|
"obj": `{"format":"{digits(2)}"}`, // object root
|
||||||
"lit": `"hello"`, // bare-string root
|
"lit": `"hello"`, // bare-string root
|
||||||
})
|
})
|
||||||
f := newGenerator(t, dir, WithSeed(1))
|
f := newGenerator(t, dir, WithSeed(1))
|
||||||
if got := fake(t, f, "obj"); !regexp.MustCompile(`^\d\d$`).MatchString(got) {
|
if got := fake(t, f, "obj"); !regexp.MustCompile(`^\d\d$`).MatchString(got) {
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ func TestSeededOutputIsStable(t *testing.T) {
|
|||||||
dir := writeData(t, map[string]string{
|
dir := writeData(t, map[string]string{
|
||||||
"alt": `{"format":"{a|b}","a":["A"],"b":["B"]}`,
|
"alt": `{"format":"{a|b}","a":["A"],"b":["B"]}`,
|
||||||
"calc": `{"format":"{net} x {qty} = {calc(net * qty, 2)}","net":["19.99","5.00","100.00"],"qty":["2","3","7"]}`,
|
"calc": `{"format":"{net} x {qty} = {calc(net * qty, 2)}","net":["19.99","5.00","100.00"],"qty":["2","3","7"]}`,
|
||||||
"classes": `{"format":"00-11-AA-aa"}`,
|
"classes": `{"format":"{digits(2)}-{int(1,9)}{int(1,9)}-{upper(2)}-{lower(2)}"}`,
|
||||||
"escapes": `{"format":"#0#1#A#a##{x}","x":["!"]}`,
|
"escapes": `{"format":"01Aa#{x}","x":["!"]}`,
|
||||||
"funcs": `{"format":"{hex(6)} {int(10,99)} {float(0,1,3)} {nanoid(5)} {seq()}"}`,
|
"funcs": `{"format":"{hex(6)} {int(10,99)} {float(0,1,3)} {nanoid(5)} {seq()}"}`,
|
||||||
"nested": `{"format":"{outer}","outer":[{"format":"{inner}-00","inner":["i"]}]}`,
|
"nested": `{"format":"{outer}","outer":[{"format":"{inner}-{digits(2)}","inner":["i"]}]}`,
|
||||||
"ref": `{"format":"see {..alt}"}`,
|
"ref": `{"format":"see {..alt}"}`,
|
||||||
"repeat": `{"format":"{w}","repeat":4,"separator":",","w":["x","y","z"]}`,
|
"repeat": `{"format":"{w}","repeat":4,"separator":",","w":["x","y","z"]}`,
|
||||||
"sums": `{"format":"9{d}{luhn()} {e}{ean()} {m}{mod11()}","d":["012345678901234"],"e":["123456789012"],"m":["12345678"]}`,
|
"sums": `{"format":"9{d}{luhn()} {e}{ean()} {m}{mod11()}","d":["012345678901234"],"e":["123456789012"],"m":["12345678"]}`,
|
||||||
|
|||||||
+99
-54
@@ -35,19 +35,33 @@ func mustRender(t *testing.T, f *Generator, s string) string {
|
|||||||
return render(f.rand, compiled(t, s))
|
return render(f.rand, compiled(t, s))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLiteralStringIsVerbatim(t *testing.T) {
|
func TestStringIsAFormat(t *testing.T) {
|
||||||
// A bare string is a literal, never formatted: 'a'/'A' must survive.
|
f := engine(1)
|
||||||
if got := mustRender(t, engine(1), `"Malmö"`); got != "Malmö" {
|
for src, want := range map[string]string{
|
||||||
t.Fatalf("literal = %q, want Malmö", got)
|
`"Malmö"`: "Malmö",
|
||||||
|
`"100 Main St, Apt 1A #0"`: "100 Main St, Apt 1A #0",
|
||||||
|
`"{{x}}"`: "{x}",
|
||||||
|
} {
|
||||||
|
if got := mustRender(t, f, src); got != want {
|
||||||
|
t.Errorf("render(%s) = %q, want %q", src, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if got := mustRender(t, f, `"{digits(3)}"`); !regexp.MustCompile(`^[0-9]{3}$`).MatchString(got) {
|
||||||
|
t.Errorf(`render("{digits(3)}") = %q, want three digits`, got)
|
||||||
|
}
|
||||||
|
if _, err := compile(parse(t, `"{x}"`)); err == nil || !strings.Contains(err.Error(), `no field "x"`) {
|
||||||
|
t.Errorf(`compile("{x}") = %v, want a no-field error`, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCharacterClasses(t *testing.T) {
|
func TestClassBuiltins(t *testing.T) {
|
||||||
cases := map[string]*regexp.Regexp{
|
cases := map[string]*regexp.Regexp{
|
||||||
`{"format":"0"}`: regexp.MustCompile(`^[0-9]$`),
|
`"{digits(1)}"`: regexp.MustCompile(`^[0-9]$`),
|
||||||
`{"format":"1"}`: regexp.MustCompile(`^[1-9]$`),
|
`"{digits(3)}"`: regexp.MustCompile(`^[0-9]{3}$`),
|
||||||
`{"format":"A"}`: regexp.MustCompile(`^[A-Z]$`),
|
`"{int(1,9)}"`: regexp.MustCompile(`^[1-9]$`),
|
||||||
`{"format":"a"}`: regexp.MustCompile(`^[a-z]$`),
|
`"{upper(1)}"`: regexp.MustCompile(`^[A-Z]$`),
|
||||||
|
`"{lower(1)}"`: regexp.MustCompile(`^[a-z]$`),
|
||||||
|
`"{upper(2)}{lower(2)}"`: regexp.MustCompile(`^[A-Z]{2}[a-z]{2}$`),
|
||||||
}
|
}
|
||||||
f := engine(7)
|
f := engine(7)
|
||||||
for tmpl, re := range cases {
|
for tmpl, re := range cases {
|
||||||
@@ -59,10 +73,34 @@ func TestCharacterClasses(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEscapeAndLiteralChars(t *testing.T) {
|
func TestTextIsLiteral(t *testing.T) {
|
||||||
// '#' escapes the next char; non-class chars (7, x, -) are literal.
|
if got := mustRender(t, engine(1), `{"format":"100 Main St #1 {x}","x":["A"]}`); got != "100 Main St #1 A" {
|
||||||
if got := mustRender(t, engine(1), `{"format":"#0#1#A#a## x7-z"}`); got != "01Aa# x7-z" {
|
t.Fatalf("text = %q, want it verbatim", got)
|
||||||
t.Fatalf("escape = %q, want \"01Aa# x7-z\"", got)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBraceEscapes(t *testing.T) {
|
||||||
|
f := engine(1)
|
||||||
|
for src, want := range map[string]string{
|
||||||
|
`{"format":"{{","x":["v"]}`: "{",
|
||||||
|
`{"format":"}}","x":["v"]}`: "}",
|
||||||
|
`{"format":"{{x}}","x":["v"]}`: "{x}",
|
||||||
|
`{"format":"{{{x}}}","x":["v"]}`: "{v}",
|
||||||
|
`{"format":"a{{{{b}}}}","x":["v"]}`: "a{{b}}",
|
||||||
|
} {
|
||||||
|
if got := mustRender(t, f, src); got != want {
|
||||||
|
t.Errorf("render(%s) = %q, want %q", src, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for src, want := range map[string]string{
|
||||||
|
`{"format":"x}y"}`: "}}",
|
||||||
|
`{"format":"}"}`: "}}",
|
||||||
|
`{"format":"{a{b}","a":["Q"]}`: "'{'",
|
||||||
|
`{"format":"{x"}`: "unterminated",
|
||||||
|
} {
|
||||||
|
if _, err := compile(parse(t, src)); err == nil || !strings.Contains(err.Error(), want) {
|
||||||
|
t.Errorf("compile(%s) = %v, want an error mentioning %s", src, err, want)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +156,7 @@ func TestRepeatRendersFormatNTimes(t *testing.T) {
|
|||||||
f, re := engine(7), regexp.MustCompile(`^[0-9]{4}$`)
|
f, re := engine(7), regexp.MustCompile(`^[0-9]{4}$`)
|
||||||
seen := map[string]bool{}
|
seen := map[string]bool{}
|
||||||
for i := 0; i < 50; i++ {
|
for i := 0; i < 50; i++ {
|
||||||
got := mustRender(t, f, `{"format":"0","repeat":4}`)
|
got := mustRender(t, f, `{"format":"{digits(1)}","repeat":4}`)
|
||||||
if !re.MatchString(got) {
|
if !re.MatchString(got) {
|
||||||
t.Fatalf("repeat-4 = %q, want 4 digits", got)
|
t.Fatalf("repeat-4 = %q, want 4 digits", got)
|
||||||
}
|
}
|
||||||
@@ -135,9 +173,9 @@ func TestFunctionTokenLuhn(t *testing.T) {
|
|||||||
// buffer, so a value is never re-rendered. Bodies are escaped to fix input.
|
// buffer, so a value is never re-rendered. Bodies are escaped to fix input.
|
||||||
f := engine(1)
|
f := engine(1)
|
||||||
cases := map[string]string{
|
cases := map[string]string{
|
||||||
`{"format":"#8#1#1#2#1#8#9#8#7{luhn()}"}`: "8112189876", // personnummer body
|
`{"format":"811218987{luhn()}"}`: "8112189876", // personnummer body
|
||||||
`{"format":"#7#9#9#2#7#3#9#8#7#1{luhn()}"}`: "79927398713", // classic Luhn vector
|
`{"format":"7992739871{luhn()}"}`: "79927398713", // classic Luhn vector
|
||||||
`{"format":"#8#1#1#2#1#8-#9#8#7{luhn()}"}`: "811218-9876", // '-' skipped, kept
|
`{"format":"811218-987{luhn()}"}`: "811218-9876", // '-' skipped, kept
|
||||||
`{"format":"{n}{luhn()}","n":["811218987"]}`: "8112189876", // over a rendered token
|
`{"format":"{n}{luhn()}","n":["811218987"]}`: "8112189876", // over a rendered token
|
||||||
}
|
}
|
||||||
for tmpl, want := range cases {
|
for tmpl, want := range cases {
|
||||||
@@ -162,45 +200,51 @@ func TestCompileErrors(t *testing.T) {
|
|||||||
// Every structural problem is caught up front, at compile/New time, never
|
// Every structural problem is caught up front, at compile/New time, never
|
||||||
// deferred to a random render that happens to hit the bad branch.
|
// deferred to a random render that happens to hit the bad branch.
|
||||||
for _, bad := range []string{
|
for _, bad := range []string{
|
||||||
`{"x":["Q"]}`, // object without "format"
|
`{"x":["Q"]}`, // object without "format"
|
||||||
`{"format":"{y}","x":1}`, // a field is a bare number
|
`{"format":"{y}","x":1}`, // a field is a bare number
|
||||||
`[1, 2]`, // a choice of numbers
|
`[1, 2]`, // a choice of numbers
|
||||||
`5`, // unsupported node type
|
`5`, // unsupported node type
|
||||||
`[]`, // empty choice
|
`[]`, // empty choice
|
||||||
`{"format":"{x"}`, // unterminated brace
|
`{"format":"{x"}`, // unterminated brace
|
||||||
`{"format":"{y}","x":["Q"]}`, // token names a missing field
|
`{"format":"x}y"}`, // a lone } must be written }}
|
||||||
`{"format":"{}"}`, // empty token name
|
`{"format":"{a{b}","a":["Q"]}`, // a brace inside a token
|
||||||
`{"format":"{a|}","a":["Q"]}`, // empty alternation segment
|
`"{x}"`, // a bare string has no fields to name
|
||||||
|
`{"format":"{digits(0)}"}`, // count must be positive
|
||||||
|
`{"format":"{upper(x)}"}`, // count must be an integer
|
||||||
|
`{"format":"{lower()}"}`, // wrong arity
|
||||||
|
`{"format":"{y}","x":["Q"]}`, // token names a missing field
|
||||||
|
`{"format":"{}"}`, // empty token name
|
||||||
|
`{"format":"{a|}","a":["Q"]}`, // empty alternation segment
|
||||||
`[{"format":"A","weight":-1},{"format":"B"}]`, // negative weight
|
`[{"format":"A","weight":-1},{"format":"B"}]`, // negative weight
|
||||||
`[{"format":"A","weight":0},{"format":"B","weight":0}]`, // weights sum to zero
|
`[{"format":"A","weight":0},{"format":"B","weight":0}]`, // weights sum to zero
|
||||||
`[{"format":"A","weight":1e308},{"format":"B","weight":1e308}]`, // weights overflow to +Inf
|
`[{"format":"A","weight":1e308},{"format":"B","weight":1e308}]`, // weights overflow to +Inf
|
||||||
`[{"format":"A","weight":"heavy"}]`, // non-numeric weight
|
`[{"format":"A","weight":"heavy"}]`, // non-numeric weight
|
||||||
`{"format":"x","repeat":0}`, // repeat below 1
|
`{"format":"x","repeat":0}`, // repeat below 1
|
||||||
`{"format":"x","repeat":-2}`, // negative repeat
|
`{"format":"x","repeat":-2}`, // negative repeat
|
||||||
`{"format":"x","repeat":1.5}`, // non-integer repeat
|
`{"format":"x","repeat":1.5}`, // non-integer repeat
|
||||||
`{"format":"x","repeat":"two"}`, // non-numeric repeat
|
`{"format":"x","repeat":"two"}`, // non-numeric repeat
|
||||||
`{"format":"x","repeat":2,"separator":5}`, // non-string separator
|
`{"format":"x","repeat":2,"separator":5}`, // non-string separator
|
||||||
`{"format":"{nope()}"}`, // unknown function
|
`{"format":"{nope()}"}`, // unknown function
|
||||||
`{"format":"{luhn(x)}"}`, // function given args it takes none of
|
`{"format":"{luhn(x)}"}`, // function given args it takes none of
|
||||||
`{"format":"{luhn(}"}`, // malformed function token
|
`{"format":"{luhn(}"}`, // malformed function token
|
||||||
`{"format":"{int(1)}"}`, // wrong arity
|
`{"format":"{int(1)}"}`, // wrong arity
|
||||||
`{"format":"{int(a,b)}"}`, // non-integer args
|
`{"format":"{int(a,b)}"}`, // non-integer args
|
||||||
`{"format":"{int(5,1)}"}`, // min > max
|
`{"format":"{int(5,1)}"}`, // min > max
|
||||||
`{"format":"{hex(0)}"}`, // count must be positive
|
`{"format":"{hex(0)}"}`, // count must be positive
|
||||||
`{"format":"{nanoid(-1)}"}`, // negative count
|
`{"format":"{nanoid(-1)}"}`, // negative count
|
||||||
`{"format":"{base64(0)}"}`, // count must be positive
|
`{"format":"{base64(0)}"}`, // count must be positive
|
||||||
`{"format":"{float(1,2)}"}`, // wrong arity
|
`{"format":"{float(1,2)}"}`, // wrong arity
|
||||||
`{"format":"{float(1,2,-1)}"}`, // negative decimals
|
`{"format":"{float(1,2,-1)}"}`, // negative decimals
|
||||||
`{"format":"{iban(US)}"}`, // unsupported country
|
`{"format":"{iban(US)}"}`, // unsupported country
|
||||||
`{"format":"{seq(a,b)}"}`, // seq takes at most one name
|
`{"format":"{seq(a,b)}"}`, // seq takes at most one name
|
||||||
`{"format":"{calc()}"}`, // calc needs an expression
|
`{"format":"{calc()}"}`, // calc needs an expression
|
||||||
`{"format":"{calc(1 +)}"}`, // dangling operator
|
`{"format":"{calc(1 +)}"}`, // dangling operator
|
||||||
`{"format":"{calc((1 + 2)}"}`, // unbalanced parenthesis
|
`{"format":"{calc((1 + 2)}"}`, // unbalanced parenthesis
|
||||||
`{"format":"{calc(1 2)}"}`, // two operands, no operator
|
`{"format":"{calc(1 2)}"}`, // two operands, no operator
|
||||||
`{"format":"{calc(price)}"}`, // operand names no field
|
`{"format":"{calc(price)}"}`, // operand names no field
|
||||||
`{"format":"{calc(1, 2, 3)}"}`, // too many args
|
`{"format":"{calc(1, 2, 3)}"}`, // too many args
|
||||||
`{"format":"{calc(1, x)}"}`, // decimals arg not an integer
|
`{"format":"{calc(1, x)}"}`, // decimals arg not an integer
|
||||||
`{"format":"{calc(1, -1)}"}`, // decimals negative
|
`{"format":"{calc(1, -1)}"}`, // decimals negative
|
||||||
} {
|
} {
|
||||||
if _, err := compile(parse(t, bad)); err == nil {
|
if _, err := compile(parse(t, bad)); err == nil {
|
||||||
t.Errorf("compile(%s) = nil error, want error", bad)
|
t.Errorf("compile(%s) = nil error, want error", bad)
|
||||||
@@ -235,8 +279,9 @@ func TestGrowIsALowerBound(t *testing.T) {
|
|||||||
for _, format := range []string{
|
for _, format := range []string{
|
||||||
"",
|
"",
|
||||||
"plain literal",
|
"plain literal",
|
||||||
"00-11-AA-aa",
|
"{digits(2)}-{int(1,9)}{int(1,9)}-{upper(2)}-{lower(2)}",
|
||||||
"#0#1#A#a## literal",
|
"01Aa# literal",
|
||||||
|
"{{}} {{{x}}}",
|
||||||
"Ö dag åäö 日本語",
|
"Ö dag åäö 日本語",
|
||||||
"{x}{x}{x}",
|
"{x}{x}{x}",
|
||||||
"{hex(8)}-{int(10,99)}-{nanoid(5)}",
|
"{hex(8)}-{int(10,99)}-{nanoid(5)}",
|
||||||
|
|||||||
Reference in New Issue
Block a user