]> Cypherpunks repositories - gostls13.git/commitdiff
encoding/json: check exact structure of local error types in tests
authorJoe Tsai <joetsai@digital-static.net>
Tue, 19 Nov 2024 01:34:06 +0000 (17:34 -0800)
committerGopher Robot <gobot@golang.org>
Tue, 19 Nov 2024 18:48:19 +0000 (18:48 +0000)
During the development of error wrapping (#29934),
the tests were modified to stop using reflect.DeepEqual
since the prototype for error wrapping at the time included
frame information of where the error was created.

However, that change diminished the fidelity of the test
so that it is no longer as strict, which affects the endeavor
to implement v1 in terms of the v2 prototype.

For locally declared error types, use reflect.DeepEqual
to check that the exact structure of the error value matches.

Change-Id: I443d418533866ab8d533bca3785fdc741e2c140e
Reviewed-on: https://go-review.googlesource.com/c/go/+/629517
Auto-Submit: Damien Neil <dneil@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
Reviewed-by: Damien Neil <dneil@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

src/encoding/json/decode_test.go

index 71895a9bb14d39dbe6a8f36db990aceb91a92b12..de09fae50f09d1315acb7960472ff6cc42f033f9 100644 (file)
@@ -443,7 +443,7 @@ var unmarshalTests = []struct {
        {CaseName: Name(""), in: `{"x": 1}`, ptr: new(tx), out: tx{}},
        {CaseName: Name(""), in: `{"x": 1}`, ptr: new(tx), err: fmt.Errorf("json: unknown field \"x\""), disallowUnknownFields: true},
        {CaseName: Name(""), in: `{"S": 23}`, ptr: new(W), out: W{}, err: &UnmarshalTypeError{"number", reflect.TypeFor[SS](), 0, "W", "S"}},
-       {CaseName: Name(""), in: `{"T": {"X": 23}}`, ptr: new(TOuter), out: TOuter{}, err: &UnmarshalTypeError{"number", reflect.TypeFor[string](), 0, "TOuter", "T.X"}},
+       {CaseName: Name(""), in: `{"T": {"X": 23}}`, ptr: new(TOuter), out: TOuter{}, err: &UnmarshalTypeError{"number", reflect.TypeFor[string](), 8, "TOuter", "T.X"}},
        {CaseName: Name(""), in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: float64(1), F2: int32(2), F3: Number("3")}},
        {CaseName: Name(""), in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: Number("1"), F2: int32(2), F3: Number("3")}, useNumber: true},
        {CaseName: Name(""), in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(any), out: ifaceNumAsFloat64},
@@ -907,7 +907,7 @@ var unmarshalTests = []struct {
                        Struct: "Top",
                        Field:  "Embed0a.Level1a",
                        Type:   reflect.TypeFor[int](),
-                       Offset: 10,
+                       Offset: 19,
                },
        },
 
@@ -1029,7 +1029,7 @@ var unmarshalTests = []struct {
                        Struct: "T",
                        Field:  "Ts.Y",
                        Type:   reflect.TypeFor[int](),
-                       Offset: 29,
+                       Offset: 44,
                },
        },
        // #14702
@@ -1170,9 +1170,28 @@ func TestMarshalEmbeds(t *testing.T) {
 }
 
 func equalError(a, b error) bool {
+       isJSONError := func(err error) bool {
+               switch err.(type) {
+               case
+                       *InvalidUTF8Error,
+                       *InvalidUnmarshalError,
+                       *MarshalerError,
+                       *SyntaxError,
+                       *UnmarshalFieldError,
+                       *UnmarshalTypeError,
+                       *UnsupportedTypeError,
+                       *UnsupportedValueError:
+                       return true
+               }
+               return false
+       }
+
        if a == nil || b == nil {
                return a == nil && b == nil
        }
+       if isJSONError(a) || isJSONError(b) {
+               return reflect.DeepEqual(a, b) // safe for locally defined error types
+       }
        return a.Error() == b.Error()
 }
 
