summaryrefslogtreecommitdiff
path: root/decode_scan_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'decode_scan_test.go')
-rw-r--r--decode_scan_test.go128
1 files changed, 117 insertions, 11 deletions
diff --git a/decode_scan_test.go b/decode_scan_test.go
index 8bc33e3..6fd9369 100644
--- a/decode_scan_test.go
+++ b/decode_scan_test.go
@@ -26,14 +26,16 @@ func (r ReadRuneTypeResult) String() string {
}
type runeTypeScannerTestcase struct {
- Input string
- Exp []ReadRuneTypeResult
+ Input string
+ ExpRemainder string
+ Exp []ReadRuneTypeResult
}
func testRuneTypeScanner(t *testing.T, testcases map[string]runeTypeScannerTestcase, factory func(io.RuneReader) runeTypeScanner) {
for tcName, tc := range testcases {
t.Run(tcName, func(t *testing.T) {
- sc := factory(strings.NewReader(tc.Input))
+ reader := strings.NewReader(tc.Input)
+ sc := factory(reader)
var exp, act []string
for _, iExp := range tc.Exp {
var iAct ReadRuneTypeResult
@@ -47,13 +49,14 @@ func testRuneTypeScanner(t *testing.T, testcases map[string]runeTypeScannerTestc
act = append(act, iAct.String())
}
assert.Equal(t, exp, act)
+ assert.Equal(t, tc.ExpRemainder, tc.Input[len(tc.Input)-reader.Len():])
})
}
}
func TestRuneTypeScanner(t *testing.T) {
testcases := map[string]runeTypeScannerTestcase{
- "basic": {`{"foo": 12.0}`, []ReadRuneTypeResult{
+ "basic": {`{"foo": 12.0}`, ``, []ReadRuneTypeResult{
{'{', 1, RuneTypeObjectBeg, nil},
{'"', 1, RuneTypeStringBeg, nil},
{'f', 1, RuneTypeStringChar, nil},
@@ -70,7 +73,7 @@ func TestRuneTypeScanner(t *testing.T) {
{0, 0, RuneTypeEOF, nil},
{0, 0, RuneTypeEOF, nil},
}},
- "unread": {`{"foo": 12.0}`, []ReadRuneTypeResult{
+ "unread": {`{"foo": 12.0}`, ``, []ReadRuneTypeResult{
{'{', 1, RuneTypeObjectBeg, nil},
{'"', 1, RuneTypeStringBeg, nil},
{'f', 1, RuneTypeStringChar, nil},
@@ -89,7 +92,7 @@ func TestRuneTypeScanner(t *testing.T) {
{0, 0, RuneTypeEOF, nil},
{0, 0, RuneTypeEOF, nil},
}},
- "unread2": {`{"foo": 12.0}`, []ReadRuneTypeResult{
+ "unread2": {`{"foo": 12.0}`, ``, []ReadRuneTypeResult{
{'{', 1, RuneTypeObjectBeg, nil},
{'"', 1, RuneTypeStringBeg, nil},
{'f', 1, RuneTypeStringChar, nil},
@@ -109,7 +112,7 @@ func TestRuneTypeScanner(t *testing.T) {
{0, 0, RuneTypeEOF, nil},
{0, 0, RuneTypeEOF, nil},
}},
- "unread-eof": {`{"foo": 12.0}`, []ReadRuneTypeResult{
+ "unread-eof": {`{"foo": 12.0}`, ``, []ReadRuneTypeResult{
{'{', 1, RuneTypeObjectBeg, nil},
{'"', 1, RuneTypeStringBeg, nil},
{'f', 1, RuneTypeStringChar, nil},
@@ -128,12 +131,26 @@ func TestRuneTypeScanner(t *testing.T) {
{0, 0, RuneTypeEOF, nil},
{0, 0, RuneTypeEOF, nil},
}},
- "syntax-error": {`[[0,]`, []ReadRuneTypeResult{
+ "syntax-error": {`[[0,]`, ``, []ReadRuneTypeResult{
{'[', 1, RuneTypeArrayBeg, nil},
{'[', 1, RuneTypeArrayBeg, nil},
{'0', 1, RuneTypeNumberIntZero, nil},
{',', 1, RuneTypeArrayComma, nil},
{']', 1, RuneTypeError, &DecodeSyntaxError{Offset: 5, Err: fmt.Errorf("invalid character %q looking for beginning of value", ']')}},
+ {']', 1, RuneTypeError, &DecodeSyntaxError{Offset: 5, Err: fmt.Errorf("invalid character %q looking for beginning of value", ']')}},
+ {']', 1, RuneTypeError, &DecodeSyntaxError{Offset: 5, Err: fmt.Errorf("invalid character %q looking for beginning of value", ']')}},
+ }},
+ "multi-value": {`1{}`, `}`, []ReadRuneTypeResult{
+ {'1', 1, RuneTypeNumberIntDig, nil},
+ {'{', 1, RuneTypeEOF, nil},
+ {'{', 1, RuneTypeEOF, nil},
+ {'{', 1, RuneTypeEOF, nil},
+ }},
+ "early-eof": {`{`, ``, []ReadRuneTypeResult{
+ {'{', 1, RuneTypeObjectBeg, nil},
+ {0, 0, RuneTypeError, &DecodeSyntaxError{Offset: 1, Err: io.ErrUnexpectedEOF}},
+ {0, 0, RuneTypeError, &DecodeSyntaxError{Offset: 1, Err: io.ErrUnexpectedEOF}},
+ {0, 0, RuneTypeError, &DecodeSyntaxError{Offset: 1, Err: io.ErrUnexpectedEOF}},
}},
}
testRuneTypeScanner(t, testcases, func(reader io.RuneReader) runeTypeScanner {
@@ -145,7 +162,7 @@ func TestRuneTypeScanner(t *testing.T) {
func TestNoWSRuneTypeScanner(t *testing.T) {
testcases := map[string]runeTypeScannerTestcase{
- "basic": {`{"foo": 12.0}`, []ReadRuneTypeResult{
+ "basic": {`{"foo": 12.0}`, ``, []ReadRuneTypeResult{
{'{', 1, RuneTypeObjectBeg, nil},
{'"', 1, RuneTypeStringBeg, nil},
{'f', 1, RuneTypeStringChar, nil},
@@ -161,7 +178,7 @@ func TestNoWSRuneTypeScanner(t *testing.T) {
{0, 0, RuneTypeEOF, nil},
{0, 0, RuneTypeEOF, nil},
}},
- "unread": {`{"foo": 12.0}`, []ReadRuneTypeResult{
+ "unread": {`{"foo": 12.0}`, ``, []ReadRuneTypeResult{
{'{', 1, RuneTypeObjectBeg, nil},
{'"', 1, RuneTypeStringBeg, nil},
{'f', 1, RuneTypeStringChar, nil},
@@ -179,7 +196,7 @@ func TestNoWSRuneTypeScanner(t *testing.T) {
{0, 0, RuneTypeEOF, nil},
{0, 0, RuneTypeEOF, nil},
}},
- "tail": {`{"foo": 12.0} `, []ReadRuneTypeResult{
+ "tail": {`{"foo": 12.0} `, ``, []ReadRuneTypeResult{
{'{', 1, RuneTypeObjectBeg, nil},
{'"', 1, RuneTypeStringBeg, nil},
{'f', 1, RuneTypeStringChar, nil},
@@ -195,6 +212,18 @@ func TestNoWSRuneTypeScanner(t *testing.T) {
{0, 0, RuneTypeEOF, nil},
{0, 0, RuneTypeEOF, nil},
}},
+ "multi-value": {`1{}`, `}`, []ReadRuneTypeResult{
+ {'1', 1, RuneTypeNumberIntDig, nil},
+ {'{', 1, RuneTypeEOF, nil},
+ {'{', 1, RuneTypeEOF, nil},
+ {'{', 1, RuneTypeEOF, nil},
+ }},
+ "early-eof": {` {`, ``, []ReadRuneTypeResult{
+ {'{', 1, RuneTypeObjectBeg, nil},
+ {0, 0, RuneTypeError, &DecodeSyntaxError{Offset: 2, Err: io.ErrUnexpectedEOF}},
+ {0, 0, RuneTypeError, &DecodeSyntaxError{Offset: 2, Err: io.ErrUnexpectedEOF}},
+ {0, 0, RuneTypeError, &DecodeSyntaxError{Offset: 2, Err: io.ErrUnexpectedEOF}},
+ }},
}
testRuneTypeScanner(t, testcases, func(reader io.RuneReader) runeTypeScanner {
return &noWSRuneTypeScanner{
@@ -206,6 +235,83 @@ func TestNoWSRuneTypeScanner(t *testing.T) {
}
func TestElemRuneTypeScanner(t *testing.T) {
+ testcases := map[string]runeTypeScannerTestcase{
+ "basic": {`1`, ``, []ReadRuneTypeResult{
+ {'1', 1, RuneTypeNumberIntDig, nil},
+ {0, 0, RuneTypeEOF, nil},
+ {0, 0, RuneTypeEOF, nil},
+ {0, 0, RuneTypeEOF, nil},
+ }},
+ "syntax-error": {`[[0,]`, ``, []ReadRuneTypeResult{
+ {'[', 1, RuneTypeArrayBeg, nil},
+ {'[', 1, RuneTypeArrayBeg, nil},
+ {'0', 1, RuneTypeNumberIntZero, nil},
+ {',', 1, RuneTypeArrayComma, nil},
+ {']', 1, RuneTypeError, &DecodeSyntaxError{Offset: 5, Err: fmt.Errorf("invalid character %q looking for beginning of value", ']')}},
+ {']', 1, RuneTypeError, &DecodeSyntaxError{Offset: 5, Err: fmt.Errorf("invalid character %q looking for beginning of value", ']')}},
+ {']', 1, RuneTypeError, &DecodeSyntaxError{Offset: 5, Err: fmt.Errorf("invalid character %q looking for beginning of value", ']')}},
+ }},
+ "multi-value": {`1{}`, `{}`, []ReadRuneTypeResult{
+ {'1', 1, RuneTypeNumberIntDig, nil},
+ {0, 0, RuneTypeEOF, nil},
+ {0, 0, RuneTypeEOF, nil},
+ {0, 0, RuneTypeEOF, nil},
+ }},
+ "fragment": {`1,`, `,`, []ReadRuneTypeResult{
+ {'1', 1, RuneTypeNumberIntDig, nil},
+ {0, 0, RuneTypeEOF, nil},
+ {0, 0, RuneTypeEOF, nil},
+ {0, 0, RuneTypeEOF, nil},
+ }},
+ "early-eof": {`{`, ``, []ReadRuneTypeResult{
+ {'{', 1, RuneTypeObjectBeg, nil},
+ {0, 0, RuneTypeError, &DecodeSyntaxError{Offset: 1, Err: io.ErrUnexpectedEOF}},
+ {0, 0, RuneTypeError, &DecodeSyntaxError{Offset: 1, Err: io.ErrUnexpectedEOF}},
+ {0, 0, RuneTypeError, &DecodeSyntaxError{Offset: 1, Err: io.ErrUnexpectedEOF}},
+ }},
+ }
+ t.Run("top-level", func(t *testing.T) {
+ testRuneTypeScanner(t, testcases, func(reader io.RuneReader) runeTypeScanner {
+ return &elemRuneTypeScanner{
+ inner: &noWSRuneTypeScanner{
+ inner: &runeTypeScannerImpl{
+ inner: reader,
+ },
+ },
+ }
+ })
+ })
+
+ for tcName, tc := range testcases {
+ tc.Input = `[` + tc.Input
+ for _, res := range tc.Exp {
+ if se, ok := res.e.(*DecodeSyntaxError); ok {
+ se.Offset++
+ }
+ }
+ testcases[tcName] = tc
+ }
+ t.Run("child", func(t *testing.T) {
+ testRuneTypeScanner(t, testcases, func(reader io.RuneReader) runeTypeScanner {
+ inner := &noWSRuneTypeScanner{
+ inner: &runeTypeScannerImpl{
+ inner: reader,
+ },
+ }
+ var res ReadRuneTypeResult
+ res.r, res.s, res.t, res.e = inner.ReadRuneType()
+ require.Equal(t,
+ ReadRuneTypeResult{'[', 1, RuneTypeArrayBeg, nil}.String(),
+ res.String())
+
+ return &elemRuneTypeScanner{
+ inner: inner,
+ }
+ })
+ })
+}
+
+func TestElemRuneTypeScanner2(t *testing.T) {
parent := &noWSRuneTypeScanner{
inner: &runeTypeScannerImpl{
inner: strings.NewReader(` { "foo" : 12.0 } `),