mirror of https://github.com/goccy/go-json.git
Add test cases
This commit is contained in:
parent
a969fe7aff
commit
45acfe9424
177
encode_test.go
177
encode_test.go
|
@ -1392,3 +1392,180 @@ func TestDuplicatedFieldDisappears(t *testing.T) {
|
|||
t.Fatalf("Marshal: got %s want %s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnonymousFields(t *testing.T) {
|
||||
tests := []struct {
|
||||
label string // Test name
|
||||
makeInput func() interface{} // Function to create input value
|
||||
want string // Expected JSON output
|
||||
}{{
|
||||
// Both S1 and S2 have a field named X. From the perspective of S,
|
||||
// it is ambiguous which one X refers to.
|
||||
// This should not serialize either field.
|
||||
label: "AmbiguousField",
|
||||
makeInput: func() interface{} {
|
||||
type (
|
||||
S1 struct{ x, X int }
|
||||
S2 struct{ x, X int }
|
||||
S struct {
|
||||
S1
|
||||
S2
|
||||
}
|
||||
)
|
||||
return S{S1{1, 2}, S2{3, 4}}
|
||||
},
|
||||
want: `{}`,
|
||||
}, {
|
||||
label: "DominantField",
|
||||
// Both S1 and S2 have a field named X, but since S has an X field as
|
||||
// well, it takes precedence over S1.X and S2.X.
|
||||
makeInput: func() interface{} {
|
||||
type (
|
||||
S1 struct{ x, X int }
|
||||
S2 struct{ x, X int }
|
||||
S struct {
|
||||
S1
|
||||
S2
|
||||
x, X int
|
||||
}
|
||||
)
|
||||
return S{S1{1, 2}, S2{3, 4}, 5, 6}
|
||||
},
|
||||
want: `{"X":6}`,
|
||||
}, {
|
||||
// Unexported embedded field of non-struct type should not be serialized.
|
||||
label: "UnexportedEmbeddedInt",
|
||||
makeInput: func() interface{} {
|
||||
type (
|
||||
myInt int
|
||||
S struct{ myInt }
|
||||
)
|
||||
return S{5}
|
||||
},
|
||||
want: `{}`,
|
||||
}, {
|
||||
// Exported embedded field of non-struct type should be serialized.
|
||||
label: "ExportedEmbeddedInt",
|
||||
makeInput: func() interface{} {
|
||||
type (
|
||||
MyInt int
|
||||
S struct{ MyInt }
|
||||
)
|
||||
return S{5}
|
||||
},
|
||||
want: `{"MyInt":5}`,
|
||||
}, {
|
||||
// Unexported embedded field of pointer to non-struct type
|
||||
// should not be serialized.
|
||||
label: "UnexportedEmbeddedIntPointer",
|
||||
makeInput: func() interface{} {
|
||||
type (
|
||||
myInt int
|
||||
S struct{ *myInt }
|
||||
)
|
||||
s := S{new(myInt)}
|
||||
*s.myInt = 5
|
||||
return s
|
||||
},
|
||||
want: `{}`,
|
||||
}, {
|
||||
// Exported embedded field of pointer to non-struct type
|
||||
// should be serialized.
|
||||
label: "ExportedEmbeddedIntPointer",
|
||||
makeInput: func() interface{} {
|
||||
type (
|
||||
MyInt int
|
||||
S struct{ *MyInt }
|
||||
)
|
||||
s := S{new(MyInt)}
|
||||
*s.MyInt = 5
|
||||
return s
|
||||
},
|
||||
want: `{"MyInt":5}`,
|
||||
}, {
|
||||
// Exported fields of embedded structs should have their
|
||||
// exported fields be serialized regardless of whether the struct types
|
||||
// themselves are exported.
|
||||
label: "EmbeddedStruct",
|
||||
makeInput: func() interface{} {
|
||||
type (
|
||||
s1 struct{ x, X int }
|
||||
S2 struct{ y, Y int }
|
||||
S struct {
|
||||
s1
|
||||
S2
|
||||
}
|
||||
)
|
||||
return S{s1{1, 2}, S2{3, 4}}
|
||||
},
|
||||
want: `{"X":2,"Y":4}`,
|
||||
}, {
|
||||
// Exported fields of pointers to embedded structs should have their
|
||||
// exported fields be serialized regardless of whether the struct types
|
||||
// themselves are exported.
|
||||
label: "EmbeddedStructPointer",
|
||||
makeInput: func() interface{} {
|
||||
type (
|
||||
s1 struct{ x, X int }
|
||||
S2 struct{ y, Y int }
|
||||
S struct {
|
||||
*s1
|
||||
*S2
|
||||
}
|
||||
)
|
||||
return S{&s1{1, 2}, &S2{3, 4}}
|
||||
},
|
||||
want: `{"X":2,"Y":4}`,
|
||||
}, {
|
||||
// Exported fields on embedded unexported structs at multiple levels
|
||||
// of nesting should still be serialized.
|
||||
label: "NestedStructAndInts",
|
||||
makeInput: func() interface{} {
|
||||
type (
|
||||
MyInt1 int
|
||||
MyInt2 int
|
||||
myInt int
|
||||
s2 struct {
|
||||
MyInt2
|
||||
myInt
|
||||
}
|
||||
s1 struct {
|
||||
MyInt1
|
||||
myInt
|
||||
s2
|
||||
}
|
||||
S struct {
|
||||
s1
|
||||
myInt
|
||||
}
|
||||
)
|
||||
return S{s1{1, 2, s2{3, 4}}, 6}
|
||||
},
|
||||
want: `{"MyInt1":1,"MyInt2":3}`,
|
||||
}, {
|
||||
// If an anonymous struct pointer field is nil, we should ignore
|
||||
// the embedded fields behind it. Not properly doing so may
|
||||
// result in the wrong output or reflect panics.
|
||||
label: "EmbeddedFieldBehindNilPointer",
|
||||
makeInput: func() interface{} {
|
||||
type (
|
||||
S2 struct{ Field string }
|
||||
S struct{ *S2 }
|
||||
)
|
||||
return S{}
|
||||
},
|
||||
want: `{}`,
|
||||
}}
|
||||
|
||||
for i, tt := range tests {
|
||||
t.Run(tt.label, func(t *testing.T) {
|
||||
b, err := json.Marshal(tt.makeInput())
|
||||
if err != nil {
|
||||
t.Fatalf("%d: Marshal() = %v, want nil error", i, err)
|
||||
}
|
||||
if string(b) != tt.want {
|
||||
t.Fatalf("%d: Marshal() = %q, want %q", i, b, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue