Skip to content

Commit 7bfacb0

Browse files
committed
Supporting structs
1 parent 959328e commit 7bfacb0

File tree

7 files changed

+557
-12
lines changed

7 files changed

+557
-12
lines changed

.travis.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
language: go
22
go:
33
- "1.10"
4-
- 1.11
5-
- 1.12
4+
- "1.11"
5+
- "1.12"
66

77
before_install:
88
- go get -u golang.org/x/tools/cmd/cover

diff/diff.go

+14-4
Original file line numberDiff line numberDiff line change
@@ -56,23 +56,33 @@ func diff(c config, lhs, rhs interface{}, visited *visited) (Differ, error) {
5656
return types{lhs, rhs}, ErrCyclic
5757
}
5858

59-
if lhsVal.Type().Comparable() && rhsVal.Type().Comparable() {
59+
if areScalars(lhsVal, rhsVal) {
6060
return scalar{lhs, rhs}, nil
6161
}
6262
if lhsVal.Kind() != rhsVal.Kind() {
6363
return types{lhs, rhs}, nil
6464
}
6565

66-
if lhsVal.Kind() == reflect.Slice {
66+
switch lhsVal.Kind() {
67+
case reflect.Slice:
6768
return c.sliceFn(c, lhs, rhs, visited)
68-
}
69-
if lhsVal.Kind() == reflect.Map {
69+
case reflect.Map:
7070
return newMap(c, lhs, rhs, visited)
71+
case reflect.Struct:
72+
return newStruct(c, lhs, rhs, visited)
7173
}
7274

7375
return types{lhs, rhs}, &ErrUnsupported{lhsVal.Type(), rhsVal.Type()}
7476
}
7577

78+
func areScalars(lhs, rhs reflect.Value) bool {
79+
if lhs.Kind() == reflect.Struct || rhs.Kind() == reflect.Struct {
80+
return false
81+
}
82+
83+
return lhs.Type().Comparable() && rhs.Type().Comparable()
84+
}
85+
7686
func nilCheck(lhs, rhs interface{}) (Differ, bool) {
7787
if lhs == nil && rhs == nil {
7888
return scalar{lhs, rhs}, true

diff/diff_test.go

+233-3
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,21 @@ func TestDiff(t *testing.T) {
5757
{LHS: []interface{}(nil), RHS: []interface{}{1, 2, 3.3}, Want: ContentDiffer},
5858
{LHS: []int(nil), RHS: []int{}, Want: Identical},
5959
{LHS: func() {}, RHS: func() {}, Want: TypesDiffer, Error: true},
60+
{
61+
LHS: struct{}{},
62+
RHS: struct{}{},
63+
Want: Identical,
64+
},
65+
{
66+
LHS: struct{ Foo int }{Foo: 42},
67+
RHS: struct{ Foo int }{Foo: 21},
68+
Want: ContentDiffer,
69+
},
70+
{
71+
LHS: struct{ Foo int }{Foo: 42},
72+
RHS: struct{ Bar int }{Bar: 42},
73+
Want: TypesDiffer,
74+
},
6075
} {
6176
diff, err := Diff(test.LHS, test.RHS)
6277

@@ -552,7 +567,7 @@ func TestMap(t *testing.T) {
552567
m, err := newMap(defaultConfig(), test.LHS, test.RHS, &visited{})
553568

554569
if err != nil {
555-
t.Errorf("NewMap(%+v, %+v): unexpected error: %q", test.LHS, test.RHS, err)
570+
t.Errorf("newMap(%+v, %+v): unexpected error: %q", test.LHS, test.RHS, err)
556571
continue
557572
}
558573
if m.Diff() != test.Type {
@@ -570,10 +585,10 @@ func TestMap(t *testing.T) {
570585
invalid, err := newMap(defaultConfig(), nil, nil, &visited{})
571586
if invalidErr, ok := err.(errInvalidType); ok {
572587
if !strings.Contains(invalidErr.Error(), "nil") {
573-
t.Errorf("NewMap(nil, nil): unexpected format for InvalidType error: got %s", err)
588+
t.Errorf("newMap(nil, nil): unexpected format for InvalidType error: got %s", err)
574589
}
575590
} else {
576-
t.Errorf("NewMap(nil, nil): expected InvalidType error, got %s", err)
591+
t.Errorf("newMap(nil, nil): expected InvalidType error, got %s", err)
577592
}
578593
ss := invalid.Strings()
579594
if len(ss) != 0 {
@@ -604,6 +619,197 @@ func TestMap(t *testing.T) {
604619
}
605620
}
606621

622+
type emptyStruct struct{}
623+
type subStruct struct {
624+
A int
625+
}
626+
type structA struct {
627+
Foo int
628+
Bar subStruct
629+
baz float64
630+
}
631+
type structB struct {
632+
Foo int
633+
Bar subStruct
634+
baz float64
635+
}
636+
type structC struct {
637+
Foo []int
638+
}
639+
type structInvalid struct {
640+
A func()
641+
}
642+
643+
func TestTypeStruct(t *testing.T) {
644+
for i, test := range []stringTest{
645+
{
646+
LHS: emptyStruct{},
647+
RHS: emptyStruct{},
648+
Want: [][]string{
649+
{"emptyStruct", "{}"},
650+
},
651+
WantJSON: [][]string{
652+
{"{}"},
653+
},
654+
Type: Identical,
655+
},
656+
{
657+
LHS: structA{
658+
Foo: 42,
659+
Bar: subStruct{
660+
A: 2,
661+
},
662+
baz: 4.2,
663+
},
664+
RHS: structA{
665+
Foo: 42,
666+
Bar: subStruct{
667+
A: 2,
668+
},
669+
baz: 1.1,
670+
},
671+
Want: [][]string{
672+
{"structA", "42", "{2}", "4.2"},
673+
},
674+
WantJSON: [][]string{
675+
{"42", "{2}", "4.2"},
676+
},
677+
Type: Identical,
678+
},
679+
{
680+
LHS: structA{
681+
Foo: 42,
682+
Bar: subStruct{
683+
A: 2,
684+
},
685+
baz: 4.2,
686+
},
687+
RHS: structB{
688+
Foo: 42,
689+
Bar: subStruct{
690+
A: 2,
691+
},
692+
baz: 1.1,
693+
},
694+
Want: [][]string{
695+
{"structA", "42", "{2}", "4.2"},
696+
},
697+
WantJSON: [][]string{
698+
{"42", "{2}", "4.2"},
699+
},
700+
Type: Identical,
701+
},
702+
{
703+
LHS: structA{
704+
Foo: 42,
705+
Bar: subStruct{
706+
A: 2,
707+
},
708+
baz: 4.2,
709+
},
710+
RHS: structB{
711+
Foo: 23,
712+
Bar: subStruct{
713+
A: 2,
714+
},
715+
baz: 1.1,
716+
},
717+
Want: [][]string{
718+
{},
719+
{"Bar"},
720+
{"Foo", "-", "int", "42"},
721+
{"Foo", "+", "int", "23"},
722+
{},
723+
},
724+
WantJSON: [][]string{
725+
{},
726+
{"Bar"},
727+
{"Foo", "-", "42"},
728+
{"Foo", "+", "23"},
729+
{},
730+
},
731+
Type: ContentDiffer,
732+
},
733+
{
734+
LHS: structA{
735+
Foo: 42,
736+
Bar: subStruct{
737+
A: 2,
738+
},
739+
baz: 4.2,
740+
},
741+
RHS: structC{
742+
Foo: []int{1, 2},
743+
},
744+
Want: [][]string{
745+
{"-", "structA", "42", "{2}", "4.2"},
746+
{"+", "structC", "[1 2]"},
747+
},
748+
WantJSON: [][]string{
749+
{"-", "42", "{2}", "4.2"},
750+
{"+", "{[1 2]}"},
751+
},
752+
Type: TypesDiffer,
753+
},
754+
} {
755+
s, err := newStruct(defaultConfig(), test.LHS, test.RHS, &visited{})
756+
757+
if err != nil {
758+
t.Errorf("newStruct(%+v, %+v): unexpected error: %q", test.LHS, test.RHS, err)
759+
continue
760+
}
761+
if s.Diff() != test.Type {
762+
t.Errorf("Types.Diff() = %q, expected %q", s.Diff(), test.Type)
763+
}
764+
765+
ss := s.Strings()
766+
indented := s.StringIndent(testKey, testPrefix, testOutput)
767+
testStrings(fmt.Sprintf("TestMap[%d]", i), t, test.Want, ss, indented)
768+
769+
indentedJSON := s.StringIndent(testKey, testPrefix, testJSONOutput)
770+
testStrings(fmt.Sprintf("TestMap[%d]", i), t, test.WantJSON, ss, indentedJSON)
771+
}
772+
773+
invalid, err := newStruct(defaultConfig(), nil, nil, &visited{})
774+
if invalidErr, ok := err.(errInvalidType); ok {
775+
if !strings.Contains(invalidErr.Error(), "nil") {
776+
t.Errorf("newStruct(nil, nil): unexpected format for InvalidType error: got %s", err)
777+
}
778+
} else {
779+
t.Errorf("newStruct(nil, nil): expected InvalidType error, got %s", err)
780+
}
781+
ss := invalid.Strings()
782+
if len(ss) != 0 {
783+
t.Errorf("len(invalidStruct.Strings()) = %d, expected 0", len(ss))
784+
}
785+
indented := invalid.StringIndent(testKey, testPrefix, testOutput)
786+
if indented != "" {
787+
t.Errorf("invalidStruct.StringIndent(%q, %q, %+v) = %q, expected %q", testKey, testPrefix, testOutput, indented, "")
788+
}
789+
790+
invalid, err = newStruct(defaultConfig(), structA{}, nil, &visited{})
791+
if invalidErr, ok := err.(errInvalidType); ok {
792+
if !strings.Contains(invalidErr.Error(), "nil") {
793+
t.Errorf("newStruct(structA{}, nil): unexpected format for InvalidType error: got %s", err)
794+
}
795+
} else {
796+
t.Errorf("newStruct(structA{}, nil): expected InvalidType error, got %s", err)
797+
}
798+
ss = invalid.Strings()
799+
if len(ss) != 0 {
800+
t.Errorf("len(invalidStruct.Strings()) = %d, expected 0", len(ss))
801+
}
802+
indented = invalid.StringIndent(testKey, testPrefix, testOutput)
803+
if indented != "" {
804+
t.Errorf("invalidStruct.StringIndent(%q, %q, %+v) = %q, expected %q", testKey, testPrefix, testOutput, indented, "")
805+
}
806+
807+
invalid, err = newStruct(defaultConfig(), structInvalid{}, structInvalid{}, &visited{})
808+
if err == nil {
809+
t.Errorf("newStruct(structInvalid{}, structInvalid{}): expected error, got nil")
810+
}
811+
}
812+
607813
func TestIgnore(t *testing.T) {
608814
ignoreDiff, _ := Ignore()
609815

@@ -643,6 +849,18 @@ func TestLHS(t *testing.T) {
643849
t.Errorf("LHS(%+v) = %v, expected %d", validLHSMapGetter, v, 42)
644850
}
645851

852+
validLHSStructGetter := Differ(&structDiff{
853+
lhs: structA{Foo: 42},
854+
rhs: structB{Foo: 23},
855+
})
856+
v, err = LHS(validLHSStructGetter)
857+
if err != nil {
858+
t.Errorf("LHS(%+v): unexpected error: %s", validLHSStructGetter, err)
859+
}
860+
if s, ok := v.(structA); !ok || s.Foo != 42 {
861+
t.Errorf("LHS(%+v).Foo = %v, expected %d", validLHSStructGetter, s.Foo, 42)
862+
}
863+
646864
validLHSSliceGetter := Differ(&slice{
647865
lhs: 42,
648866
rhs: "hello",
@@ -727,6 +945,18 @@ func TestRHS(t *testing.T) {
727945
t.Errorf("RHS(%+v) = %v, expected %q", validRHSMapGetter, v, "hello")
728946
}
729947

948+
validRHSStructGetter := Differ(&structDiff{
949+
lhs: structA{Foo: 42},
950+
rhs: structB{Foo: 23},
951+
})
952+
v, err = RHS(validRHSStructGetter)
953+
if err != nil {
954+
t.Errorf("RHS(%+v): unexpected error: %s", validRHSStructGetter, err)
955+
}
956+
if s, ok := v.(structB); !ok || s.Foo != 23 {
957+
t.Errorf("RHS(%+v).Foo = %v, expected %d", validRHSStructGetter, s.Foo, 23)
958+
}
959+
730960
validRHSSliceGetter := Differ(&slice{
731961
lhs: 42,
732962
rhs: "hello",

0 commit comments

Comments
 (0)