diff options
Diffstat (limited to 'decode_scan_test.go')
-rw-r--r-- | decode_scan_test.go | 128 |
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 } `), |