1 package testing
2
3 import (
4 "bytes"
5 "encoding/hex"
6 "fmt"
7 "io"
8 "math"
9 "reflect"
10
11 "github.com/aws/smithy-go/document"
12 "github.com/aws/smithy-go/middleware"
13 )
14
15
16
17
18
19
20
21
22
23 func CompareValues(expect, actual interface{}, _ ...interface{}) error {
24 return deepEqual(reflect.ValueOf(expect), reflect.ValueOf(actual), "<root>")
25 }
26
27 func deepEqual(expect, actual reflect.Value, path string) error {
28 if et, at := expect.Kind(), actual.Kind(); et != at {
29 return fmt.Errorf("%s: kind %s != %s", path, et, at)
30 }
31
32
33
34
35
36
37 ei, ai := expect.Interface(), actual.Interface()
38 if _, _, ok := asMetadatas(ei, ai); ok {
39 return nil
40 }
41 if e, a, ok := asDocuments(ei, ai); ok {
42 if !compareDocumentTypes(e, a) {
43 return fmt.Errorf("%s: document values unequal", path)
44 }
45 return nil
46 }
47 if e, a, ok := asReaders(ei, ai); ok {
48 if err := CompareReaders(e, a); err != nil {
49 return fmt.Errorf("%s: %w", path, err)
50 }
51 return nil
52 }
53
54 switch expect.Kind() {
55 case reflect.Pointer:
56 if expect.Type() != actual.Type() {
57 return fmt.Errorf("%s: type mismatch", path)
58 }
59
60 expect = deref(expect)
61 actual = deref(actual)
62 ek, ak := expect.Kind(), actual.Kind()
63 if ek == reflect.Invalid || ak == reflect.Invalid {
64
65 if ek == ak {
66 return nil
67 }
68 return fmt.Errorf("%s: %s != %s", path, fmtNil(ek), fmtNil(ak))
69 }
70 if err := deepEqual(expect, actual, path); err != nil {
71 return err
72 }
73 return nil
74 case reflect.Slice:
75 if expect.Len() != actual.Len() {
76 return fmt.Errorf("%s: slice length unequal", path)
77 }
78 for i := 0; i < expect.Len(); i++ {
79 ipath := fmt.Sprintf("%s[%d]", path, i)
80 if err := deepEqual(expect.Index(i), actual.Index(i), ipath); err != nil {
81 return err
82 }
83 }
84 return nil
85 case reflect.Map:
86 if expect.Len() != actual.Len() {
87 return fmt.Errorf("%s: map length unequal", path)
88 }
89 for _, k := range expect.MapKeys() {
90 kpath := fmt.Sprintf("%s[%q]", path, k.String())
91 if err := deepEqual(expect.MapIndex(k), actual.MapIndex(k), kpath); err != nil {
92 return err
93 }
94 }
95 return nil
96 case reflect.Struct:
97 for i := 0; i < expect.NumField(); i++ {
98 if !expect.Field(i).CanInterface() {
99 continue
100 }
101 fpath := fmt.Sprintf("%s.%s", path, expect.Type().Field(i).Name)
102 if err := deepEqual(expect.Field(i), actual.Field(i), fpath); err != nil {
103 return err
104 }
105 }
106 return nil
107 case reflect.Float32, reflect.Float64:
108
109 ef, af := math.Float64bits(expect.Float()), math.Float64bits(actual.Float())
110 if ef != af {
111 return fmt.Errorf("%s: float 0x%x != 0x%x", path, ef, af)
112 }
113 return nil
114 default:
115
116 if !reflect.DeepEqual(ei, ai) {
117 return fmt.Errorf("%s: %v != %v", path, ei, ai)
118 }
119 return nil
120 }
121 }
122
123 func asMetadatas(i, j interface{}) (ii, jj middleware.Metadata, ok bool) {
124 ii, iok := i.(middleware.Metadata)
125 jj, jok := j.(middleware.Metadata)
126 return ii, jj, iok || jok
127 }
128
129 func asDocuments(i, j interface{}) (ii, jj documentInterface, ok bool) {
130 ii, iok := i.(documentInterface)
131 jj, jok := j.(documentInterface)
132 return ii, jj, iok || jok
133 }
134
135 func asReaders(i, j interface{}) (ii, jj io.Reader, ok bool) {
136 ii, iok := i.(io.Reader)
137 jj, jok := j.(io.Reader)
138 return ii, jj, iok || jok
139 }
140
141 func deref(v reflect.Value) reflect.Value {
142 switch v.Kind() {
143 case reflect.Interface, reflect.Ptr:
144 for v.Kind() == reflect.Interface || v.Kind() == reflect.Ptr {
145 v = v.Elem()
146 }
147 }
148 return v
149 }
150
151 type documentInterface interface {
152 document.Marshaler
153 document.Unmarshaler
154 }
155
156 func compareDocumentTypes(x documentInterface, y documentInterface) bool {
157 if x == nil {
158 x = nopMarshaler{}
159 }
160 if y == nil {
161 y = nopMarshaler{}
162 }
163
164 xBytes, err := x.MarshalSmithyDocument()
165 if err != nil {
166 panic(fmt.Sprintf("MarshalSmithyDocument error: %v", err))
167 }
168 yBytes, err := y.MarshalSmithyDocument()
169 if err != nil {
170 panic(fmt.Sprintf("MarshalSmithyDocument error: %v", err))
171 }
172 return JSONEqual(xBytes, yBytes) == nil
173 }
174
175
176
177 func CompareReaders(expect, actual io.Reader) error {
178 if expect == nil {
179 expect = nopReader{}
180 }
181 if actual == nil {
182 actual = nopReader{}
183 }
184
185 e, err := io.ReadAll(expect)
186 if err != nil {
187 return fmt.Errorf("failed to read expect body, %w", err)
188 }
189
190 a, err := io.ReadAll(actual)
191 if err != nil {
192 return fmt.Errorf("failed to read actual body, %w", err)
193 }
194
195 if !bytes.Equal(e, a) {
196 return fmt.Errorf("bytes do not match\nexpect:\n%s\nactual:\n%s",
197 hex.Dump(e), hex.Dump(a))
198 }
199
200 return nil
201 }
202
203 func fmtNil(k reflect.Kind) string {
204 if k == reflect.Invalid {
205 return "nil"
206 }
207 return "non-nil"
208 }
209
210 type nopReader struct{}
211
212 func (nopReader) Read(p []byte) (int, error) { return 0, io.EOF }
213
214 type nopMarshaler struct{}
215
216 func (nopMarshaler) MarshalSmithyDocument() ([]byte, error) { return nil, nil }
217 func (nopMarshaler) UnmarshalSmithyDocument(v interface{}) error { return nil }
218
View as plain text