...

Source file src/sigs.k8s.io/json/json_test.go

Documentation: sigs.k8s.io/json

     1  /*
     2  Copyright 2021 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package json
    18  
    19  import (
    20  	"bytes"
    21  	gojson "encoding/json"
    22  	"io/ioutil"
    23  	"reflect"
    24  	"strings"
    25  	"testing"
    26  )
    27  
    28  func TestSyntaxErrorOffset(t *testing.T) {
    29  	malformedJSON :=
    30  		[]byte(`{
    31  	"test1":true,
    32  	"test2":true
    33  	"test3":true
    34  }`)
    35  
    36  	err1 := UnmarshalCaseSensitivePreserveInts(malformedJSON, &map[string]interface{}{})
    37  	if err1 == nil {
    38  		t.Fatal("expected err, got none")
    39  	}
    40  	ok1, offset1 := SyntaxErrorOffset(err1)
    41  	if !ok1 {
    42  		t.Fatal("expected ok, got false")
    43  	}
    44  
    45  	err2 := gojson.Unmarshal(malformedJSON, &map[string]interface{}{})
    46  	if err2 == nil {
    47  		t.Fatal("expected err, got none")
    48  	}
    49  	ok2, offset2 := SyntaxErrorOffset(err2)
    50  	if !ok2 {
    51  		t.Fatal("expected ok, got false")
    52  	}
    53  	if offset1 != offset2 {
    54  		t.Fatalf("offset mismatch from stdlib and custom: %d != %d", offset1, offset2)
    55  	}
    56  }
    57  
    58  func TestUnmarshal(t *testing.T) {
    59  	type Obj struct {
    60  		A int               `json:"a"`
    61  		B int               `json:"b"`
    62  		C map[string]string `json:"c"`
    63  		D int
    64  		E int
    65  	}
    66  
    67  	testcases := []struct {
    68  		name             string
    69  		in               string
    70  		to               func() interface{}
    71  		expect           interface{}
    72  		expectErr        string
    73  		expectStrictErrs []string
    74  	}{
    75  		{
    76  			name:   "simple",
    77  			in:     `{"a":1}`,
    78  			to:     func() interface{} { return map[string]interface{}{} },
    79  			expect: map[string]interface{}{"a": int64(1)},
    80  		},
    81  		{
    82  			name:             "case-sensitive",
    83  			in:               `{"a":1,"A":2,"B":3}`,
    84  			to:               func() interface{} { return &Obj{} },
    85  			expect:           &Obj{A: 1},                                         // case-mismatches don't decode
    86  			expectStrictErrs: []string{`unknown field "A"`, `unknown field "B"`}, // multiple strict errors are returned
    87  		},
    88  		{
    89  			name:             "duplicate untyped",
    90  			in:               `{"a":1,"a":2,"b":1,"b":2}`,
    91  			to:               func() interface{} { return map[string]interface{}{} },
    92  			expect:           map[string]interface{}{"a": int64(2), "b": int64(2)},   // last duplicates win
    93  			expectStrictErrs: []string{`duplicate field "a"`, `duplicate field "b"`}, // multiple strict errors are returned
    94  		},
    95  		{
    96  			name:             "duplicate typed",
    97  			in:               `{"a":1,"a":2,"b":1,"b":2}`,
    98  			to:               func() interface{} { return &Obj{} },
    99  			expect:           &Obj{A: 2, B: 2},                                       // last duplicates win
   100  			expectStrictErrs: []string{`duplicate field "a"`, `duplicate field "b"`}, // multiple strict errors are returned
   101  		},
   102  		{
   103  			name:             "duplicate map field",
   104  			in:               `{"c":{"a":"1","a":"2","b":"1","b":"2"}}`,
   105  			to:               func() interface{} { return &Obj{} },
   106  			expect:           &Obj{C: map[string]string{"a": "2", "b": "2"}},             // last duplicates win
   107  			expectStrictErrs: []string{`duplicate field "c.a"`, `duplicate field "c.b"`}, // multiple strict errors are returned
   108  		},
   109  		{
   110  			name:             "unknown fields",
   111  			in:               `{"a":1,"unknown":true,"unknown2":false,"b":2}`,
   112  			to:               func() interface{} { return &Obj{} },
   113  			expect:           &Obj{A: 1, B: 2},                                                // data is populated
   114  			expectStrictErrs: []string{`unknown field "unknown"`, `unknown field "unknown2"`}, // multiple strict errors are returned
   115  		},
   116  	}
   117  
   118  	for _, tc := range testcases {
   119  		t.Run(tc.name, func(t *testing.T) {
   120  			unmarshalTo := tc.to()
   121  			err := UnmarshalCaseSensitivePreserveInts([]byte(tc.in), &unmarshalTo)
   122  
   123  			strictUnmarshalTo := tc.to()
   124  			strictErrors, strictErr := UnmarshalStrict([]byte(tc.in), &strictUnmarshalTo)
   125  
   126  			decodeTo := tc.to()
   127  			decodeErr := NewDecoderCaseSensitivePreserveInts(bytes.NewBuffer([]byte(tc.in))).Decode(&decodeTo)
   128  
   129  			// ensure expected errors are returned
   130  			if (len(tc.expectErr) > 0) != (err != nil) {
   131  				t.Fatalf("expected err=%v, got %v", len(tc.expectErr) > 0, err)
   132  			}
   133  			if len(tc.expectErr) > 0 && !strings.Contains(err.Error(), tc.expectErr) {
   134  				t.Fatalf("expected error containing '%s', got %v", tc.expectErr, err)
   135  			}
   136  
   137  			// ensure expected strict errors are returned
   138  			if len(tc.expectStrictErrs) != len(strictErrors) {
   139  				t.Fatalf("expected %d strict errors, got %v", len(tc.expectStrictErrs), strictErrors)
   140  			}
   141  			for i := range tc.expectStrictErrs {
   142  				strictFieldErr, ok := strictErrors[i].(FieldError)
   143  				if !ok {
   144  					t.Fatalf("strict error does not implement FieldError: %v", strictErrors[i])
   145  				}
   146  				if !strings.Contains(strictFieldErr.Error(), tc.expectStrictErrs[i]) {
   147  					t.Fatalf("expected strict errors:\n  %s\ngot:\n  %v", strings.Join(tc.expectStrictErrs, "\n  "), strictErrors)
   148  				}
   149  			}
   150  
   151  			// ensure expected decode errors are returned
   152  			if (len(tc.expectErr) > 0) != (decodeErr != nil) {
   153  				t.Fatalf("expected err=%v, got %v", len(tc.expectErr) > 0, decodeErr)
   154  			}
   155  			if len(tc.expectErr) > 0 && !strings.Contains(decodeErr.Error(), tc.expectErr) {
   156  				t.Fatalf("expected error containing '%s', got %v", tc.expectErr, decodeErr)
   157  			}
   158  
   159  			// ensure we got the expected object back
   160  			if !reflect.DeepEqual(tc.expect, unmarshalTo) {
   161  				t.Fatalf("expected\n%#v\ngot\n%#v", tc.expect, unmarshalTo)
   162  			}
   163  			if !reflect.DeepEqual(tc.expect, decodeTo) {
   164  				t.Fatalf("expected\n%#v\ngot\n%#v", tc.expect, decodeTo)
   165  			}
   166  
   167  			// ensure Unmarshal and UnmarshalStrict return identical errors and objects
   168  			if !reflect.DeepEqual(err, strictErr) {
   169  				t.Fatalf("unmarshal/strictunmarshal returned different errors:\n%v\n%v", err, strictErr)
   170  			}
   171  			if !reflect.DeepEqual(unmarshalTo, strictUnmarshalTo) {
   172  				t.Fatalf("unmarshal/strictunmarshal returned different objects:\n%#v\n%#v", unmarshalTo, strictUnmarshalTo)
   173  			}
   174  
   175  			// ensure Unmarshal and Decode return identical errors and objects
   176  			if !reflect.DeepEqual(err, decodeErr) {
   177  				t.Fatalf("unmarshal/decode returned different errors:\n%v\n%v", err, decodeErr)
   178  			}
   179  			if !reflect.DeepEqual(unmarshalTo, decodeTo) {
   180  				t.Fatalf("unmarshal/decode returned different objects:\n%#v\n%#v", unmarshalTo, decodeTo)
   181  			}
   182  		})
   183  	}
   184  }
   185  
   186  func BenchmarkUnmarshal(b *testing.B) {
   187  	testcases := []struct {
   188  		name      string
   189  		unmarshal func(b *testing.B, data []byte, v interface{})
   190  	}{
   191  		{
   192  			name: "stdlib",
   193  			unmarshal: func(b *testing.B, data []byte, v interface{}) {
   194  				if err := gojson.Unmarshal(data, v); err != nil {
   195  					b.Fatal(err)
   196  				}
   197  			},
   198  		},
   199  		{
   200  			name: "unmarshal",
   201  			unmarshal: func(b *testing.B, data []byte, v interface{}) {
   202  				if err := UnmarshalCaseSensitivePreserveInts(data, v); err != nil {
   203  					b.Fatal(err)
   204  				}
   205  			},
   206  		},
   207  		{
   208  			name: "strict",
   209  			unmarshal: func(b *testing.B, data []byte, v interface{}) {
   210  				if strict, err := UnmarshalStrict(data, v); err != nil {
   211  					b.Fatal(err)
   212  				} else if len(strict) > 0 {
   213  					b.Fatal(strict)
   214  				}
   215  			},
   216  		},
   217  		{
   218  			name: "strict-custom",
   219  			unmarshal: func(b *testing.B, data []byte, v interface{}) {
   220  				if strict, err := UnmarshalStrict(data, v, DisallowDuplicateFields, DisallowUnknownFields); err != nil {
   221  					b.Fatal(err)
   222  				} else if len(strict) > 0 {
   223  					b.Fatal(strict)
   224  				}
   225  			},
   226  		},
   227  	}
   228  
   229  	data, err := ioutil.ReadFile("testdata/bench.json")
   230  	if err != nil {
   231  		b.Fatal(err)
   232  	}
   233  	b.ResetTimer()
   234  
   235  	for _, tc := range testcases {
   236  		b.Run("typed_"+tc.name, func(b *testing.B) {
   237  			for i := 0; i < b.N; i++ {
   238  				tc.unmarshal(b, data, &A{})
   239  			}
   240  		})
   241  	}
   242  	for _, tc := range testcases {
   243  		b.Run("untyped_"+tc.name, func(b *testing.B) {
   244  			for i := 0; i < b.N; i++ {
   245  				tc.unmarshal(b, data, &map[string]interface{}{})
   246  			}
   247  		})
   248  	}
   249  }
   250  
   251  type A struct {
   252  	Int    int    `json:"int"`
   253  	Bool   bool   `json:"bool"`
   254  	String string `json:"string"`
   255  
   256  	StringMap   map[string]string `json:"map"`
   257  	ObjectArray []A               `json:"array"`
   258  
   259  	Small Small `json:"small"`
   260  	Big   Big   `json:"big"`
   261  
   262  	Custom Custom `json:"custom"`
   263  }
   264  
   265  type Small struct {
   266  	F01 string `json:"f01"`
   267  	F02 string `json:"f02"`
   268  	F03 string `json:"f03"`
   269  	F04 string `json:"f04"`
   270  	F05 string `json:"f05"`
   271  	F06 string `json:"f06"`
   272  	F07 string `json:"f07"`
   273  	F08 string `json:"f08"`
   274  	F09 string `json:"f09"`
   275  	F10 string `json:"f10"`
   276  	F11 string `json:"f11"`
   277  	F12 string `json:"f12"`
   278  	F13 string `json:"f13"`
   279  	F14 string `json:"f14"`
   280  	F15 string `json:"f15"`
   281  	F16 string `json:"f16"`
   282  	F17 string `json:"f17"`
   283  	F18 string `json:"f18"`
   284  	F19 string `json:"f19"`
   285  	F20 string `json:"f20"`
   286  	F21 string `json:"f21"`
   287  	F22 string `json:"f22"`
   288  	F23 string `json:"f23"`
   289  	F24 string `json:"f24"`
   290  	F25 string `json:"f25"`
   291  	F26 string `json:"f26"`
   292  	F27 string `json:"f27"`
   293  	F28 string `json:"f28"`
   294  	F29 string `json:"f29"`
   295  	F30 string `json:"f30"`
   296  	F31 string `json:"f31"`
   297  	F32 string `json:"f32"`
   298  	F33 string `json:"f33"`
   299  	F34 string `json:"f34"`
   300  	F35 string `json:"f35"`
   301  	F36 string `json:"f36"`
   302  	F37 string `json:"f37"`
   303  	F38 string `json:"f38"`
   304  	F39 string `json:"f39"`
   305  	F40 string `json:"f40"`
   306  	F41 string `json:"f41"`
   307  	F42 string `json:"f42"`
   308  	F43 string `json:"f43"`
   309  	F44 string `json:"f44"`
   310  	F45 string `json:"f45"`
   311  	F46 string `json:"f46"`
   312  	F47 string `json:"f47"`
   313  	F48 string `json:"f48"`
   314  	F49 string `json:"f49"`
   315  	F50 string `json:"f50"`
   316  	F51 string `json:"f51"`
   317  	F52 string `json:"f52"`
   318  	F53 string `json:"f53"`
   319  	F54 string `json:"f54"`
   320  	F55 string `json:"f55"`
   321  	F56 string `json:"f56"`
   322  	F57 string `json:"f57"`
   323  	F58 string `json:"f58"`
   324  	F59 string `json:"f59"`
   325  	F60 string `json:"f60"`
   326  	F61 string `json:"f61"`
   327  	F62 string `json:"f62"`
   328  	F63 string `json:"f63"`
   329  	F64 string `json:"f64"`
   330  }
   331  
   332  type Big struct {
   333  	F01 string `json:"f01"`
   334  	F02 string `json:"f02"`
   335  	F03 string `json:"f03"`
   336  	F04 string `json:"f04"`
   337  	F05 string `json:"f05"`
   338  	F06 string `json:"f06"`
   339  	F07 string `json:"f07"`
   340  	F08 string `json:"f08"`
   341  	F09 string `json:"f09"`
   342  	F10 string `json:"f10"`
   343  	F11 string `json:"f11"`
   344  	F12 string `json:"f12"`
   345  	F13 string `json:"f13"`
   346  	F14 string `json:"f14"`
   347  	F15 string `json:"f15"`
   348  	F16 string `json:"f16"`
   349  	F17 string `json:"f17"`
   350  	F18 string `json:"f18"`
   351  	F19 string `json:"f19"`
   352  	F20 string `json:"f20"`
   353  	F21 string `json:"f21"`
   354  	F22 string `json:"f22"`
   355  	F23 string `json:"f23"`
   356  	F24 string `json:"f24"`
   357  	F25 string `json:"f25"`
   358  	F26 string `json:"f26"`
   359  	F27 string `json:"f27"`
   360  	F28 string `json:"f28"`
   361  	F29 string `json:"f29"`
   362  	F30 string `json:"f30"`
   363  	F31 string `json:"f31"`
   364  	F32 string `json:"f32"`
   365  	F33 string `json:"f33"`
   366  	F34 string `json:"f34"`
   367  	F35 string `json:"f35"`
   368  	F36 string `json:"f36"`
   369  	F37 string `json:"f37"`
   370  	F38 string `json:"f38"`
   371  	F39 string `json:"f39"`
   372  	F40 string `json:"f40"`
   373  	F41 string `json:"f41"`
   374  	F42 string `json:"f42"`
   375  	F43 string `json:"f43"`
   376  	F44 string `json:"f44"`
   377  	F45 string `json:"f45"`
   378  	F46 string `json:"f46"`
   379  	F47 string `json:"f47"`
   380  	F48 string `json:"f48"`
   381  	F49 string `json:"f49"`
   382  	F50 string `json:"f50"`
   383  	F51 string `json:"f51"`
   384  	F52 string `json:"f52"`
   385  	F53 string `json:"f53"`
   386  	F54 string `json:"f54"`
   387  	F55 string `json:"f55"`
   388  	F56 string `json:"f56"`
   389  	F57 string `json:"f57"`
   390  	F58 string `json:"f58"`
   391  	F59 string `json:"f59"`
   392  	F60 string `json:"f60"`
   393  	F61 string `json:"f61"`
   394  	F62 string `json:"f62"`
   395  	F63 string `json:"f63"`
   396  	F64 string `json:"f64"`
   397  	F65 string `json:"f65"`
   398  }
   399  
   400  type Custom struct{}
   401  
   402  func (c *Custom) UnmarshalJSON(data []byte) error {
   403  	return nil
   404  }
   405  

View as plain text