@@ -1217,7 +1236,7 @@ func TestUnmarshal(t *testing.T) {
                                dec.DisallowUnknownFields()
                        }
                        if err := dec.Decode(v.Interface()); !equalError(err, tt.err) {
-                               t.Fatalf("%s: Decode error:\n\tgot:  %v\n\twant: %v", tt.Where, err, tt.err)
+                               t.Fatalf("%s: Decode error:\n\tgot:  %#v\n\twant: %#v", tt.Where, err, tt.err)
                        } else if err != nil {
                                return
                        }
@@ -2222,49 +2241,27 @@ func TestPrefilled(t *testing.T) {
 }
 
 func TestInvalidUnmarshal(t *testing.T) {
-       buf := []byte(`{"a":"1"}`)
        tests := []struct {
                CaseName
-               v    any
-               want string
+               in      string
+               v       any
+               wantErr error
        }{
-               {Name(""), nil, "json: Unmarshal(nil)"},
-               {Name(""), struct{}{}, "json: Unmarshal(non-pointer struct {})"},
-               {Name(""), (*int)(nil), "json: Unmarshal(nil *int)"},
+               {Name(""), `{"a":"1"}`, nil, &InvalidUnmarshalError{}},
+               {Name(""), `{"a":"1"}`, struct{}{}, &InvalidUnmarshalError{reflect.TypeFor[struct{}]()}},
+               {Name(""), `{"a":"1"}`, (*int)(nil), &InvalidUnmarshalError{reflect.TypeFor[*int]()}},
+               {Name(""), `123`, nil, &InvalidUnmarshalError{}},
+               {Name(""), `123`, struct{}{}, &InvalidUnmarshalError{reflect.TypeFor[struct{}]()}},
+               {Name(""), `123`, (*int)(nil), &InvalidUnmarshalError{reflect.TypeFor[*int]()}},
+               {Name(""), `123`, new(net.IP), &UnmarshalTypeError{Value: "number", Type: reflect.TypeFor[*net.IP](), Offset: 3}},
        }
        for _, tt := range tests {
                t.Run(tt.Name, func(t *testing.T) {
-                       err := Unmarshal(buf, tt.v)
-                       if err == nil {
+                       switch gotErr := Unmarshal([]byte(tt.in), tt.v); {
+                       case gotErr == nil:
                                t.Fatalf("%s: Unmarshal error: got nil, want non-nil", tt.Where)
-                       }
-                       if got := err.Error(); got != tt.want {
-                               t.Errorf("%s: Unmarshal error:\n\tgot:  %s\n\twant: %s", tt.Where, got, tt.want)
-                       }
-               })
-       }
-}
-
-func TestInvalidUnmarshalText(t *testing.T) {
-       buf := []byte(`123`)
-       tests := []struct {
-               CaseName
-               v    any
-               want string
-       }{
-               {Name(""), nil, "json: Unmarshal(nil)"},
-               {Name(""), struct{}{}, "json: Unmarshal(non-pointer struct {})"},
-               {Name(""), (*int)(nil), "json: Unmarshal(nil *int)"},
-               {Name(""), new(net.IP), "json: cannot unmarshal number into Go value of type *net.IP"},
-       }
-       for _, tt := range tests {
-               t.Run(tt.Name, func(t *testing.T) {
-                       err := Unmarshal(buf, tt.v)
-                       if err == nil {
-                               t.Fatalf("%s: Unmarshal error: got nil, want non-nil", tt.Where)
-                       }
-                       if got := err.Error(); got != tt.want {
-                               t.Errorf("%s: Unmarshal error:\n\tgot:  %s\n\twant: %s", tt.Where, got, tt.want)
+                       case !reflect.DeepEqual(gotErr, tt.wantErr):
+                               t.Errorf("%s: Unmarshal error:\n\tgot:  %#v\n\twant: %#v", tt.Where, gotErr, tt.wantErr)
                        }
                })
        }