mirror of
https://github.com/golang/net.git
synced 2026-03-31 18:37:08 +09:00
Change-Id: I4a06e68307ac3deb5b2e1bab9235ebe01dcd4ea6 Reviewed-on: https://go-review.googlesource.com/c/net/+/713380 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Sean Liao <sean@liao.dev> Reviewed-by: Sean Liao <sean@liao.dev> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Reviewed-by: David Chase <drchase@google.com>
1440 lines
32 KiB
Go
1440 lines
32 KiB
Go
// Copyright 2025 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
package httpsfv
|
|
|
|
import (
|
|
"slices"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestParseList(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
wantMembers []string
|
|
wantParams []string
|
|
wantOk bool
|
|
}{
|
|
{
|
|
name: "valid list",
|
|
in: `a, b,c`,
|
|
wantMembers: []string{"a", "b", "c"},
|
|
wantParams: []string{"", "", ""},
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid list with params",
|
|
in: `a;foo=bar, b,c; baz=baz`,
|
|
wantMembers: []string{"a", "b", "c"},
|
|
wantParams: []string{";foo=bar", "", "; baz=baz"},
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid list with fake commas",
|
|
in: `a;foo=",", (",")`,
|
|
wantMembers: []string{"a", `(",")`},
|
|
wantParams: []string{`;foo=","`, ""},
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid list with inner list member",
|
|
in: `(a b c); foo, bar;baz`,
|
|
wantMembers: []string{"(a b c)", "bar"},
|
|
wantParams: []string{"; foo", ";baz"},
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "invalid list with trailing comma",
|
|
in: `a;foo=bar, b,c; baz=baz,`,
|
|
wantMembers: []string{"a", "b", "c"},
|
|
wantParams: []string{";foo=bar", "", "; baz=baz"},
|
|
},
|
|
{
|
|
name: "invalid list with unclosed string",
|
|
in: `", b, c,d`,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
var gotMembers, gotParams []string
|
|
f := func(member, param string) {
|
|
gotMembers = append(gotMembers, member)
|
|
gotParams = append(gotParams, param)
|
|
}
|
|
ok := ParseList(tc.in, f)
|
|
if ok != tc.wantOk {
|
|
t.Fatalf("test %q: want ok to be %v, got: %v", tc.name, tc.wantOk, ok)
|
|
}
|
|
if !slices.Equal(tc.wantMembers, gotMembers) {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, gotMembers, tc.wantMembers)
|
|
}
|
|
if !slices.Equal(tc.wantParams, gotParams) {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, gotParams, tc.wantParams)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestConsumeBareInnerList(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
wantBareItems []string
|
|
wantParams []string
|
|
wantListParam string
|
|
wantOk bool
|
|
}{
|
|
{
|
|
name: "valid inner list without param",
|
|
in: `(a b c)`,
|
|
wantBareItems: []string{"a", "b", "c"},
|
|
wantParams: []string{"", "", ""},
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid inner list with param",
|
|
in: `(a;d b c;e)`,
|
|
wantBareItems: []string{"a", "b", "c"},
|
|
wantParams: []string{";d", "", ";e"},
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid inner list with fake ending parenthesis",
|
|
in: `(")";foo=")")`,
|
|
wantBareItems: []string{`")"`},
|
|
wantParams: []string{`;foo=")"`},
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid inner list with list parameter",
|
|
in: `(a b;c); d`,
|
|
wantBareItems: []string{"a", "b"},
|
|
wantParams: []string{"", ";c"},
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid inner list with more content after",
|
|
in: `(a b;c); d, a`,
|
|
wantBareItems: []string{"a", "b"},
|
|
wantParams: []string{"", ";c"},
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "invalid inner list",
|
|
in: `(a b;c `,
|
|
wantBareItems: []string{"a", "b"},
|
|
wantParams: []string{"", ";c"},
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
var gotBareItems, gotParams []string
|
|
f := func(bareItem, param string) {
|
|
gotBareItems = append(gotBareItems, bareItem)
|
|
gotParams = append(gotParams, param)
|
|
}
|
|
gotConsumed, gotRest, ok := consumeBareInnerList(tc.in, f)
|
|
if ok != tc.wantOk {
|
|
t.Fatalf("test %q: want ok to be %v, got: %v", tc.name, tc.wantOk, ok)
|
|
}
|
|
if !slices.Equal(tc.wantBareItems, gotBareItems) {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, gotBareItems, tc.wantBareItems)
|
|
}
|
|
if !slices.Equal(tc.wantParams, gotParams) {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, gotParams, tc.wantParams)
|
|
}
|
|
if gotConsumed+gotRest != tc.in {
|
|
t.Fatalf("test %q: %#v + %#v != %#v", tc.name, gotConsumed, gotRest, tc.in)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseBareInnerList(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
wantBareItems []string
|
|
wantParams []string
|
|
wantOk bool
|
|
}{
|
|
{
|
|
name: "valid inner list",
|
|
in: `(a b;c)`,
|
|
wantBareItems: []string{"a", "b"},
|
|
wantParams: []string{"", ";c"},
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid inner list with list parameter",
|
|
in: `(a b;c); d`,
|
|
wantBareItems: []string{"a", "b"},
|
|
wantParams: []string{"", ";c"},
|
|
},
|
|
{
|
|
name: "invalid inner list",
|
|
in: `(a b;c `,
|
|
wantBareItems: []string{"a", "b"},
|
|
wantParams: []string{"", ";c"},
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
var gotBareItems, gotParams []string
|
|
f := func(bareItem, param string) {
|
|
gotBareItems = append(gotBareItems, bareItem)
|
|
gotParams = append(gotParams, param)
|
|
}
|
|
ok := ParseBareInnerList(tc.in, f)
|
|
if ok != tc.wantOk {
|
|
t.Fatalf("test %q: want ok to be %v, got: %v", tc.name, tc.wantOk, ok)
|
|
}
|
|
if !slices.Equal(tc.wantBareItems, gotBareItems) {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, gotBareItems, tc.wantBareItems)
|
|
}
|
|
if !slices.Equal(tc.wantParams, gotParams) {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, gotParams, tc.wantParams)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestConsumeItem(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
wantBareItem string
|
|
wantParam string
|
|
wantOk bool
|
|
}{
|
|
{
|
|
name: "valid bare item",
|
|
in: `fookey`,
|
|
wantBareItem: `fookey`,
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid bare item and param",
|
|
in: `fookey; a="123"`,
|
|
wantBareItem: `fookey`,
|
|
wantParam: `; a="123"`,
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid item with content after",
|
|
in: `fookey; a="123", otheritem; otherparam=1`,
|
|
wantBareItem: `fookey`,
|
|
wantParam: `; a="123"`,
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "invalid just param",
|
|
in: `;a="123"`,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
var gotBareItem, gotParam string
|
|
f := func(bareItem, param string) {
|
|
gotBareItem = bareItem
|
|
gotParam = param
|
|
}
|
|
gotConsumed, gotRest, ok := consumeItem(tc.in, f)
|
|
if ok != tc.wantOk {
|
|
t.Fatalf("test %q: want ok to be %v, got: %v", tc.name, tc.wantOk, ok)
|
|
}
|
|
if tc.wantBareItem != gotBareItem {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, gotBareItem, tc.wantBareItem)
|
|
}
|
|
if tc.wantParam != gotParam {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, gotParam, tc.wantParam)
|
|
}
|
|
if gotConsumed+gotRest != tc.in {
|
|
t.Fatalf("test %q: %#v + %#v != %#v", tc.name, gotConsumed, gotRest, tc.in)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseItem(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
wantBareItem string
|
|
wantParam string
|
|
wantOk bool
|
|
}{
|
|
{
|
|
name: "valid bare item",
|
|
in: `fookey`,
|
|
wantBareItem: `fookey`,
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid bare item and param",
|
|
in: `fookey; a="123"`,
|
|
wantBareItem: `fookey`,
|
|
wantParam: `; a="123"`,
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid item with content after",
|
|
in: `fookey; a="123", otheritem; otherparam=1`,
|
|
wantBareItem: `fookey`,
|
|
wantParam: `; a="123"`,
|
|
},
|
|
{
|
|
name: "invalid just param",
|
|
in: `;a="123"`,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
var gotBareItem, gotParam string
|
|
f := func(bareItem, param string) {
|
|
gotBareItem = bareItem
|
|
gotParam = param
|
|
}
|
|
ok := ParseItem(tc.in, f)
|
|
if ok != tc.wantOk {
|
|
t.Fatalf("test %q: want ok to be %v, got: %v", tc.name, tc.wantOk, ok)
|
|
}
|
|
if tc.wantBareItem != gotBareItem {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, gotBareItem, tc.wantBareItem)
|
|
}
|
|
if tc.wantParam != gotParam {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, gotParam, tc.wantParam)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseDictionary(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
wantVal string
|
|
wantParam string
|
|
wantOk bool
|
|
}{
|
|
{
|
|
name: "valid dictionary with simple value",
|
|
in: `a=b, want=foo, c=d`,
|
|
wantVal: "foo",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid dictionary with implicit value",
|
|
in: `a, want, c=d`,
|
|
wantVal: "?1",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid dictionary with parameter",
|
|
in: `a, want=foo;bar=baz, c=d`,
|
|
wantVal: "foo",
|
|
wantParam: ";bar=baz",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid dictionary with inner list",
|
|
in: `a, want=(a b c d;e;f);g=h, c=d`,
|
|
wantVal: "(a b c d;e;f)",
|
|
wantParam: ";g=h",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid dictionary with fake commas",
|
|
in: `a=(";");b=";",want=foo;bar`,
|
|
wantVal: "foo",
|
|
wantParam: ";bar",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "invalid dictionary with bad key",
|
|
in: `UPPERCASEKEY=BAD, want=foo, c=d`,
|
|
},
|
|
{
|
|
name: "invalid dictionary with trailing comma",
|
|
in: `trailing=comma,`,
|
|
},
|
|
{
|
|
name: "invalid dictionary with unclosed string",
|
|
in: `a=""",want=foo;bar`,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
var gotVal, gotParam string
|
|
f := func(key, val, param string) {
|
|
if key == "want" {
|
|
gotVal = val
|
|
gotParam = param
|
|
}
|
|
}
|
|
ok := ParseDictionary(tc.in, f)
|
|
if ok != tc.wantOk {
|
|
t.Fatalf("test %q: want ok to be %v, got: %v", tc.name, tc.wantOk, ok)
|
|
}
|
|
if tc.wantVal != gotVal {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, gotVal, tc.wantVal)
|
|
}
|
|
if tc.wantParam != gotParam {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, gotParam, tc.wantParam)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestConsumeParameter(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
want any
|
|
wantOk bool
|
|
}{
|
|
{
|
|
name: "valid string",
|
|
in: `;parameter;want="wantvalue"`,
|
|
want: "wantvalue",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid integer",
|
|
in: `;parameter;want=123456;something`,
|
|
want: 123456,
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid decimal",
|
|
in: `;parameter;want=3.14;something`,
|
|
want: 3.14,
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid implicit bool",
|
|
in: `;parameter;want;something`,
|
|
want: true,
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid token",
|
|
in: `;want=*atoken;something`,
|
|
want: "*atoken",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid byte sequence",
|
|
in: `;want=:eWF5Cg==:;something`,
|
|
want: "eWF5Cg==",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid repeated key",
|
|
in: `;want=:eWF5Cg==:;now;want=1;is;repeated;want="overwritten!"`,
|
|
want: "overwritten!",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid parameter with content after",
|
|
in: `;want=:eWF5Cg==:;now;want=1;is;repeated;want="overwritten!", some=stuff`,
|
|
want: "overwritten!",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "invalid parameter",
|
|
in: `;UPPERCASEKEY=NOT_ACCEPTED`,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests[len(tests)-1:] {
|
|
var got any
|
|
f := func(key, val string) {
|
|
if key != "want" {
|
|
return
|
|
}
|
|
switch {
|
|
case strings.HasPrefix(val, "?"): // Bool
|
|
got = val == "?1"
|
|
case strings.HasPrefix(val, `"`): // String
|
|
got = val[1 : len(val)-1]
|
|
case strings.HasPrefix(val, "*"): // Token
|
|
got = val
|
|
case strings.HasPrefix(val, ":"): // Byte sequence
|
|
got = val[1 : len(val)-1]
|
|
default:
|
|
if valConv, err := strconv.Atoi(val); err == nil { // Integer
|
|
got = valConv
|
|
return
|
|
}
|
|
if valConv, err := strconv.ParseFloat(val, 64); err == nil { // Float
|
|
got = valConv
|
|
return
|
|
}
|
|
}
|
|
}
|
|
consumed, rest, ok := consumeParameter(tc.in, f)
|
|
if ok != tc.wantOk {
|
|
t.Fatalf("test %q: want ok to be %v, got: %v", tc.name, tc.wantOk, ok)
|
|
}
|
|
if got != tc.want {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, got, tc.want)
|
|
}
|
|
if consumed+rest != tc.in {
|
|
t.Fatalf("test %q: %#v + %#v != %#v", tc.name, got, rest, tc.in)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseParameter(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
want any
|
|
wantOk bool
|
|
}{
|
|
{
|
|
name: "valid parameter",
|
|
in: `;parameter;want="wantvalue"`,
|
|
want: "wantvalue",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid parameter with content after",
|
|
in: `;want=:eWF5Cg==:;now;want=1;is;repeated;want="overwritten!", some=stuff`,
|
|
want: "overwritten!",
|
|
},
|
|
{
|
|
name: "invalid parameter",
|
|
in: `;UPPERCASEKEY=NOT_ACCEPTED`,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests[len(tests)-1:] {
|
|
var got any
|
|
f := func(key, val string) {
|
|
if key != "want" {
|
|
return
|
|
}
|
|
switch {
|
|
case strings.HasPrefix(val, "?"): // Bool
|
|
got = val == "?1"
|
|
case strings.HasPrefix(val, `"`): // String
|
|
got = val[1 : len(val)-1]
|
|
case strings.HasPrefix(val, "*"): // Token
|
|
got = val
|
|
case strings.HasPrefix(val, ":"): // Byte sequence
|
|
got = val[1 : len(val)-1]
|
|
default:
|
|
if valConv, err := strconv.Atoi(val); err == nil { // Integer
|
|
got = valConv
|
|
return
|
|
}
|
|
if valConv, err := strconv.ParseFloat(val, 64); err == nil { // Float
|
|
got = valConv
|
|
return
|
|
}
|
|
}
|
|
}
|
|
ok := ParseParameter(tc.in, f)
|
|
if ok != tc.wantOk {
|
|
t.Fatalf("test %q: want ok to be %v, got: %v", tc.name, tc.wantOk, ok)
|
|
}
|
|
if got != tc.want {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, got, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestConsumeKey(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
want string
|
|
wantOk bool
|
|
}{
|
|
{
|
|
name: "valid basic key",
|
|
in: `fookey`,
|
|
want: `fookey`,
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid basic key with more content after",
|
|
in: `fookey,u=7`,
|
|
want: `fookey`,
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "invalid key",
|
|
in: `1keycannotstartwithnum`,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
got, gotRest, ok := consumeKey(tc.in)
|
|
if ok != tc.wantOk {
|
|
t.Fatalf("test %q: want ok to be %v, got: %v", tc.name, tc.wantOk, ok)
|
|
}
|
|
if tc.want != got {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, got, tc.want)
|
|
}
|
|
if got+gotRest != tc.in {
|
|
t.Fatalf("test %q: %#v + %#v != %#v", tc.name, got, gotRest, tc.in)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestConsumeIntegerOrDecimal(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
want string
|
|
wantOk bool
|
|
}{
|
|
{
|
|
name: "valid integer",
|
|
in: "123456",
|
|
want: "123456",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid integer with more content after",
|
|
in: "123456,12345",
|
|
want: "123456",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid max integer",
|
|
in: "999999999999999",
|
|
want: "999999999999999",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid min integer",
|
|
in: "-999999999999999",
|
|
want: "-999999999999999",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "invalid integer too high",
|
|
in: "9999999999999999",
|
|
},
|
|
{
|
|
name: "invalid integer too low",
|
|
in: "-9999999999999999",
|
|
},
|
|
{
|
|
name: "valid decimal",
|
|
in: "-123456789012.123",
|
|
want: "-123456789012.123",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "invalid decimal integer component too long",
|
|
in: "1234567890123.1",
|
|
},
|
|
{
|
|
name: "invalid decimal fraction component too long",
|
|
in: "1.1234",
|
|
},
|
|
{
|
|
name: "invalid decimal trailing dot",
|
|
in: "1.",
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
got, gotRest, ok := consumeIntegerOrDecimal(tc.in)
|
|
if ok != tc.wantOk {
|
|
t.Fatalf("test %q: want ok to be %v, got: %v", tc.name, tc.wantOk, ok)
|
|
}
|
|
if tc.want != got {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, got, tc.want)
|
|
}
|
|
if got+gotRest != tc.in {
|
|
t.Fatalf("test %q: %#v + %#v != %#v", tc.name, got, gotRest, tc.in)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseInteger(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
want int64
|
|
wantOk bool
|
|
}{
|
|
{
|
|
name: "valid integer",
|
|
in: "123456",
|
|
want: 123456,
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid integer with more content after",
|
|
in: "123456,12345",
|
|
},
|
|
{
|
|
name: "valid max integer",
|
|
in: "999999999999999",
|
|
want: 999999999999999,
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid min integer",
|
|
in: "-999999999999999",
|
|
want: -999999999999999,
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "invalid integer too high",
|
|
in: "9999999999999999",
|
|
},
|
|
{
|
|
name: "invalid integer too low",
|
|
in: "-9999999999999999",
|
|
},
|
|
{
|
|
name: "invalid integer with fraction",
|
|
in: "-123456789012.123",
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
got, ok := ParseInteger(tc.in)
|
|
if ok != tc.wantOk {
|
|
t.Fatalf("test %q: want ok to be %v, got: %v", tc.name, tc.wantOk, ok)
|
|
}
|
|
if tc.want != got {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, got, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseDecimal(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
want float64
|
|
wantOk bool
|
|
}{
|
|
{
|
|
name: "valid decimal",
|
|
in: "123456.789",
|
|
want: 123456.789,
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid decimal with more content after",
|
|
in: "123456.789, 123",
|
|
},
|
|
{
|
|
name: "invalid decimal with no fraction",
|
|
in: "123456",
|
|
},
|
|
{
|
|
name: "invalid decimal integer component too long",
|
|
in: "1234567890123.1",
|
|
},
|
|
{
|
|
name: "invalid decimal fraction component too long",
|
|
in: "1.1234",
|
|
},
|
|
{
|
|
name: "invalid decimal trailing dot",
|
|
in: "1.",
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
got, ok := ParseDecimal(tc.in)
|
|
if ok != tc.wantOk {
|
|
t.Fatalf("test %q: want ok to be %v, got: %v", tc.name, tc.wantOk, ok)
|
|
}
|
|
if tc.want != got {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, got, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestConsumeString(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
want string
|
|
wantOk bool
|
|
}{
|
|
{
|
|
name: "valid basic string",
|
|
in: `"foo bar"`,
|
|
want: `"foo bar"`,
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid basic string with more content after",
|
|
in: `"foo bar", a=3`,
|
|
want: `"foo bar"`,
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid string with escaped dquote",
|
|
in: `"foo bar \""`,
|
|
want: `"foo bar \""`,
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "invalid string no starting dquote",
|
|
in: `foo bar"`,
|
|
},
|
|
{
|
|
name: "invalid string no closing dquote",
|
|
in: `"foo bar`,
|
|
},
|
|
{
|
|
name: "invalid string invalid character",
|
|
in: string([]byte{'"', 0x00, '"'}),
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
got, gotRest, ok := consumeString(tc.in)
|
|
if ok != tc.wantOk {
|
|
t.Fatalf("test %q: want ok to be %v, got: %v", tc.name, tc.wantOk, ok)
|
|
}
|
|
if tc.want != got {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, got, tc.want)
|
|
}
|
|
if got+gotRest != tc.in {
|
|
t.Fatalf("test %q: %#v + %#v != %#v", tc.name, got, gotRest, tc.in)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseString(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
want string
|
|
wantOk bool
|
|
}{
|
|
{
|
|
name: "valid basic string",
|
|
in: `"foo bar"`,
|
|
want: "foo bar",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid basic string with more content after",
|
|
in: `"foo bar", a=3`,
|
|
},
|
|
{
|
|
name: "valid string with escaped dquote",
|
|
in: `"foo bar \""`,
|
|
want: `foo bar \"`,
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "invalid string no starting dquote",
|
|
in: `foo bar"`,
|
|
},
|
|
{
|
|
name: "invalid string no closing dquote",
|
|
in: `"foo bar`,
|
|
},
|
|
{
|
|
name: "invalid string invalid character",
|
|
in: string([]byte{'"', 0x00, '"'}),
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
got, ok := ParseString(tc.in)
|
|
if ok != tc.wantOk {
|
|
t.Fatalf("test %q: want ok to be %v, got: %v", tc.name, tc.wantOk, ok)
|
|
}
|
|
if tc.want != got {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, got, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestConsumeToken(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
want string
|
|
wantOk bool
|
|
}{
|
|
{
|
|
name: "valid token",
|
|
in: "*atoken",
|
|
want: "*atoken",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid token with more content after",
|
|
in: "*atoken something",
|
|
want: "*atoken",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "invalid token",
|
|
in: "0invalid",
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
got, gotRest, ok := consumeToken(tc.in)
|
|
if ok != tc.wantOk {
|
|
t.Fatalf("test %q: want ok to be %v, got: %v", tc.name, tc.wantOk, ok)
|
|
}
|
|
if tc.want != got {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, got, tc.want)
|
|
}
|
|
if got+gotRest != tc.in {
|
|
t.Fatalf("test %q: %#v + %#v != %#v", tc.name, got, gotRest, tc.in)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseToken(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
want string
|
|
wantOk bool
|
|
}{
|
|
{
|
|
name: "valid token",
|
|
in: "a_b-c.d3:f%00/*",
|
|
want: "a_b-c.d3:f%00/*",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid token with uppercase",
|
|
in: "FOOBAR",
|
|
want: "FOOBAR",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid token with content after",
|
|
in: "FOOBAR, foobar",
|
|
},
|
|
{
|
|
name: "invalid token",
|
|
in: "0invalid",
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
got, ok := ParseToken(tc.in)
|
|
if ok != tc.wantOk {
|
|
t.Fatalf("test %q: want ok to be %v, got: %v", tc.name, tc.wantOk, ok)
|
|
}
|
|
if tc.want != got {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, got, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestConsumeByteSequence(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
want string
|
|
wantOk bool
|
|
}{
|
|
{
|
|
name: "valid byte sequence",
|
|
in: ":aGVsbG8gd29ybGQ=:",
|
|
want: ":aGVsbG8gd29ybGQ=:",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid byte sequence with more content after",
|
|
in: ":aGVsbG8gd29ybGQ=::aGVsbG8gd29ybGQ=:",
|
|
want: ":aGVsbG8gd29ybGQ=:",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "invalid byte sequence character",
|
|
in: ":-:",
|
|
},
|
|
{
|
|
name: "invalid byte sequence opening",
|
|
in: "aGVsbG8gd29ybGQ=:",
|
|
},
|
|
{
|
|
name: "invalid byte sequence closing",
|
|
in: ":aGVsbG8gd29ybGQ=",
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
got, gotRest, ok := consumeByteSequence(tc.in)
|
|
if ok != tc.wantOk {
|
|
t.Fatalf("test %q: want ok to be %v, got: %v", tc.name, tc.wantOk, ok)
|
|
}
|
|
if tc.want != got {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, got, tc.want)
|
|
}
|
|
if got+gotRest != tc.in {
|
|
t.Fatalf("test %q: %#v + %#v != %#v", tc.name, got, gotRest, tc.in)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseByteSequence(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
want []byte
|
|
wantOk bool
|
|
}{
|
|
{
|
|
name: "valid byte sequence",
|
|
in: ":aGVsbG8gd29ybGQ=:",
|
|
want: []byte("aGVsbG8gd29ybGQ="),
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid byte sequence with more content after",
|
|
in: ":aGVsbG8gd29ybGQ=::aGVsbG8gd29ybGQ=:",
|
|
},
|
|
{
|
|
name: "invalid byte sequence character",
|
|
in: ":-:",
|
|
},
|
|
{
|
|
name: "invalid byte sequence opening",
|
|
in: "aGVsbG8gd29ybGQ=:",
|
|
},
|
|
{
|
|
name: "invalid byte sequence closing",
|
|
in: ":aGVsbG8gd29ybGQ=",
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
got, ok := ParseByteSequence(tc.in)
|
|
if ok != tc.wantOk {
|
|
t.Fatalf("test %q: want ok to be %v, got: %v", tc.name, tc.wantOk, ok)
|
|
}
|
|
if !slices.Equal(tc.want, got) {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, got, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestConsumeBoolean(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
want string
|
|
wantOk bool
|
|
}{
|
|
{
|
|
name: "valid boolean",
|
|
in: "?0",
|
|
want: "?0",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid boolean with more content after",
|
|
in: "?1, a=1",
|
|
want: "?1",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "invalid boolean",
|
|
in: "!2",
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
got, gotRest, ok := consumeBoolean(tc.in)
|
|
if ok != tc.wantOk {
|
|
t.Fatalf("test %q: want ok to be %v, got: %v", tc.name, tc.wantOk, ok)
|
|
}
|
|
if tc.want != got {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, got, tc.want)
|
|
}
|
|
if got+gotRest != tc.in {
|
|
t.Fatalf("test %q: %#v + %#v != %#v", tc.name, got, gotRest, tc.in)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseBoolean(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
want bool
|
|
wantOk bool
|
|
}{
|
|
{
|
|
name: "valid boolean false",
|
|
in: "?0",
|
|
want: false,
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid boolean true",
|
|
in: "?1",
|
|
want: true,
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid boolean with more content after",
|
|
in: "?1, a=1",
|
|
},
|
|
{
|
|
name: "invalid boolean",
|
|
in: "?2",
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
got, ok := ParseBoolean(tc.in)
|
|
if ok != tc.wantOk {
|
|
t.Fatalf("test %q: want ok to be %v, got: %v", tc.name, tc.wantOk, ok)
|
|
}
|
|
if tc.want != got {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, got, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestConsumeDate(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
want string
|
|
wantOk bool
|
|
}{
|
|
{
|
|
name: "valid zero date",
|
|
in: "@0",
|
|
want: "@0",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid positive date",
|
|
in: "@1659578233",
|
|
want: "@1659578233",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid negative date",
|
|
in: "@-1659578233",
|
|
want: "@-1659578233",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid large date",
|
|
in: "@25340221440",
|
|
want: "@25340221440",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid small date",
|
|
in: "@-62135596800",
|
|
want: "@-62135596800",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "invalid decimal date",
|
|
in: "@1.2",
|
|
},
|
|
{
|
|
name: "valid date with more content after",
|
|
in: "@1659578233, foo;bar",
|
|
want: "@1659578233",
|
|
wantOk: true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
got, gotRest, ok := consumeDate(tc.in)
|
|
if ok != tc.wantOk {
|
|
t.Fatalf("test %q: want ok to be %v, got: %v", tc.name, tc.wantOk, ok)
|
|
}
|
|
if tc.want != got {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, got, tc.want)
|
|
}
|
|
if got+gotRest != tc.in {
|
|
t.Fatalf("test %q: %#v + %#v != %#v", tc.name, got, gotRest, tc.in)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseDate(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
want time.Time
|
|
wantOk bool
|
|
}{
|
|
{
|
|
name: "valid zero date",
|
|
in: "@0",
|
|
want: time.Unix(0, 0),
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid positive date",
|
|
in: "@1659578233",
|
|
want: time.Date(2022, 8, 4, 1, 57, 13, 0, time.UTC).Local(),
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid negative date",
|
|
in: "@-1659578233",
|
|
want: time.Date(1917, 5, 30, 22, 2, 47, 0, time.UTC).Local(),
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid max date required",
|
|
in: "@253402214400",
|
|
want: time.Date(9999, 12, 31, 0, 0, 0, 0, time.UTC).Local(),
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid min date required",
|
|
in: "@-62135596800",
|
|
want: time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC).Local(),
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "invalid date with fraction",
|
|
in: "@0.123",
|
|
},
|
|
{
|
|
name: "valid date with more content after",
|
|
in: "@0, @0",
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
got, ok := ParseDate(tc.in)
|
|
if ok != tc.wantOk {
|
|
t.Fatalf("test %q: want ok to be %v, got: %v", tc.name, tc.wantOk, ok)
|
|
}
|
|
if tc.want != got {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, got, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestConsumeDisplayString(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
want string
|
|
wantOk bool
|
|
}{
|
|
{
|
|
name: "valid ascii string",
|
|
in: "%\" !%22#$%25&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\"",
|
|
want: "%\" !%22#$%25&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\"",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid lowercase non-ascii string",
|
|
in: `%"f%c3%bc%c3%bc"`,
|
|
want: `%"f%c3%bc%c3%bc"`,
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "invalid uppercase non-ascii string",
|
|
in: `%"f%C3%BC%C3%BC"`,
|
|
},
|
|
{
|
|
name: "invalid unquoted string",
|
|
in: "%foo",
|
|
},
|
|
{
|
|
name: "invalid string missing initial quote",
|
|
in: `%foo"`,
|
|
},
|
|
{
|
|
name: "invalid string missing closing quote",
|
|
in: `%"foo`,
|
|
},
|
|
{
|
|
name: "invalid tab in string",
|
|
in: "%\"\t\"",
|
|
},
|
|
{
|
|
name: "invalid newline in string",
|
|
in: "%\"\n\"",
|
|
},
|
|
{
|
|
name: "invalid single quoted string",
|
|
in: `%'foo'`,
|
|
},
|
|
{
|
|
name: "invalid string bad escaping",
|
|
in: `%\"foo %a"`,
|
|
},
|
|
{
|
|
name: "valid string with escaped quotes",
|
|
in: `%"foo %22bar%22 \\ baz"`,
|
|
want: `%"foo %22bar%22 \\ baz"`,
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "invalid sequence id utf-8 string",
|
|
in: `%"%a0%a1"`,
|
|
},
|
|
{
|
|
name: "invalid 2 bytes sequence utf-8 string",
|
|
in: `%"%c3%28"`,
|
|
},
|
|
{
|
|
name: "invalid 3 bytes sequence utf-8 string",
|
|
in: `%"%e2%28%a1"`,
|
|
},
|
|
{
|
|
name: "invalid 4 bytes sequence utf-8 string",
|
|
in: `%"%f0%28%8c%28"`,
|
|
},
|
|
{
|
|
name: "invalid hex utf-8 string",
|
|
in: `%"%g0%1w"`,
|
|
},
|
|
{
|
|
name: "valid byte order mark in display string",
|
|
in: `%"BOM: %ef%bb%bf"`,
|
|
want: `%"BOM: %ef%bb%bf"`,
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid string with content after",
|
|
in: `%"foo\nbar", foo;bar`,
|
|
want: `%"foo\nbar"`,
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "invalid unfinished 4 bytes rune",
|
|
in: `%"%f0%9f%98"`,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
got, gotRest, ok := consumeDisplayString(tc.in)
|
|
if ok != tc.wantOk {
|
|
t.Fatalf("test %q: want ok to be %v, got: %v", tc.name, tc.wantOk, ok)
|
|
}
|
|
if tc.want != got {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, got, tc.want)
|
|
}
|
|
if got+gotRest != tc.in {
|
|
t.Fatalf("test %q: %#v + %#v != %#v", tc.name, got, gotRest, tc.in)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseDisplayString(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
in string
|
|
want string
|
|
wantOk bool
|
|
}{
|
|
{
|
|
name: "valid ascii string",
|
|
in: "%\" !%22#$%25&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\"",
|
|
want: " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid lowercase non-ascii string",
|
|
in: `%"f%c3%bc%c3%bc"`,
|
|
want: "füü",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "invalid uppercase non-ascii string",
|
|
in: `%"f%C3%BC%C3%BC"`,
|
|
},
|
|
{
|
|
name: "invalid unquoted string",
|
|
in: "%foo",
|
|
},
|
|
{
|
|
name: "invalid string missing initial quote",
|
|
in: `%foo"`,
|
|
},
|
|
{
|
|
name: "invalid string missing closing quote",
|
|
in: `%"foo`,
|
|
},
|
|
{
|
|
name: "invalid tab in string",
|
|
in: "%\"\t\"",
|
|
},
|
|
{
|
|
name: "invalid newline in string",
|
|
in: "%\"\n\"",
|
|
},
|
|
{
|
|
name: "invalid single quoted string",
|
|
in: `%'foo'`,
|
|
},
|
|
{
|
|
name: "invalid string bad escaping",
|
|
in: `%\"foo %a"`,
|
|
},
|
|
{
|
|
name: "valid string with escaped quotes",
|
|
in: "%\"foo %22bar%22 \\ baz\"",
|
|
want: "foo \"bar\" \\ baz",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "invalid sequence id utf-8 string",
|
|
in: `%"%a0%a1"`,
|
|
},
|
|
{
|
|
name: "invalid 2 bytes sequence utf-8 string",
|
|
in: `%"%c3%28"`,
|
|
},
|
|
{
|
|
name: "invalid 3 bytes sequence utf-8 string",
|
|
in: `%"%e2%28%a1"`,
|
|
},
|
|
{
|
|
name: "invalid 4 bytes sequence utf-8 string",
|
|
in: `%"%f0%28%8c%28"`,
|
|
},
|
|
{
|
|
name: "invalid hex utf-8 string",
|
|
in: `%"%g0%1w"`,
|
|
},
|
|
{
|
|
name: "valid byte order mark in display string",
|
|
in: `%"BOM: %ef%bb%bf"`,
|
|
want: "BOM: \uFEFF",
|
|
wantOk: true,
|
|
},
|
|
{
|
|
name: "valid string with content after",
|
|
in: `%"foo\nbar", foo;bar`,
|
|
},
|
|
{
|
|
name: "invalid unfinished 4 bytes rune",
|
|
in: `%"%f0%9f%98"`,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
got, ok := ParseDisplayString(tc.in)
|
|
if ok != tc.wantOk {
|
|
t.Fatalf("test %q: want ok to be %v, got: %v", tc.name, tc.wantOk, ok)
|
|
}
|
|
if tc.want != got {
|
|
t.Fatalf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tc.name, got, tc.want)
|
|
}
|
|
}
|
|
}
|