summaryrefslogtreecommitdiff
path: root/compat/json/compat_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'compat/json/compat_test.go')
-rw-r--r--compat/json/compat_test.go241
1 files changed, 241 insertions, 0 deletions
diff --git a/compat/json/compat_test.go b/compat/json/compat_test.go
new file mode 100644
index 0000000..df9d387
--- /dev/null
+++ b/compat/json/compat_test.go
@@ -0,0 +1,241 @@
+// Copyright (C) 2023 Luke Shumaker <lukeshu@lukeshu.com>
+//
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package json
+
+import (
+ "bytes"
+ "reflect"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestCompatHTMLEscape(t *testing.T) {
+ t.Parallel()
+ type testcase struct {
+ In string
+ Out string
+ }
+ testcases := map[string]testcase{
+ "invalid": {In: `x`, Out: `x`},
+ "hex-lower": {In: `"\uabcd"`, Out: `"\uabcd"`},
+ "hex-upper": {In: `"\uABCD"`, Out: `"\uABCD"`},
+ "hex-mixed": {In: `"\uAbCd"`, Out: `"\uAbCd"`},
+ }
+ for tcName, tc := range testcases {
+ tc := tc
+ t.Run(tcName, func(t *testing.T) {
+ t.Parallel()
+ t.Logf("in=%q", tc.In)
+ var dst bytes.Buffer
+ HTMLEscape(&dst, []byte(tc.In))
+ assert.Equal(t, tc.Out, dst.String())
+ })
+ }
+}
+
+func TestCompatValid(t *testing.T) {
+ t.Parallel()
+ type testcase struct {
+ In string
+ Exp bool
+ }
+ testcases := map[string]testcase{
+ "empty": {In: ``, Exp: false},
+ "num": {In: `1`, Exp: true},
+ "trunc": {In: `{`, Exp: false},
+ "object": {In: `{}`, Exp: true},
+ "non-utf8": {In: "\"\x85\xcd\"", Exp: false}, // https://github.com/golang/go/issues/58517
+ "hex-lower": {In: `"\uabcd"`, Exp: true},
+ "hex-upper": {In: `"\uABCD"`, Exp: true},
+ "hex-mixed": {In: `"\uAbCd"`, Exp: true},
+ }
+ for tcName, tc := range testcases {
+ tc := tc
+ t.Run(tcName, func(t *testing.T) {
+ t.Parallel()
+ t.Logf("in=%q", tc.In)
+ act := Valid([]byte(tc.In))
+ assert.Equal(t, tc.Exp, act)
+ })
+ }
+}
+
+func TestCompatCompact(t *testing.T) {
+ t.Parallel()
+ type testcase struct {
+ In string
+ Out string
+ Err string
+ }
+ testcases := map[string]testcase{
+ "trunc": {In: `{`, Out: ``, Err: `unexpected end of JSON input`},
+ "object": {In: `{}`, Out: `{}`},
+ "non-utf8": {In: "\"\x85\xcd\"", Out: "\"\x85\xcd\""},
+ "float": {In: `1.200e003`, Out: `1.200e003`},
+ "hex-lower": {In: `"\uabcd"`, Out: `"\uabcd"`},
+ "hex-upper": {In: `"\uABCD"`, Out: `"\uABCD"`},
+ "hex-mixed": {In: `"\uAbCd"`, Out: `"\uAbCd"`},
+ }
+ for tcName, tc := range testcases {
+ tc := tc
+ t.Run(tcName, func(t *testing.T) {
+ t.Parallel()
+ t.Logf("in=%q", tc.In)
+ var out bytes.Buffer
+ err := Compact(&out, []byte(tc.In))
+ assert.Equal(t, tc.Out, out.String())
+ if tc.Err == "" {
+ assert.NoError(t, err)
+ } else {
+ assert.EqualError(t, err, tc.Err)
+ }
+ })
+ }
+}
+
+func TestCompatIndent(t *testing.T) {
+ t.Parallel()
+ type testcase struct {
+ In string
+ Out string
+ Err string
+ }
+ testcases := map[string]testcase{
+ "trunc": {In: `{`, Out: ``, Err: `unexpected end of JSON input`},
+ "object": {In: `{}`, Out: `{}`},
+ "non-utf8": {In: "\"\x85\xcd\"", Out: "\"\x85\xcd\""},
+ "float": {In: `1.200e003`, Out: `1.200e003`},
+ "tailws0": {In: `0`, Out: `0`},
+ "tailws1": {In: `0 `, Out: `0 `},
+ "tailws2": {In: `0 `, Out: `0 `},
+ "tailws3": {In: "0\n", Out: "0\n"},
+ "headws1": {In: ` 0`, Out: `0`},
+ "objws1": {In: `{"a" : 1}`, Out: "{\n>.\"a\": 1\n>}"},
+ "objws2": {In: "{\"a\"\n:\n1}", Out: "{\n>.\"a\": 1\n>}"},
+ "hex-lower": {In: `"\uabcd"`, Out: `"\uabcd"`},
+ "hex-upper": {In: `"\uABCD"`, Out: `"\uABCD"`},
+ "hex-mixed": {In: `"\uAbCd"`, Out: `"\uAbCd"`},
+ }
+ for tcName, tc := range testcases {
+ tc := tc
+ t.Run(tcName, func(t *testing.T) {
+ t.Parallel()
+ t.Logf("in=%q", tc.In)
+ var out bytes.Buffer
+ err := Indent(&out, []byte(tc.In), ">", ".")
+ assert.Equal(t, tc.Out, out.String())
+ if tc.Err == "" {
+ assert.NoError(t, err)
+ } else {
+ assert.EqualError(t, err, tc.Err)
+ }
+ })
+ }
+}
+
+func TestCompatMarshal(t *testing.T) {
+ t.Parallel()
+ type testcase struct {
+ In any
+ Out string
+ Err string
+ }
+ testcases := map[string]testcase{
+ "non-utf8": {In: "\x85\xcd", Out: "\"\\ufffd\\ufffd\""},
+ "urc": {In: "\ufffd", Out: "\"\ufffd\""},
+ "float": {In: 1.2e3, Out: `1200`},
+ }
+ for tcName, tc := range testcases {
+ tc := tc
+ t.Run(tcName, func(t *testing.T) {
+ t.Parallel()
+ out, err := Marshal(tc.In)
+ assert.Equal(t, tc.Out, string(out))
+ if tc.Err == "" {
+ assert.NoError(t, err)
+ } else {
+ assert.EqualError(t, err, tc.Err)
+ }
+ })
+ }
+}
+
+func TestCompatUnmarshal(t *testing.T) {
+ t.Parallel()
+ type testcase struct {
+ In string
+ InPtr any
+ ExpOut any
+ ExpErr string
+ }
+ testcases := map[string]testcase{
+ "empty-obj": {In: `{}`, ExpOut: map[string]any{}},
+ "partial-obj": {In: `{"foo":"bar",`, ExpOut: nil, ExpErr: `unexpected end of JSON input`},
+ "existing-obj": {In: `{"baz":"quz"}`, InPtr: &map[string]string{"foo": "bar"}, ExpOut: map[string]string{"foo": "bar", "baz": "quz"}},
+ "existing-obj-partial": {In: `{"baz":"quz"`, InPtr: &map[string]string{"foo": "bar"}, ExpOut: map[string]string{"foo": "bar"}, ExpErr: "unexpected end of JSON input"},
+ "empty-ary": {In: `[]`, ExpOut: []any{}},
+ "two-objs": {In: `{} {}`, ExpOut: nil, ExpErr: `invalid character '{' after top-level value`},
+ "two-numbers1": {In: `00`, ExpOut: nil, ExpErr: `invalid character '0' after top-level value`},
+ "two-numbers2": {In: `1 2`, ExpOut: nil, ExpErr: `invalid character '2' after top-level value`},
+ }
+ for tcName, tc := range testcases {
+ tc := tc
+ t.Run(tcName, func(t *testing.T) {
+ t.Parallel()
+ ptr := tc.InPtr
+ if ptr == nil {
+ var out any
+ ptr = &out
+ }
+ err := Unmarshal([]byte(tc.In), ptr)
+ assert.Equal(t, tc.ExpOut, reflect.ValueOf(ptr).Elem().Interface())
+ if tc.ExpErr == "" {
+ assert.NoError(t, err)
+ } else {
+ assert.EqualError(t, err, tc.ExpErr)
+ }
+ })
+ }
+}
+
+func TestCompatDecode(t *testing.T) {
+ t.Parallel()
+ type testcase struct {
+ In string
+ InPtr any
+ ExpOut any
+ ExpErr string
+ }
+ testcases := map[string]testcase{
+ "empty-obj": {In: `{}`, ExpOut: map[string]any{}},
+ "partial-obj": {In: `{"foo":"bar",`, ExpOut: nil, ExpErr: `unexpected EOF`},
+ "existing-obj": {In: `{"baz":"quz"}`, InPtr: &map[string]string{"foo": "bar"}, ExpOut: map[string]string{"foo": "bar", "baz": "quz"}},
+ "existing-obj-partial": {In: `{"baz":"quz"`, InPtr: &map[string]string{"foo": "bar"}, ExpOut: map[string]string{"foo": "bar"}, ExpErr: "unexpected EOF"},
+ "empty-ary": {In: `[]`, ExpOut: []any{}},
+ "two-objs": {In: `{} {}`, ExpOut: map[string]any{}},
+ "two-numbers1": {In: `00`, ExpOut: float64(0)},
+ "two-numbers2": {In: `1 2`, ExpOut: float64(1)},
+ }
+ for tcName, tc := range testcases {
+ tc := tc
+ t.Run(tcName, func(t *testing.T) {
+ t.Parallel()
+ ptr := tc.InPtr
+ if ptr == nil {
+ var out any
+ ptr = &out
+ }
+ err := NewDecoder(strings.NewReader(tc.In)).Decode(ptr)
+ assert.Equal(t, tc.ExpOut, reflect.ValueOf(ptr).Elem().Interface())
+ if tc.ExpErr == "" {
+ assert.NoError(t, err)
+ } else {
+ assert.EqualError(t, err, tc.ExpErr)
+ }
+ })
+ }
+}