diff options
Diffstat (limited to 'vendor/github.com/google/go-cmp')
24 files changed, 2315 insertions, 875 deletions
diff --git a/vendor/github.com/google/go-cmp/cmp/compare.go b/vendor/github.com/google/go-cmp/cmp/compare.go index 7e215f2..2133562 100644 --- a/vendor/github.com/google/go-cmp/cmp/compare.go +++ b/vendor/github.com/google/go-cmp/cmp/compare.go | |||
@@ -29,26 +29,17 @@ package cmp | |||
29 | import ( | 29 | import ( |
30 | "fmt" | 30 | "fmt" |
31 | "reflect" | 31 | "reflect" |
32 | "strings" | ||
32 | 33 | ||
33 | "github.com/google/go-cmp/cmp/internal/diff" | 34 | "github.com/google/go-cmp/cmp/internal/diff" |
35 | "github.com/google/go-cmp/cmp/internal/flags" | ||
34 | "github.com/google/go-cmp/cmp/internal/function" | 36 | "github.com/google/go-cmp/cmp/internal/function" |
35 | "github.com/google/go-cmp/cmp/internal/value" | 37 | "github.com/google/go-cmp/cmp/internal/value" |
36 | ) | 38 | ) |
37 | 39 | ||
38 | // BUG(dsnet): Maps with keys containing NaN values cannot be properly compared due to | ||
39 | // the reflection package's inability to retrieve such entries. Equal will panic | ||
40 | // anytime it comes across a NaN key, but this behavior may change. | ||
41 | // | ||
42 | // See https://golang.org/issue/11104 for more details. | ||
43 | |||
44 | var nothing = reflect.Value{} | ||
45 | |||
46 | // Equal reports whether x and y are equal by recursively applying the | 40 | // Equal reports whether x and y are equal by recursively applying the |
47 | // following rules in the given order to x and y and all of their sub-values: | 41 | // following rules in the given order to x and y and all of their sub-values: |
48 | // | 42 | // |
49 | // • If two values are not of the same type, then they are never equal | ||
50 | // and the overall result is false. | ||
51 | // | ||
52 | // • Let S be the set of all Ignore, Transformer, and Comparer options that | 43 | // • Let S be the set of all Ignore, Transformer, and Comparer options that |
53 | // remain after applying all path filters, value filters, and type filters. | 44 | // remain after applying all path filters, value filters, and type filters. |
54 | // If at least one Ignore exists in S, then the comparison is ignored. | 45 | // If at least one Ignore exists in S, then the comparison is ignored. |
@@ -61,43 +52,79 @@ var nothing = reflect.Value{} | |||
61 | // | 52 | // |
62 | // • If the values have an Equal method of the form "(T) Equal(T) bool" or | 53 | // • If the values have an Equal method of the form "(T) Equal(T) bool" or |
63 | // "(T) Equal(I) bool" where T is assignable to I, then use the result of | 54 | // "(T) Equal(I) bool" where T is assignable to I, then use the result of |
64 | // x.Equal(y) even if x or y is nil. | 55 | // x.Equal(y) even if x or y is nil. Otherwise, no such method exists and |
65 | // Otherwise, no such method exists and evaluation proceeds to the next rule. | 56 | // evaluation proceeds to the next rule. |
66 | // | 57 | // |
67 | // • Lastly, try to compare x and y based on their basic kinds. | 58 | // • Lastly, try to compare x and y based on their basic kinds. |
68 | // Simple kinds like booleans, integers, floats, complex numbers, strings, and | 59 | // Simple kinds like booleans, integers, floats, complex numbers, strings, and |
69 | // channels are compared using the equivalent of the == operator in Go. | 60 | // channels are compared using the equivalent of the == operator in Go. |
70 | // Functions are only equal if they are both nil, otherwise they are unequal. | 61 | // Functions are only equal if they are both nil, otherwise they are unequal. |
71 | // Pointers are equal if the underlying values they point to are also equal. | ||
72 | // Interfaces are equal if their underlying concrete values are also equal. | ||
73 | // | 62 | // |
74 | // Structs are equal if all of their fields are equal. If a struct contains | 63 | // Structs are equal if recursively calling Equal on all fields report equal. |
75 | // unexported fields, Equal panics unless the AllowUnexported option is used or | 64 | // If a struct contains unexported fields, Equal panics unless an Ignore option |
76 | // an Ignore option (e.g., cmpopts.IgnoreUnexported) ignores that field. | 65 | // (e.g., cmpopts.IgnoreUnexported) ignores that field or the AllowUnexported |
66 | // option explicitly permits comparing the unexported field. | ||
67 | // | ||
68 | // Slices are equal if they are both nil or both non-nil, where recursively | ||
69 | // calling Equal on all non-ignored slice or array elements report equal. | ||
70 | // Empty non-nil slices and nil slices are not equal; to equate empty slices, | ||
71 | // consider using cmpopts.EquateEmpty. | ||
77 | // | 72 | // |
78 | // Arrays, slices, and maps are equal if they are both nil or both non-nil | 73 | // Maps are equal if they are both nil or both non-nil, where recursively |
79 | // with the same length and the elements at each index or key are equal. | 74 | // calling Equal on all non-ignored map entries report equal. |
80 | // Note that a non-nil empty slice and a nil slice are not equal. | ||
81 | // To equate empty slices and maps, consider using cmpopts.EquateEmpty. | ||
82 | // Map keys are equal according to the == operator. | 75 | // Map keys are equal according to the == operator. |
83 | // To use custom comparisons for map keys, consider using cmpopts.SortMaps. | 76 | // To use custom comparisons for map keys, consider using cmpopts.SortMaps. |
77 | // Empty non-nil maps and nil maps are not equal; to equate empty maps, | ||
78 | // consider using cmpopts.EquateEmpty. | ||
79 | // | ||
80 | // Pointers and interfaces are equal if they are both nil or both non-nil, | ||
81 | // where they have the same underlying concrete type and recursively | ||
82 | // calling Equal on the underlying values reports equal. | ||
84 | func Equal(x, y interface{}, opts ...Option) bool { | 83 | func Equal(x, y interface{}, opts ...Option) bool { |
84 | vx := reflect.ValueOf(x) | ||
85 | vy := reflect.ValueOf(y) | ||
86 | |||
87 | // If the inputs are different types, auto-wrap them in an empty interface | ||
88 | // so that they have the same parent type. | ||
89 | var t reflect.Type | ||
90 | if !vx.IsValid() || !vy.IsValid() || vx.Type() != vy.Type() { | ||
91 | t = reflect.TypeOf((*interface{})(nil)).Elem() | ||
92 | if vx.IsValid() { | ||
93 | vvx := reflect.New(t).Elem() | ||
94 | vvx.Set(vx) | ||
95 | vx = vvx | ||
96 | } | ||
97 | if vy.IsValid() { | ||
98 | vvy := reflect.New(t).Elem() | ||
99 | vvy.Set(vy) | ||
100 | vy = vvy | ||
101 | } | ||
102 | } else { | ||
103 | t = vx.Type() | ||
104 | } | ||
105 | |||
85 | s := newState(opts) | 106 | s := newState(opts) |
86 | s.compareAny(reflect.ValueOf(x), reflect.ValueOf(y)) | 107 | s.compareAny(&pathStep{t, vx, vy}) |
87 | return s.result.Equal() | 108 | return s.result.Equal() |
88 | } | 109 | } |
89 | 110 | ||
90 | // Diff returns a human-readable report of the differences between two values. | 111 | // Diff returns a human-readable report of the differences between two values. |
91 | // It returns an empty string if and only if Equal returns true for the same | 112 | // It returns an empty string if and only if Equal returns true for the same |
92 | // input values and options. The output string will use the "-" symbol to | 113 | // input values and options. |
93 | // indicate elements removed from x, and the "+" symbol to indicate elements | 114 | // |
94 | // added to y. | 115 | // The output is displayed as a literal in pseudo-Go syntax. |
116 | // At the start of each line, a "-" prefix indicates an element removed from x, | ||
117 | // a "+" prefix to indicates an element added to y, and the lack of a prefix | ||
118 | // indicates an element common to both x and y. If possible, the output | ||
119 | // uses fmt.Stringer.String or error.Error methods to produce more humanly | ||
120 | // readable outputs. In such cases, the string is prefixed with either an | ||
121 | // 's' or 'e' character, respectively, to indicate that the method was called. | ||
95 | // | 122 | // |
96 | // Do not depend on this output being stable. | 123 | // Do not depend on this output being stable. If you need the ability to |
124 | // programmatically interpret the difference, consider using a custom Reporter. | ||
97 | func Diff(x, y interface{}, opts ...Option) string { | 125 | func Diff(x, y interface{}, opts ...Option) string { |
98 | r := new(defaultReporter) | 126 | r := new(defaultReporter) |
99 | opts = Options{Options(opts), r} | 127 | eq := Equal(x, y, Options(opts), Reporter(r)) |
100 | eq := Equal(x, y, opts...) | ||
101 | d := r.String() | 128 | d := r.String() |
102 | if (d == "") != eq { | 129 | if (d == "") != eq { |
103 | panic("inconsistent difference and equality results") | 130 | panic("inconsistent difference and equality results") |
@@ -108,9 +135,13 @@ func Diff(x, y interface{}, opts ...Option) string { | |||
108 | type state struct { | 135 | type state struct { |
109 | // These fields represent the "comparison state". | 136 | // These fields represent the "comparison state". |
110 | // Calling statelessCompare must not result in observable changes to these. | 137 | // Calling statelessCompare must not result in observable changes to these. |
111 | result diff.Result // The current result of comparison | 138 | result diff.Result // The current result of comparison |
112 | curPath Path // The current path in the value tree | 139 | curPath Path // The current path in the value tree |
113 | reporter reporter // Optional reporter used for difference formatting | 140 | reporters []reporter // Optional reporters |
141 | |||
142 | // recChecker checks for infinite cycles applying the same set of | ||
143 | // transformers upon the output of itself. | ||
144 | recChecker recChecker | ||
114 | 145 | ||
115 | // dynChecker triggers pseudo-random checks for option correctness. | 146 | // dynChecker triggers pseudo-random checks for option correctness. |
116 | // It is safe for statelessCompare to mutate this value. | 147 | // It is safe for statelessCompare to mutate this value. |
@@ -122,10 +153,9 @@ type state struct { | |||
122 | } | 153 | } |
123 | 154 | ||
124 | func newState(opts []Option) *state { | 155 | func newState(opts []Option) *state { |
125 | s := new(state) | 156 | // Always ensure a validator option exists to validate the inputs. |
126 | for _, opt := range opts { | 157 | s := &state{opts: Options{validator{}}} |
127 | s.processOption(opt) | 158 | s.processOption(Options(opts)) |
128 | } | ||
129 | return s | 159 | return s |
130 | } | 160 | } |
131 | 161 | ||
@@ -152,10 +182,7 @@ func (s *state) processOption(opt Option) { | |||
152 | s.exporters[t] = true | 182 | s.exporters[t] = true |
153 | } | 183 | } |
154 | case reporter: | 184 | case reporter: |
155 | if s.reporter != nil { | 185 | s.reporters = append(s.reporters, opt) |
156 | panic("difference reporter already registered") | ||
157 | } | ||
158 | s.reporter = opt | ||
159 | default: | 186 | default: |
160 | panic(fmt.Sprintf("unknown option %T", opt)) | 187 | panic(fmt.Sprintf("unknown option %T", opt)) |
161 | } | 188 | } |
@@ -164,153 +191,88 @@ func (s *state) processOption(opt Option) { | |||
164 | // statelessCompare compares two values and returns the result. | 191 | // statelessCompare compares two values and returns the result. |
165 | // This function is stateless in that it does not alter the current result, | 192 | // This function is stateless in that it does not alter the current result, |
166 | // or output to any registered reporters. | 193 | // or output to any registered reporters. |
167 | func (s *state) statelessCompare(vx, vy reflect.Value) diff.Result { | 194 | func (s *state) statelessCompare(step PathStep) diff.Result { |
168 | // We do not save and restore the curPath because all of the compareX | 195 | // We do not save and restore the curPath because all of the compareX |
169 | // methods should properly push and pop from the path. | 196 | // methods should properly push and pop from the path. |
170 | // It is an implementation bug if the contents of curPath differs from | 197 | // It is an implementation bug if the contents of curPath differs from |
171 | // when calling this function to when returning from it. | 198 | // when calling this function to when returning from it. |
172 | 199 | ||
173 | oldResult, oldReporter := s.result, s.reporter | 200 | oldResult, oldReporters := s.result, s.reporters |
174 | s.result = diff.Result{} // Reset result | 201 | s.result = diff.Result{} // Reset result |
175 | s.reporter = nil // Remove reporter to avoid spurious printouts | 202 | s.reporters = nil // Remove reporters to avoid spurious printouts |
176 | s.compareAny(vx, vy) | 203 | s.compareAny(step) |
177 | res := s.result | 204 | res := s.result |
178 | s.result, s.reporter = oldResult, oldReporter | 205 | s.result, s.reporters = oldResult, oldReporters |
179 | return res | 206 | return res |
180 | } | 207 | } |
181 | 208 | ||
182 | func (s *state) compareAny(vx, vy reflect.Value) { | 209 | func (s *state) compareAny(step PathStep) { |
183 | // TODO: Support cyclic data structures. | 210 | // Update the path stack. |
184 | 211 | s.curPath.push(step) | |
185 | // Rule 0: Differing types are never equal. | 212 | defer s.curPath.pop() |
186 | if !vx.IsValid() || !vy.IsValid() { | 213 | for _, r := range s.reporters { |
187 | s.report(vx.IsValid() == vy.IsValid(), vx, vy) | 214 | r.PushStep(step) |
188 | return | 215 | defer r.PopStep() |
189 | } | ||
190 | if vx.Type() != vy.Type() { | ||
191 | s.report(false, vx, vy) // Possible for path to be empty | ||
192 | return | ||
193 | } | ||
194 | t := vx.Type() | ||
195 | if len(s.curPath) == 0 { | ||
196 | s.curPath.push(&pathStep{typ: t}) | ||
197 | defer s.curPath.pop() | ||
198 | } | 216 | } |
199 | vx, vy = s.tryExporting(vx, vy) | 217 | s.recChecker.Check(s.curPath) |
218 | |||
219 | // Obtain the current type and values. | ||
220 | t := step.Type() | ||
221 | vx, vy := step.Values() | ||
200 | 222 | ||
201 | // Rule 1: Check whether an option applies on this node in the value tree. | 223 | // Rule 1: Check whether an option applies on this node in the value tree. |
202 | if s.tryOptions(vx, vy, t) { | 224 | if s.tryOptions(t, vx, vy) { |
203 | return | 225 | return |
204 | } | 226 | } |
205 | 227 | ||
206 | // Rule 2: Check whether the type has a valid Equal method. | 228 | // Rule 2: Check whether the type has a valid Equal method. |
207 | if s.tryMethod(vx, vy, t) { | 229 | if s.tryMethod(t, vx, vy) { |
208 | return | 230 | return |
209 | } | 231 | } |
210 | 232 | ||
211 | // Rule 3: Recursively descend into each value's underlying kind. | 233 | // Rule 3: Compare based on the underlying kind. |
212 | switch t.Kind() { | 234 | switch t.Kind() { |
213 | case reflect.Bool: | 235 | case reflect.Bool: |
214 | s.report(vx.Bool() == vy.Bool(), vx, vy) | 236 | s.report(vx.Bool() == vy.Bool(), 0) |
215 | return | ||
216 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | 237 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: |
217 | s.report(vx.Int() == vy.Int(), vx, vy) | 238 | s.report(vx.Int() == vy.Int(), 0) |
218 | return | ||
219 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | 239 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: |
220 | s.report(vx.Uint() == vy.Uint(), vx, vy) | 240 | s.report(vx.Uint() == vy.Uint(), 0) |
221 | return | ||
222 | case reflect.Float32, reflect.Float64: | 241 | case reflect.Float32, reflect.Float64: |
223 | s.report(vx.Float() == vy.Float(), vx, vy) | 242 | s.report(vx.Float() == vy.Float(), 0) |
224 | return | ||
225 | case reflect.Complex64, reflect.Complex128: | 243 | case reflect.Complex64, reflect.Complex128: |
226 | s.report(vx.Complex() == vy.Complex(), vx, vy) | 244 | s.report(vx.Complex() == vy.Complex(), 0) |
227 | return | ||
228 | case reflect.String: | 245 | case reflect.String: |
229 | s.report(vx.String() == vy.String(), vx, vy) | 246 | s.report(vx.String() == vy.String(), 0) |
230 | return | ||
231 | case reflect.Chan, reflect.UnsafePointer: | 247 | case reflect.Chan, reflect.UnsafePointer: |
232 | s.report(vx.Pointer() == vy.Pointer(), vx, vy) | 248 | s.report(vx.Pointer() == vy.Pointer(), 0) |
233 | return | ||
234 | case reflect.Func: | 249 | case reflect.Func: |
235 | s.report(vx.IsNil() && vy.IsNil(), vx, vy) | 250 | s.report(vx.IsNil() && vy.IsNil(), 0) |
236 | return | 251 | case reflect.Struct: |
252 | s.compareStruct(t, vx, vy) | ||
253 | case reflect.Slice, reflect.Array: | ||
254 | s.compareSlice(t, vx, vy) | ||
255 | case reflect.Map: | ||
256 | s.compareMap(t, vx, vy) | ||
237 | case reflect.Ptr: | 257 | case reflect.Ptr: |
238 | if vx.IsNil() || vy.IsNil() { | 258 | s.comparePtr(t, vx, vy) |
239 | s.report(vx.IsNil() && vy.IsNil(), vx, vy) | ||
240 | return | ||
241 | } | ||
242 | s.curPath.push(&indirect{pathStep{t.Elem()}}) | ||
243 | defer s.curPath.pop() | ||
244 | s.compareAny(vx.Elem(), vy.Elem()) | ||
245 | return | ||
246 | case reflect.Interface: | 259 | case reflect.Interface: |
247 | if vx.IsNil() || vy.IsNil() { | 260 | s.compareInterface(t, vx, vy) |
248 | s.report(vx.IsNil() && vy.IsNil(), vx, vy) | ||
249 | return | ||
250 | } | ||
251 | if vx.Elem().Type() != vy.Elem().Type() { | ||
252 | s.report(false, vx.Elem(), vy.Elem()) | ||
253 | return | ||
254 | } | ||
255 | s.curPath.push(&typeAssertion{pathStep{vx.Elem().Type()}}) | ||
256 | defer s.curPath.pop() | ||
257 | s.compareAny(vx.Elem(), vy.Elem()) | ||
258 | return | ||
259 | case reflect.Slice: | ||
260 | if vx.IsNil() || vy.IsNil() { | ||
261 | s.report(vx.IsNil() && vy.IsNil(), vx, vy) | ||
262 | return | ||
263 | } | ||
264 | fallthrough | ||
265 | case reflect.Array: | ||
266 | s.compareArray(vx, vy, t) | ||
267 | return | ||
268 | case reflect.Map: | ||
269 | s.compareMap(vx, vy, t) | ||
270 | return | ||
271 | case reflect.Struct: | ||
272 | s.compareStruct(vx, vy, t) | ||
273 | return | ||
274 | default: | 261 | default: |
275 | panic(fmt.Sprintf("%v kind not handled", t.Kind())) | 262 | panic(fmt.Sprintf("%v kind not handled", t.Kind())) |
276 | } | 263 | } |
277 | } | 264 | } |
278 | 265 | ||
279 | func (s *state) tryExporting(vx, vy reflect.Value) (reflect.Value, reflect.Value) { | 266 | func (s *state) tryOptions(t reflect.Type, vx, vy reflect.Value) bool { |
280 | if sf, ok := s.curPath[len(s.curPath)-1].(*structField); ok && sf.unexported { | ||
281 | if sf.force { | ||
282 | // Use unsafe pointer arithmetic to get read-write access to an | ||
283 | // unexported field in the struct. | ||
284 | vx = unsafeRetrieveField(sf.pvx, sf.field) | ||
285 | vy = unsafeRetrieveField(sf.pvy, sf.field) | ||
286 | } else { | ||
287 | // We are not allowed to export the value, so invalidate them | ||
288 | // so that tryOptions can panic later if not explicitly ignored. | ||
289 | vx = nothing | ||
290 | vy = nothing | ||
291 | } | ||
292 | } | ||
293 | return vx, vy | ||
294 | } | ||
295 | |||
296 | func (s *state) tryOptions(vx, vy reflect.Value, t reflect.Type) bool { | ||
297 | // If there were no FilterValues, we will not detect invalid inputs, | ||
298 | // so manually check for them and append invalid if necessary. | ||
299 | // We still evaluate the options since an ignore can override invalid. | ||
300 | opts := s.opts | ||
301 | if !vx.IsValid() || !vy.IsValid() { | ||
302 | opts = Options{opts, invalid{}} | ||
303 | } | ||
304 | |||
305 | // Evaluate all filters and apply the remaining options. | 267 | // Evaluate all filters and apply the remaining options. |
306 | if opt := opts.filter(s, vx, vy, t); opt != nil { | 268 | if opt := s.opts.filter(s, t, vx, vy); opt != nil { |
307 | opt.apply(s, vx, vy) | 269 | opt.apply(s, vx, vy) |
308 | return true | 270 | return true |
309 | } | 271 | } |
310 | return false | 272 | return false |
311 | } | 273 | } |
312 | 274 | ||
313 | func (s *state) tryMethod(vx, vy reflect.Value, t reflect.Type) bool { | 275 | func (s *state) tryMethod(t reflect.Type, vx, vy reflect.Value) bool { |
314 | // Check if this type even has an Equal method. | 276 | // Check if this type even has an Equal method. |
315 | m, ok := t.MethodByName("Equal") | 277 | m, ok := t.MethodByName("Equal") |
316 | if !ok || !function.IsType(m.Type, function.EqualAssignable) { | 278 | if !ok || !function.IsType(m.Type, function.EqualAssignable) { |
@@ -318,11 +280,11 @@ func (s *state) tryMethod(vx, vy reflect.Value, t reflect.Type) bool { | |||
318 | } | 280 | } |
319 | 281 | ||
320 | eq := s.callTTBFunc(m.Func, vx, vy) | 282 | eq := s.callTTBFunc(m.Func, vx, vy) |
321 | s.report(eq, vx, vy) | 283 | s.report(eq, reportByMethod) |
322 | return true | 284 | return true |
323 | } | 285 | } |
324 | 286 | ||
325 | func (s *state) callTRFunc(f, v reflect.Value) reflect.Value { | 287 | func (s *state) callTRFunc(f, v reflect.Value, step Transform) reflect.Value { |
326 | v = sanitizeValue(v, f.Type().In(0)) | 288 | v = sanitizeValue(v, f.Type().In(0)) |
327 | if !s.dynChecker.Next() { | 289 | if !s.dynChecker.Next() { |
328 | return f.Call([]reflect.Value{v})[0] | 290 | return f.Call([]reflect.Value{v})[0] |
@@ -333,15 +295,15 @@ func (s *state) callTRFunc(f, v reflect.Value) reflect.Value { | |||
333 | // unsafe mutations to the input. | 295 | // unsafe mutations to the input. |
334 | c := make(chan reflect.Value) | 296 | c := make(chan reflect.Value) |
335 | go detectRaces(c, f, v) | 297 | go detectRaces(c, f, v) |
298 | got := <-c | ||
336 | want := f.Call([]reflect.Value{v})[0] | 299 | want := f.Call([]reflect.Value{v})[0] |
337 | if got := <-c; !s.statelessCompare(got, want).Equal() { | 300 | if step.vx, step.vy = got, want; !s.statelessCompare(step).Equal() { |
338 | // To avoid false-positives with non-reflexive equality operations, | 301 | // To avoid false-positives with non-reflexive equality operations, |
339 | // we sanity check whether a value is equal to itself. | 302 | // we sanity check whether a value is equal to itself. |
340 | if !s.statelessCompare(want, want).Equal() { | 303 | if step.vx, step.vy = want, want; !s.statelessCompare(step).Equal() { |
341 | return want | 304 | return want |
342 | } | 305 | } |
343 | fn := getFuncName(f.Pointer()) | 306 | panic(fmt.Sprintf("non-deterministic function detected: %s", function.NameOf(f))) |
344 | panic(fmt.Sprintf("non-deterministic function detected: %s", fn)) | ||
345 | } | 307 | } |
346 | return want | 308 | return want |
347 | } | 309 | } |
@@ -359,10 +321,10 @@ func (s *state) callTTBFunc(f, x, y reflect.Value) bool { | |||
359 | // unsafe mutations to the input. | 321 | // unsafe mutations to the input. |
360 | c := make(chan reflect.Value) | 322 | c := make(chan reflect.Value) |
361 | go detectRaces(c, f, y, x) | 323 | go detectRaces(c, f, y, x) |
324 | got := <-c | ||
362 | want := f.Call([]reflect.Value{x, y})[0].Bool() | 325 | want := f.Call([]reflect.Value{x, y})[0].Bool() |
363 | if got := <-c; !got.IsValid() || got.Bool() != want { | 326 | if !got.IsValid() || got.Bool() != want { |
364 | fn := getFuncName(f.Pointer()) | 327 | panic(fmt.Sprintf("non-deterministic or non-symmetric function detected: %s", function.NameOf(f))) |
365 | panic(fmt.Sprintf("non-deterministic or non-symmetric function detected: %s", fn)) | ||
366 | } | 328 | } |
367 | return want | 329 | return want |
368 | } | 330 | } |
@@ -380,140 +342,241 @@ func detectRaces(c chan<- reflect.Value, f reflect.Value, vs ...reflect.Value) { | |||
380 | // assuming that T is assignable to R. | 342 | // assuming that T is assignable to R. |
381 | // Otherwise, it returns the input value as is. | 343 | // Otherwise, it returns the input value as is. |
382 | func sanitizeValue(v reflect.Value, t reflect.Type) reflect.Value { | 344 | func sanitizeValue(v reflect.Value, t reflect.Type) reflect.Value { |
383 | // TODO(dsnet): Remove this hacky workaround. | 345 | // TODO(dsnet): Workaround for reflect bug (https://golang.org/issue/22143). |
384 | // See https://golang.org/issue/22143 | 346 | if !flags.AtLeastGo110 { |
385 | if v.Kind() == reflect.Interface && v.IsNil() && v.Type() != t { | 347 | if v.Kind() == reflect.Interface && v.IsNil() && v.Type() != t { |
386 | return reflect.New(t).Elem() | 348 | return reflect.New(t).Elem() |
349 | } | ||
387 | } | 350 | } |
388 | return v | 351 | return v |
389 | } | 352 | } |
390 | 353 | ||
391 | func (s *state) compareArray(vx, vy reflect.Value, t reflect.Type) { | 354 | func (s *state) compareStruct(t reflect.Type, vx, vy reflect.Value) { |
392 | step := &sliceIndex{pathStep{t.Elem()}, 0, 0} | 355 | var vax, vay reflect.Value // Addressable versions of vx and vy |
393 | s.curPath.push(step) | ||
394 | 356 | ||
395 | // Compute an edit-script for slices vx and vy. | 357 | step := StructField{&structField{}} |
396 | es := diff.Difference(vx.Len(), vy.Len(), func(ix, iy int) diff.Result { | 358 | for i := 0; i < t.NumField(); i++ { |
397 | step.xkey, step.ykey = ix, iy | 359 | step.typ = t.Field(i).Type |
398 | return s.statelessCompare(vx.Index(ix), vy.Index(iy)) | 360 | step.vx = vx.Field(i) |
399 | }) | 361 | step.vy = vy.Field(i) |
362 | step.name = t.Field(i).Name | ||
363 | step.idx = i | ||
364 | step.unexported = !isExported(step.name) | ||
365 | if step.unexported { | ||
366 | if step.name == "_" { | ||
367 | continue | ||
368 | } | ||
369 | // Defer checking of unexported fields until later to give an | ||
370 | // Ignore a chance to ignore the field. | ||
371 | if !vax.IsValid() || !vay.IsValid() { | ||
372 | // For retrieveUnexportedField to work, the parent struct must | ||
373 | // be addressable. Create a new copy of the values if | ||
374 | // necessary to make them addressable. | ||
375 | vax = makeAddressable(vx) | ||
376 | vay = makeAddressable(vy) | ||
377 | } | ||
378 | step.mayForce = s.exporters[t] | ||
379 | step.pvx = vax | ||
380 | step.pvy = vay | ||
381 | step.field = t.Field(i) | ||
382 | } | ||
383 | s.compareAny(step) | ||
384 | } | ||
385 | } | ||
400 | 386 | ||
401 | // Report the entire slice as is if the arrays are of primitive kind, | 387 | func (s *state) compareSlice(t reflect.Type, vx, vy reflect.Value) { |
402 | // and the arrays are different enough. | 388 | isSlice := t.Kind() == reflect.Slice |
403 | isPrimitive := false | 389 | if isSlice && (vx.IsNil() || vy.IsNil()) { |
404 | switch t.Elem().Kind() { | 390 | s.report(vx.IsNil() && vy.IsNil(), 0) |
405 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, | ||
406 | reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, | ||
407 | reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: | ||
408 | isPrimitive = true | ||
409 | } | ||
410 | if isPrimitive && es.Dist() > (vx.Len()+vy.Len())/4 { | ||
411 | s.curPath.pop() // Pop first since we are reporting the whole slice | ||
412 | s.report(false, vx, vy) | ||
413 | return | 391 | return |
414 | } | 392 | } |
415 | 393 | ||
416 | // Replay the edit-script. | 394 | // TODO: Support cyclic data structures. |
395 | |||
396 | step := SliceIndex{&sliceIndex{pathStep: pathStep{typ: t.Elem()}}} | ||
397 | withIndexes := func(ix, iy int) SliceIndex { | ||
398 | if ix >= 0 { | ||
399 | step.vx, step.xkey = vx.Index(ix), ix | ||
400 | } else { | ||
401 | step.vx, step.xkey = reflect.Value{}, -1 | ||
402 | } | ||
403 | if iy >= 0 { | ||
404 | step.vy, step.ykey = vy.Index(iy), iy | ||
405 | } else { | ||
406 | step.vy, step.ykey = reflect.Value{}, -1 | ||
407 | } | ||
408 | return step | ||
409 | } | ||
410 | |||
411 | // Ignore options are able to ignore missing elements in a slice. | ||
412 | // However, detecting these reliably requires an optimal differencing | ||
413 | // algorithm, for which diff.Difference is not. | ||
414 | // | ||
415 | // Instead, we first iterate through both slices to detect which elements | ||
416 | // would be ignored if standing alone. The index of non-discarded elements | ||
417 | // are stored in a separate slice, which diffing is then performed on. | ||
418 | var indexesX, indexesY []int | ||
419 | var ignoredX, ignoredY []bool | ||
420 | for ix := 0; ix < vx.Len(); ix++ { | ||
421 | ignored := s.statelessCompare(withIndexes(ix, -1)).NumDiff == 0 | ||
422 | if !ignored { | ||
423 | indexesX = append(indexesX, ix) | ||
424 | } | ||
425 | ignoredX = append(ignoredX, ignored) | ||
426 | } | ||
427 | for iy := 0; iy < vy.Len(); iy++ { | ||
428 | ignored := s.statelessCompare(withIndexes(-1, iy)).NumDiff == 0 | ||
429 | if !ignored { | ||
430 | indexesY = append(indexesY, iy) | ||
431 | } | ||
432 | ignoredY = append(ignoredY, ignored) | ||
433 | } | ||
434 | |||
435 | // Compute an edit-script for slices vx and vy (excluding ignored elements). | ||
436 | edits := diff.Difference(len(indexesX), len(indexesY), func(ix, iy int) diff.Result { | ||
437 | return s.statelessCompare(withIndexes(indexesX[ix], indexesY[iy])) | ||
438 | }) | ||
439 | |||
440 | // Replay the ignore-scripts and the edit-script. | ||
417 | var ix, iy int | 441 | var ix, iy int |
418 | for _, e := range es { | 442 | for ix < vx.Len() || iy < vy.Len() { |
443 | var e diff.EditType | ||
444 | switch { | ||
445 | case ix < len(ignoredX) && ignoredX[ix]: | ||
446 | e = diff.UniqueX | ||
447 | case iy < len(ignoredY) && ignoredY[iy]: | ||
448 | e = diff.UniqueY | ||
449 | default: | ||
450 | e, edits = edits[0], edits[1:] | ||
451 | } | ||
419 | switch e { | 452 | switch e { |
420 | case diff.UniqueX: | 453 | case diff.UniqueX: |
421 | step.xkey, step.ykey = ix, -1 | 454 | s.compareAny(withIndexes(ix, -1)) |
422 | s.report(false, vx.Index(ix), nothing) | ||
423 | ix++ | 455 | ix++ |
424 | case diff.UniqueY: | 456 | case diff.UniqueY: |
425 | step.xkey, step.ykey = -1, iy | 457 | s.compareAny(withIndexes(-1, iy)) |
426 | s.report(false, nothing, vy.Index(iy)) | ||
427 | iy++ | 458 | iy++ |
428 | default: | 459 | default: |
429 | step.xkey, step.ykey = ix, iy | 460 | s.compareAny(withIndexes(ix, iy)) |
430 | if e == diff.Identity { | ||
431 | s.report(true, vx.Index(ix), vy.Index(iy)) | ||
432 | } else { | ||
433 | s.compareAny(vx.Index(ix), vy.Index(iy)) | ||
434 | } | ||
435 | ix++ | 461 | ix++ |
436 | iy++ | 462 | iy++ |
437 | } | 463 | } |
438 | } | 464 | } |
439 | s.curPath.pop() | ||
440 | return | ||
441 | } | 465 | } |
442 | 466 | ||
443 | func (s *state) compareMap(vx, vy reflect.Value, t reflect.Type) { | 467 | func (s *state) compareMap(t reflect.Type, vx, vy reflect.Value) { |
444 | if vx.IsNil() || vy.IsNil() { | 468 | if vx.IsNil() || vy.IsNil() { |
445 | s.report(vx.IsNil() && vy.IsNil(), vx, vy) | 469 | s.report(vx.IsNil() && vy.IsNil(), 0) |
446 | return | 470 | return |
447 | } | 471 | } |
448 | 472 | ||
473 | // TODO: Support cyclic data structures. | ||
474 | |||
449 | // We combine and sort the two map keys so that we can perform the | 475 | // We combine and sort the two map keys so that we can perform the |
450 | // comparisons in a deterministic order. | 476 | // comparisons in a deterministic order. |
451 | step := &mapIndex{pathStep: pathStep{t.Elem()}} | 477 | step := MapIndex{&mapIndex{pathStep: pathStep{typ: t.Elem()}}} |
452 | s.curPath.push(step) | ||
453 | defer s.curPath.pop() | ||
454 | for _, k := range value.SortKeys(append(vx.MapKeys(), vy.MapKeys()...)) { | 478 | for _, k := range value.SortKeys(append(vx.MapKeys(), vy.MapKeys()...)) { |
479 | step.vx = vx.MapIndex(k) | ||
480 | step.vy = vy.MapIndex(k) | ||
455 | step.key = k | 481 | step.key = k |
456 | vvx := vx.MapIndex(k) | 482 | if !step.vx.IsValid() && !step.vy.IsValid() { |
457 | vvy := vy.MapIndex(k) | 483 | // It is possible for both vx and vy to be invalid if the |
458 | switch { | 484 | // key contained a NaN value in it. |
459 | case vvx.IsValid() && vvy.IsValid(): | 485 | // |
460 | s.compareAny(vvx, vvy) | 486 | // Even with the ability to retrieve NaN keys in Go 1.12, |
461 | case vvx.IsValid() && !vvy.IsValid(): | 487 | // there still isn't a sensible way to compare the values since |
462 | s.report(false, vvx, nothing) | 488 | // a NaN key may map to multiple unordered values. |
463 | case !vvx.IsValid() && vvy.IsValid(): | 489 | // The most reasonable way to compare NaNs would be to compare the |
464 | s.report(false, nothing, vvy) | 490 | // set of values. However, this is impossible to do efficiently |
465 | default: | 491 | // since set equality is provably an O(n^2) operation given only |
466 | // It is possible for both vvx and vvy to be invalid if the | 492 | // an Equal function. If we had a Less function or Hash function, |
467 | // key contained a NaN value in it. There is no way in | 493 | // this could be done in O(n*log(n)) or O(n), respectively. |
468 | // reflection to be able to retrieve these values. | 494 | // |
469 | // See https://golang.org/issue/11104 | 495 | // Rather than adding complex logic to deal with NaNs, make it |
470 | panic(fmt.Sprintf("%#v has map key with NaNs", s.curPath)) | 496 | // the user's responsibility to compare such obscure maps. |
497 | const help = "consider providing a Comparer to compare the map" | ||
498 | panic(fmt.Sprintf("%#v has map key with NaNs\n%s", s.curPath, help)) | ||
471 | } | 499 | } |
500 | s.compareAny(step) | ||
472 | } | 501 | } |
473 | } | 502 | } |
474 | 503 | ||
475 | func (s *state) compareStruct(vx, vy reflect.Value, t reflect.Type) { | 504 | func (s *state) comparePtr(t reflect.Type, vx, vy reflect.Value) { |
476 | var vax, vay reflect.Value // Addressable versions of vx and vy | 505 | if vx.IsNil() || vy.IsNil() { |
506 | s.report(vx.IsNil() && vy.IsNil(), 0) | ||
507 | return | ||
508 | } | ||
477 | 509 | ||
478 | step := &structField{} | 510 | // TODO: Support cyclic data structures. |
479 | s.curPath.push(step) | 511 | |
480 | defer s.curPath.pop() | 512 | vx, vy = vx.Elem(), vy.Elem() |
481 | for i := 0; i < t.NumField(); i++ { | 513 | s.compareAny(Indirect{&indirect{pathStep{t.Elem(), vx, vy}}}) |
482 | vvx := vx.Field(i) | 514 | } |
483 | vvy := vy.Field(i) | 515 | |
484 | step.typ = t.Field(i).Type | 516 | func (s *state) compareInterface(t reflect.Type, vx, vy reflect.Value) { |
485 | step.name = t.Field(i).Name | 517 | if vx.IsNil() || vy.IsNil() { |
486 | step.idx = i | 518 | s.report(vx.IsNil() && vy.IsNil(), 0) |
487 | step.unexported = !isExported(step.name) | 519 | return |
488 | if step.unexported { | 520 | } |
489 | // Defer checking of unexported fields until later to give an | 521 | vx, vy = vx.Elem(), vy.Elem() |
490 | // Ignore a chance to ignore the field. | 522 | if vx.Type() != vy.Type() { |
491 | if !vax.IsValid() || !vay.IsValid() { | 523 | s.report(false, 0) |
492 | // For unsafeRetrieveField to work, the parent struct must | 524 | return |
493 | // be addressable. Create a new copy of the values if | 525 | } |
494 | // necessary to make them addressable. | 526 | s.compareAny(TypeAssertion{&typeAssertion{pathStep{vx.Type(), vx, vy}}}) |
495 | vax = makeAddressable(vx) | 527 | } |
496 | vay = makeAddressable(vy) | 528 | |
497 | } | 529 | func (s *state) report(eq bool, rf resultFlags) { |
498 | step.force = s.exporters[t] | 530 | if rf&reportByIgnore == 0 { |
499 | step.pvx = vax | 531 | if eq { |
500 | step.pvy = vay | 532 | s.result.NumSame++ |
501 | step.field = t.Field(i) | 533 | rf |= reportEqual |
534 | } else { | ||
535 | s.result.NumDiff++ | ||
536 | rf |= reportUnequal | ||
502 | } | 537 | } |
503 | s.compareAny(vvx, vvy) | 538 | } |
539 | for _, r := range s.reporters { | ||
540 | r.Report(Result{flags: rf}) | ||
504 | } | 541 | } |
505 | } | 542 | } |
506 | 543 | ||
507 | // report records the result of a single comparison. | 544 | // recChecker tracks the state needed to periodically perform checks that |
508 | // It also calls Report if any reporter is registered. | 545 | // user provided transformers are not stuck in an infinitely recursive cycle. |
509 | func (s *state) report(eq bool, vx, vy reflect.Value) { | 546 | type recChecker struct{ next int } |
510 | if eq { | 547 | |
511 | s.result.NSame++ | 548 | // Check scans the Path for any recursive transformers and panics when any |
512 | } else { | 549 | // recursive transformers are detected. Note that the presence of a |
513 | s.result.NDiff++ | 550 | // recursive Transformer does not necessarily imply an infinite cycle. |
551 | // As such, this check only activates after some minimal number of path steps. | ||
552 | func (rc *recChecker) Check(p Path) { | ||
553 | const minLen = 1 << 16 | ||
554 | if rc.next == 0 { | ||
555 | rc.next = minLen | ||
556 | } | ||
557 | if len(p) < rc.next { | ||
558 | return | ||
559 | } | ||
560 | rc.next <<= 1 | ||
561 | |||
562 | // Check whether the same transformer has appeared at least twice. | ||
563 | var ss []string | ||
564 | m := map[Option]int{} | ||
565 | for _, ps := range p { | ||
566 | if t, ok := ps.(Transform); ok { | ||
567 | t := t.Option() | ||
568 | if m[t] == 1 { // Transformer was used exactly once before | ||
569 | tf := t.(*transformer).fnc.Type() | ||
570 | ss = append(ss, fmt.Sprintf("%v: %v => %v", t, tf.In(0), tf.Out(0))) | ||
571 | } | ||
572 | m[t]++ | ||
573 | } | ||
514 | } | 574 | } |
515 | if s.reporter != nil { | 575 | if len(ss) > 0 { |
516 | s.reporter.Report(vx, vy, eq, s.curPath) | 576 | const warning = "recursive set of Transformers detected" |
577 | const help = "consider using cmpopts.AcyclicTransformer" | ||
578 | set := strings.Join(ss, "\n\t") | ||
579 | panic(fmt.Sprintf("%s:\n\t%s\n%s", warning, set, help)) | ||
517 | } | 580 | } |
518 | } | 581 | } |
519 | 582 | ||
diff --git a/vendor/github.com/google/go-cmp/cmp/unsafe_panic.go b/vendor/github.com/google/go-cmp/cmp/export_panic.go index d1518eb..abc3a1c 100644 --- a/vendor/github.com/google/go-cmp/cmp/unsafe_panic.go +++ b/vendor/github.com/google/go-cmp/cmp/export_panic.go | |||
@@ -2,7 +2,7 @@ | |||
2 | // Use of this source code is governed by a BSD-style | 2 | // Use of this source code is governed by a BSD-style |
3 | // license that can be found in the LICENSE.md file. | 3 | // license that can be found in the LICENSE.md file. |
4 | 4 | ||
5 | // +build purego appengine js | 5 | // +build purego |
6 | 6 | ||
7 | package cmp | 7 | package cmp |
8 | 8 | ||
@@ -10,6 +10,6 @@ import "reflect" | |||
10 | 10 | ||
11 | const supportAllowUnexported = false | 11 | const supportAllowUnexported = false |
12 | 12 | ||
13 | func unsafeRetrieveField(reflect.Value, reflect.StructField) reflect.Value { | 13 | func retrieveUnexportedField(reflect.Value, reflect.StructField) reflect.Value { |
14 | panic("unsafeRetrieveField is not implemented") | 14 | panic("retrieveUnexportedField is not implemented") |
15 | } | 15 | } |
diff --git a/vendor/github.com/google/go-cmp/cmp/unsafe_reflect.go b/vendor/github.com/google/go-cmp/cmp/export_unsafe.go index 579b655..59d4ee9 100644 --- a/vendor/github.com/google/go-cmp/cmp/unsafe_reflect.go +++ b/vendor/github.com/google/go-cmp/cmp/export_unsafe.go | |||
@@ -2,7 +2,7 @@ | |||
2 | // Use of this source code is governed by a BSD-style | 2 | // Use of this source code is governed by a BSD-style |
3 | // license that can be found in the LICENSE.md file. | 3 | // license that can be found in the LICENSE.md file. |
4 | 4 | ||
5 | // +build !purego,!appengine,!js | 5 | // +build !purego |
6 | 6 | ||
7 | package cmp | 7 | package cmp |
8 | 8 | ||
@@ -13,11 +13,11 @@ import ( | |||
13 | 13 | ||
14 | const supportAllowUnexported = true | 14 | const supportAllowUnexported = true |
15 | 15 | ||
16 | // unsafeRetrieveField uses unsafe to forcibly retrieve any field from a struct | 16 | // retrieveUnexportedField uses unsafe to forcibly retrieve any field from |
17 | // such that the value has read-write permissions. | 17 | // a struct such that the value has read-write permissions. |
18 | // | 18 | // |
19 | // The parent struct, v, must be addressable, while f must be a StructField | 19 | // The parent struct, v, must be addressable, while f must be a StructField |
20 | // describing the field to retrieve. | 20 | // describing the field to retrieve. |
21 | func unsafeRetrieveField(v reflect.Value, f reflect.StructField) reflect.Value { | 21 | func retrieveUnexportedField(v reflect.Value, f reflect.StructField) reflect.Value { |
22 | return reflect.NewAt(f.Type, unsafe.Pointer(v.UnsafeAddr()+f.Offset)).Elem() | 22 | return reflect.NewAt(f.Type, unsafe.Pointer(v.UnsafeAddr()+f.Offset)).Elem() |
23 | } | 23 | } |
diff --git a/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go b/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go index 42afa49..fe98dcc 100644 --- a/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go +++ b/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go | |||
@@ -2,7 +2,7 @@ | |||
2 | // Use of this source code is governed by a BSD-style | 2 | // Use of this source code is governed by a BSD-style |
3 | // license that can be found in the LICENSE.md file. | 3 | // license that can be found in the LICENSE.md file. |
4 | 4 | ||
5 | // +build !debug | 5 | // +build !cmp_debug |
6 | 6 | ||
7 | package diff | 7 | package diff |
8 | 8 | ||
diff --git a/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go b/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go index fd9f7f1..597b6ae 100644 --- a/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go +++ b/vendor/github.com/google/go-cmp/cmp/internal/diff/debug_enable.go | |||
@@ -2,7 +2,7 @@ | |||
2 | // Use of this source code is governed by a BSD-style | 2 | // Use of this source code is governed by a BSD-style |
3 | // license that can be found in the LICENSE.md file. | 3 | // license that can be found in the LICENSE.md file. |
4 | 4 | ||
5 | // +build debug | 5 | // +build cmp_debug |
6 | 6 | ||
7 | package diff | 7 | package diff |
8 | 8 | ||
@@ -14,7 +14,7 @@ import ( | |||
14 | ) | 14 | ) |
15 | 15 | ||
16 | // The algorithm can be seen running in real-time by enabling debugging: | 16 | // The algorithm can be seen running in real-time by enabling debugging: |
17 | // go test -tags=debug -v | 17 | // go test -tags=cmp_debug -v |
18 | // | 18 | // |
19 | // Example output: | 19 | // Example output: |
20 | // === RUN TestDifference/#34 | 20 | // === RUN TestDifference/#34 |
diff --git a/vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go b/vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go index 260befe..3d2e426 100644 --- a/vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go +++ b/vendor/github.com/google/go-cmp/cmp/internal/diff/diff.go | |||
@@ -85,22 +85,31 @@ func (es EditScript) LenY() int { return len(es) - es.stats().NX } | |||
85 | type EqualFunc func(ix int, iy int) Result | 85 | type EqualFunc func(ix int, iy int) Result |
86 | 86 | ||
87 | // Result is the result of comparison. | 87 | // Result is the result of comparison. |
88 | // NSame is the number of sub-elements that are equal. | 88 | // NumSame is the number of sub-elements that are equal. |
89 | // NDiff is the number of sub-elements that are not equal. | 89 | // NumDiff is the number of sub-elements that are not equal. |
90 | type Result struct{ NSame, NDiff int } | 90 | type Result struct{ NumSame, NumDiff int } |
91 | |||
92 | // BoolResult returns a Result that is either Equal or not Equal. | ||
93 | func BoolResult(b bool) Result { | ||
94 | if b { | ||
95 | return Result{NumSame: 1} // Equal, Similar | ||
96 | } else { | ||
97 | return Result{NumDiff: 2} // Not Equal, not Similar | ||
98 | } | ||
99 | } | ||
91 | 100 | ||
92 | // Equal indicates whether the symbols are equal. Two symbols are equal | 101 | // Equal indicates whether the symbols are equal. Two symbols are equal |
93 | // if and only if NDiff == 0. If Equal, then they are also Similar. | 102 | // if and only if NumDiff == 0. If Equal, then they are also Similar. |
94 | func (r Result) Equal() bool { return r.NDiff == 0 } | 103 | func (r Result) Equal() bool { return r.NumDiff == 0 } |
95 | 104 | ||
96 | // Similar indicates whether two symbols are similar and may be represented | 105 | // Similar indicates whether two symbols are similar and may be represented |
97 | // by using the Modified type. As a special case, we consider binary comparisons | 106 | // by using the Modified type. As a special case, we consider binary comparisons |
98 | // (i.e., those that return Result{1, 0} or Result{0, 1}) to be similar. | 107 | // (i.e., those that return Result{1, 0} or Result{0, 1}) to be similar. |
99 | // | 108 | // |
100 | // The exact ratio of NSame to NDiff to determine similarity may change. | 109 | // The exact ratio of NumSame to NumDiff to determine similarity may change. |
101 | func (r Result) Similar() bool { | 110 | func (r Result) Similar() bool { |
102 | // Use NSame+1 to offset NSame so that binary comparisons are similar. | 111 | // Use NumSame+1 to offset NumSame so that binary comparisons are similar. |
103 | return r.NSame+1 >= r.NDiff | 112 | return r.NumSame+1 >= r.NumDiff |
104 | } | 113 | } |
105 | 114 | ||
106 | // Difference reports whether two lists of lengths nx and ny are equal | 115 | // Difference reports whether two lists of lengths nx and ny are equal |
@@ -191,9 +200,9 @@ func Difference(nx, ny int, f EqualFunc) (es EditScript) { | |||
191 | // that two lists commonly differ because elements were added to the front | 200 | // that two lists commonly differ because elements were added to the front |
192 | // or end of the other list. | 201 | // or end of the other list. |
193 | // | 202 | // |
194 | // Running the tests with the "debug" build tag prints a visualization of | 203 | // Running the tests with the "cmp_debug" build tag prints a visualization |
195 | // the algorithm running in real-time. This is educational for understanding | 204 | // of the algorithm running in real-time. This is educational for |
196 | // how the algorithm works. See debug_enable.go. | 205 | // understanding how the algorithm works. See debug_enable.go. |
197 | f = debug.Begin(nx, ny, f, &fwdPath.es, &revPath.es) | 206 | f = debug.Begin(nx, ny, f, &fwdPath.es, &revPath.es) |
198 | for { | 207 | for { |
199 | // Forward search from the beginning. | 208 | // Forward search from the beginning. |
diff --git a/vendor/github.com/google/go-cmp/cmp/internal/flags/flags.go b/vendor/github.com/google/go-cmp/cmp/internal/flags/flags.go new file mode 100644 index 0000000..a9e7fc0 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/internal/flags/flags.go | |||
@@ -0,0 +1,9 @@ | |||
1 | // Copyright 2019, The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE.md file. | ||
4 | |||
5 | package flags | ||
6 | |||
7 | // Deterministic controls whether the output of Diff should be deterministic. | ||
8 | // This is only used for testing. | ||
9 | var Deterministic bool | ||
diff --git a/vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_legacy.go b/vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_legacy.go new file mode 100644 index 0000000..01aed0a --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_legacy.go | |||
@@ -0,0 +1,10 @@ | |||
1 | // Copyright 2019, The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE.md file. | ||
4 | |||
5 | // +build !go1.10 | ||
6 | |||
7 | package flags | ||
8 | |||
9 | // AtLeastGo110 reports whether the Go toolchain is at least Go 1.10. | ||
10 | const AtLeastGo110 = false | ||
diff --git a/vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_recent.go b/vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_recent.go new file mode 100644 index 0000000..c0b667f --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/internal/flags/toolchain_recent.go | |||
@@ -0,0 +1,10 @@ | |||
1 | // Copyright 2019, The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE.md file. | ||
4 | |||
5 | // +build go1.10 | ||
6 | |||
7 | package flags | ||
8 | |||
9 | // AtLeastGo110 reports whether the Go toolchain is at least Go 1.10. | ||
10 | const AtLeastGo110 = true | ||
diff --git a/vendor/github.com/google/go-cmp/cmp/internal/function/func.go b/vendor/github.com/google/go-cmp/cmp/internal/function/func.go index 4c35ff1..ace1dbe 100644 --- a/vendor/github.com/google/go-cmp/cmp/internal/function/func.go +++ b/vendor/github.com/google/go-cmp/cmp/internal/function/func.go | |||
@@ -2,25 +2,34 @@ | |||
2 | // Use of this source code is governed by a BSD-style | 2 | // Use of this source code is governed by a BSD-style |
3 | // license that can be found in the LICENSE.md file. | 3 | // license that can be found in the LICENSE.md file. |
4 | 4 | ||
5 | // Package function identifies function types. | 5 | // Package function provides functionality for identifying function types. |
6 | package function | 6 | package function |
7 | 7 | ||
8 | import "reflect" | 8 | import ( |
9 | "reflect" | ||
10 | "regexp" | ||
11 | "runtime" | ||
12 | "strings" | ||
13 | ) | ||
9 | 14 | ||
10 | type funcType int | 15 | type funcType int |
11 | 16 | ||
12 | const ( | 17 | const ( |
13 | _ funcType = iota | 18 | _ funcType = iota |
14 | 19 | ||
20 | tbFunc // func(T) bool | ||
15 | ttbFunc // func(T, T) bool | 21 | ttbFunc // func(T, T) bool |
22 | trbFunc // func(T, R) bool | ||
16 | tibFunc // func(T, I) bool | 23 | tibFunc // func(T, I) bool |
17 | trFunc // func(T) R | 24 | trFunc // func(T) R |
18 | 25 | ||
19 | Equal = ttbFunc // func(T, T) bool | 26 | Equal = ttbFunc // func(T, T) bool |
20 | EqualAssignable = tibFunc // func(T, I) bool; encapsulates func(T, T) bool | 27 | EqualAssignable = tibFunc // func(T, I) bool; encapsulates func(T, T) bool |
21 | Transformer = trFunc // func(T) R | 28 | Transformer = trFunc // func(T) R |
22 | ValueFilter = ttbFunc // func(T, T) bool | 29 | ValueFilter = ttbFunc // func(T, T) bool |
23 | Less = ttbFunc // func(T, T) bool | 30 | Less = ttbFunc // func(T, T) bool |
31 | ValuePredicate = tbFunc // func(T) bool | ||
32 | KeyValuePredicate = trbFunc // func(T, R) bool | ||
24 | ) | 33 | ) |
25 | 34 | ||
26 | var boolType = reflect.TypeOf(true) | 35 | var boolType = reflect.TypeOf(true) |
@@ -32,10 +41,18 @@ func IsType(t reflect.Type, ft funcType) bool { | |||
32 | } | 41 | } |
33 | ni, no := t.NumIn(), t.NumOut() | 42 | ni, no := t.NumIn(), t.NumOut() |
34 | switch ft { | 43 | switch ft { |
44 | case tbFunc: // func(T) bool | ||
45 | if ni == 1 && no == 1 && t.Out(0) == boolType { | ||
46 | return true | ||
47 | } | ||
35 | case ttbFunc: // func(T, T) bool | 48 | case ttbFunc: // func(T, T) bool |
36 | if ni == 2 && no == 1 && t.In(0) == t.In(1) && t.Out(0) == boolType { | 49 | if ni == 2 && no == 1 && t.In(0) == t.In(1) && t.Out(0) == boolType { |
37 | return true | 50 | return true |
38 | } | 51 | } |
52 | case trbFunc: // func(T, R) bool | ||
53 | if ni == 2 && no == 1 && t.Out(0) == boolType { | ||
54 | return true | ||
55 | } | ||
39 | case tibFunc: // func(T, I) bool | 56 | case tibFunc: // func(T, I) bool |
40 | if ni == 2 && no == 1 && t.In(0).AssignableTo(t.In(1)) && t.Out(0) == boolType { | 57 | if ni == 2 && no == 1 && t.In(0).AssignableTo(t.In(1)) && t.Out(0) == boolType { |
41 | return true | 58 | return true |
@@ -47,3 +64,36 @@ func IsType(t reflect.Type, ft funcType) bool { | |||
47 | } | 64 | } |
48 | return false | 65 | return false |
49 | } | 66 | } |
67 | |||
68 | var lastIdentRx = regexp.MustCompile(`[_\p{L}][_\p{L}\p{N}]*$`) | ||
69 | |||
70 | // NameOf returns the name of the function value. | ||
71 | func NameOf(v reflect.Value) string { | ||
72 | fnc := runtime.FuncForPC(v.Pointer()) | ||
73 | if fnc == nil { | ||
74 | return "<unknown>" | ||
75 | } | ||
76 | fullName := fnc.Name() // e.g., "long/path/name/mypkg.(*MyType).(long/path/name/mypkg.myMethod)-fm" | ||
77 | |||
78 | // Method closures have a "-fm" suffix. | ||
79 | fullName = strings.TrimSuffix(fullName, "-fm") | ||
80 | |||
81 | var name string | ||
82 | for len(fullName) > 0 { | ||
83 | inParen := strings.HasSuffix(fullName, ")") | ||
84 | fullName = strings.TrimSuffix(fullName, ")") | ||
85 | |||
86 | s := lastIdentRx.FindString(fullName) | ||
87 | if s == "" { | ||
88 | break | ||
89 | } | ||
90 | name = s + "." + name | ||
91 | fullName = strings.TrimSuffix(fullName, s) | ||
92 | |||
93 | if i := strings.LastIndexByte(fullName, '('); inParen && i >= 0 { | ||
94 | fullName = fullName[:i] | ||
95 | } | ||
96 | fullName = strings.TrimSuffix(fullName, ".") | ||
97 | } | ||
98 | return strings.TrimSuffix(name, ".") | ||
99 | } | ||
diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/format.go b/vendor/github.com/google/go-cmp/cmp/internal/value/format.go deleted file mode 100644 index 657e508..0000000 --- a/vendor/github.com/google/go-cmp/cmp/internal/value/format.go +++ /dev/null | |||
@@ -1,277 +0,0 @@ | |||
1 | // Copyright 2017, The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE.md file. | ||
4 | |||
5 | // Package value provides functionality for reflect.Value types. | ||
6 | package value | ||
7 | |||
8 | import ( | ||
9 | "fmt" | ||
10 | "reflect" | ||
11 | "strconv" | ||
12 | "strings" | ||
13 | "unicode" | ||
14 | ) | ||
15 | |||
16 | var stringerIface = reflect.TypeOf((*fmt.Stringer)(nil)).Elem() | ||
17 | |||
18 | // Format formats the value v as a string. | ||
19 | // | ||
20 | // This is similar to fmt.Sprintf("%+v", v) except this: | ||
21 | // * Prints the type unless it can be elided | ||
22 | // * Avoids printing struct fields that are zero | ||
23 | // * Prints a nil-slice as being nil, not empty | ||
24 | // * Prints map entries in deterministic order | ||
25 | func Format(v reflect.Value, conf FormatConfig) string { | ||
26 | conf.printType = true | ||
27 | conf.followPointers = true | ||
28 | conf.realPointers = true | ||
29 | return formatAny(v, conf, nil) | ||
30 | } | ||
31 | |||
32 | type FormatConfig struct { | ||
33 | UseStringer bool // Should the String method be used if available? | ||
34 | printType bool // Should we print the type before the value? | ||
35 | PrintPrimitiveType bool // Should we print the type of primitives? | ||
36 | followPointers bool // Should we recursively follow pointers? | ||
37 | realPointers bool // Should we print the real address of pointers? | ||
38 | } | ||
39 | |||
40 | func formatAny(v reflect.Value, conf FormatConfig, visited map[uintptr]bool) string { | ||
41 | // TODO: Should this be a multi-line printout in certain situations? | ||
42 | |||
43 | if !v.IsValid() { | ||
44 | return "<non-existent>" | ||
45 | } | ||
46 | if conf.UseStringer && v.Type().Implements(stringerIface) && v.CanInterface() { | ||
47 | if (v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface) && v.IsNil() { | ||
48 | return "<nil>" | ||
49 | } | ||
50 | |||
51 | const stringerPrefix = "s" // Indicates that the String method was used | ||
52 | s := v.Interface().(fmt.Stringer).String() | ||
53 | return stringerPrefix + formatString(s) | ||
54 | } | ||
55 | |||
56 | switch v.Kind() { | ||
57 | case reflect.Bool: | ||
58 | return formatPrimitive(v.Type(), v.Bool(), conf) | ||
59 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||
60 | return formatPrimitive(v.Type(), v.Int(), conf) | ||
61 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | ||
62 | if v.Type().PkgPath() == "" || v.Kind() == reflect.Uintptr { | ||
63 | // Unnamed uints are usually bytes or words, so use hexadecimal. | ||
64 | return formatPrimitive(v.Type(), formatHex(v.Uint()), conf) | ||
65 | } | ||
66 | return formatPrimitive(v.Type(), v.Uint(), conf) | ||
67 | case reflect.Float32, reflect.Float64: | ||
68 | return formatPrimitive(v.Type(), v.Float(), conf) | ||
69 | case reflect.Complex64, reflect.Complex128: | ||
70 | return formatPrimitive(v.Type(), v.Complex(), conf) | ||
71 | case reflect.String: | ||
72 | return formatPrimitive(v.Type(), formatString(v.String()), conf) | ||
73 | case reflect.UnsafePointer, reflect.Chan, reflect.Func: | ||
74 | return formatPointer(v, conf) | ||
75 | case reflect.Ptr: | ||
76 | if v.IsNil() { | ||
77 | if conf.printType { | ||
78 | return fmt.Sprintf("(%v)(nil)", v.Type()) | ||
79 | } | ||
80 | return "<nil>" | ||
81 | } | ||
82 | if visited[v.Pointer()] || !conf.followPointers { | ||
83 | return formatPointer(v, conf) | ||
84 | } | ||
85 | visited = insertPointer(visited, v.Pointer()) | ||
86 | return "&" + formatAny(v.Elem(), conf, visited) | ||
87 | case reflect.Interface: | ||
88 | if v.IsNil() { | ||
89 | if conf.printType { | ||
90 | return fmt.Sprintf("%v(nil)", v.Type()) | ||
91 | } | ||
92 | return "<nil>" | ||
93 | } | ||
94 | return formatAny(v.Elem(), conf, visited) | ||
95 | case reflect.Slice: | ||
96 | if v.IsNil() { | ||
97 | if conf.printType { | ||
98 | return fmt.Sprintf("%v(nil)", v.Type()) | ||
99 | } | ||
100 | return "<nil>" | ||
101 | } | ||
102 | if visited[v.Pointer()] { | ||
103 | return formatPointer(v, conf) | ||
104 | } | ||
105 | visited = insertPointer(visited, v.Pointer()) | ||
106 | fallthrough | ||
107 | case reflect.Array: | ||
108 | var ss []string | ||
109 | subConf := conf | ||
110 | subConf.printType = v.Type().Elem().Kind() == reflect.Interface | ||
111 | for i := 0; i < v.Len(); i++ { | ||
112 | s := formatAny(v.Index(i), subConf, visited) | ||
113 | ss = append(ss, s) | ||
114 | } | ||
115 | s := fmt.Sprintf("{%s}", strings.Join(ss, ", ")) | ||
116 | if conf.printType { | ||
117 | return v.Type().String() + s | ||
118 | } | ||
119 | return s | ||
120 | case reflect.Map: | ||
121 | if v.IsNil() { | ||
122 | if conf.printType { | ||
123 | return fmt.Sprintf("%v(nil)", v.Type()) | ||
124 | } | ||
125 | return "<nil>" | ||
126 | } | ||
127 | if visited[v.Pointer()] { | ||
128 | return formatPointer(v, conf) | ||
129 | } | ||
130 | visited = insertPointer(visited, v.Pointer()) | ||
131 | |||
132 | var ss []string | ||
133 | keyConf, valConf := conf, conf | ||
134 | keyConf.printType = v.Type().Key().Kind() == reflect.Interface | ||
135 | keyConf.followPointers = false | ||
136 | valConf.printType = v.Type().Elem().Kind() == reflect.Interface | ||
137 | for _, k := range SortKeys(v.MapKeys()) { | ||
138 | sk := formatAny(k, keyConf, visited) | ||
139 | sv := formatAny(v.MapIndex(k), valConf, visited) | ||
140 | ss = append(ss, fmt.Sprintf("%s: %s", sk, sv)) | ||
141 | } | ||
142 | s := fmt.Sprintf("{%s}", strings.Join(ss, ", ")) | ||
143 | if conf.printType { | ||
144 | return v.Type().String() + s | ||
145 | } | ||
146 | return s | ||
147 | case reflect.Struct: | ||
148 | var ss []string | ||
149 | subConf := conf | ||
150 | subConf.printType = true | ||
151 | for i := 0; i < v.NumField(); i++ { | ||
152 | vv := v.Field(i) | ||
153 | if isZero(vv) { | ||
154 | continue // Elide zero value fields | ||
155 | } | ||
156 | name := v.Type().Field(i).Name | ||
157 | subConf.UseStringer = conf.UseStringer | ||
158 | s := formatAny(vv, subConf, visited) | ||
159 | ss = append(ss, fmt.Sprintf("%s: %s", name, s)) | ||
160 | } | ||
161 | s := fmt.Sprintf("{%s}", strings.Join(ss, ", ")) | ||
162 | if conf.printType { | ||
163 | return v.Type().String() + s | ||
164 | } | ||
165 | return s | ||
166 | default: | ||
167 | panic(fmt.Sprintf("%v kind not handled", v.Kind())) | ||
168 | } | ||
169 | } | ||
170 | |||
171 | func formatString(s string) string { | ||
172 | // Use quoted string if it the same length as a raw string literal. | ||
173 | // Otherwise, attempt to use the raw string form. | ||
174 | qs := strconv.Quote(s) | ||
175 | if len(qs) == 1+len(s)+1 { | ||
176 | return qs | ||
177 | } | ||
178 | |||
179 | // Disallow newlines to ensure output is a single line. | ||
180 | // Only allow printable runes for readability purposes. | ||
181 | rawInvalid := func(r rune) bool { | ||
182 | return r == '`' || r == '\n' || !unicode.IsPrint(r) | ||
183 | } | ||
184 | if strings.IndexFunc(s, rawInvalid) < 0 { | ||
185 | return "`" + s + "`" | ||
186 | } | ||
187 | return qs | ||
188 | } | ||
189 | |||
190 | func formatPrimitive(t reflect.Type, v interface{}, conf FormatConfig) string { | ||
191 | if conf.printType && (conf.PrintPrimitiveType || t.PkgPath() != "") { | ||
192 | return fmt.Sprintf("%v(%v)", t, v) | ||
193 | } | ||
194 | return fmt.Sprintf("%v", v) | ||
195 | } | ||
196 | |||
197 | func formatPointer(v reflect.Value, conf FormatConfig) string { | ||
198 | p := v.Pointer() | ||
199 | if !conf.realPointers { | ||
200 | p = 0 // For deterministic printing purposes | ||
201 | } | ||
202 | s := formatHex(uint64(p)) | ||
203 | if conf.printType { | ||
204 | return fmt.Sprintf("(%v)(%s)", v.Type(), s) | ||
205 | } | ||
206 | return s | ||
207 | } | ||
208 | |||
209 | func formatHex(u uint64) string { | ||
210 | var f string | ||
211 | switch { | ||
212 | case u <= 0xff: | ||
213 | f = "0x%02x" | ||
214 | case u <= 0xffff: | ||
215 | f = "0x%04x" | ||
216 | case u <= 0xffffff: | ||
217 | f = "0x%06x" | ||
218 | case u <= 0xffffffff: | ||
219 | f = "0x%08x" | ||
220 | case u <= 0xffffffffff: | ||
221 | f = "0x%010x" | ||
222 | case u <= 0xffffffffffff: | ||
223 | f = "0x%012x" | ||
224 | case u <= 0xffffffffffffff: | ||
225 | f = "0x%014x" | ||
226 | case u <= 0xffffffffffffffff: | ||
227 | f = "0x%016x" | ||
228 | } | ||
229 | return fmt.Sprintf(f, u) | ||
230 | } | ||
231 | |||
232 | // insertPointer insert p into m, allocating m if necessary. | ||
233 | func insertPointer(m map[uintptr]bool, p uintptr) map[uintptr]bool { | ||
234 | if m == nil { | ||
235 | m = make(map[uintptr]bool) | ||
236 | } | ||
237 | m[p] = true | ||
238 | return m | ||
239 | } | ||
240 | |||
241 | // isZero reports whether v is the zero value. | ||
242 | // This does not rely on Interface and so can be used on unexported fields. | ||
243 | func isZero(v reflect.Value) bool { | ||
244 | switch v.Kind() { | ||
245 | case reflect.Bool: | ||
246 | return v.Bool() == false | ||
247 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||
248 | return v.Int() == 0 | ||
249 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | ||
250 | return v.Uint() == 0 | ||
251 | case reflect.Float32, reflect.Float64: | ||
252 | return v.Float() == 0 | ||
253 | case reflect.Complex64, reflect.Complex128: | ||
254 | return v.Complex() == 0 | ||
255 | case reflect.String: | ||
256 | return v.String() == "" | ||
257 | case reflect.UnsafePointer: | ||
258 | return v.Pointer() == 0 | ||
259 | case reflect.Chan, reflect.Func, reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: | ||
260 | return v.IsNil() | ||
261 | case reflect.Array: | ||
262 | for i := 0; i < v.Len(); i++ { | ||
263 | if !isZero(v.Index(i)) { | ||
264 | return false | ||
265 | } | ||
266 | } | ||
267 | return true | ||
268 | case reflect.Struct: | ||
269 | for i := 0; i < v.NumField(); i++ { | ||
270 | if !isZero(v.Field(i)) { | ||
271 | return false | ||
272 | } | ||
273 | } | ||
274 | return true | ||
275 | } | ||
276 | return false | ||
277 | } | ||
diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go b/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go new file mode 100644 index 0000000..0a01c47 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go | |||
@@ -0,0 +1,23 @@ | |||
1 | // Copyright 2018, The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE.md file. | ||
4 | |||
5 | // +build purego | ||
6 | |||
7 | package value | ||
8 | |||
9 | import "reflect" | ||
10 | |||
11 | // Pointer is an opaque typed pointer and is guaranteed to be comparable. | ||
12 | type Pointer struct { | ||
13 | p uintptr | ||
14 | t reflect.Type | ||
15 | } | ||
16 | |||
17 | // PointerOf returns a Pointer from v, which must be a | ||
18 | // reflect.Ptr, reflect.Slice, or reflect.Map. | ||
19 | func PointerOf(v reflect.Value) Pointer { | ||
20 | // NOTE: Storing a pointer as an uintptr is technically incorrect as it | ||
21 | // assumes that the GC implementation does not use a moving collector. | ||
22 | return Pointer{v.Pointer(), v.Type()} | ||
23 | } | ||
diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go b/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go new file mode 100644 index 0000000..da134ae --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go | |||
@@ -0,0 +1,26 @@ | |||
1 | // Copyright 2018, The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE.md file. | ||
4 | |||
5 | // +build !purego | ||
6 | |||
7 | package value | ||
8 | |||
9 | import ( | ||
10 | "reflect" | ||
11 | "unsafe" | ||
12 | ) | ||
13 | |||
14 | // Pointer is an opaque typed pointer and is guaranteed to be comparable. | ||
15 | type Pointer struct { | ||
16 | p unsafe.Pointer | ||
17 | t reflect.Type | ||
18 | } | ||
19 | |||
20 | // PointerOf returns a Pointer from v, which must be a | ||
21 | // reflect.Ptr, reflect.Slice, or reflect.Map. | ||
22 | func PointerOf(v reflect.Value) Pointer { | ||
23 | // The proper representation of a pointer is unsafe.Pointer, | ||
24 | // which is necessary if the GC ever uses a moving collector. | ||
25 | return Pointer{unsafe.Pointer(v.Pointer()), v.Type()} | ||
26 | } | ||
diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go b/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go index fe8aa27..938f646 100644 --- a/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go +++ b/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go | |||
@@ -19,7 +19,7 @@ func SortKeys(vs []reflect.Value) []reflect.Value { | |||
19 | } | 19 | } |
20 | 20 | ||
21 | // Sort the map keys. | 21 | // Sort the map keys. |
22 | sort.Sort(valueSorter(vs)) | 22 | sort.Slice(vs, func(i, j int) bool { return isLess(vs[i], vs[j]) }) |
23 | 23 | ||
24 | // Deduplicate keys (fails for NaNs). | 24 | // Deduplicate keys (fails for NaNs). |
25 | vs2 := vs[:1] | 25 | vs2 := vs[:1] |
@@ -31,13 +31,6 @@ func SortKeys(vs []reflect.Value) []reflect.Value { | |||
31 | return vs2 | 31 | return vs2 |
32 | } | 32 | } |
33 | 33 | ||
34 | // TODO: Use sort.Slice once Google AppEngine is on Go1.8 or above. | ||
35 | type valueSorter []reflect.Value | ||
36 | |||
37 | func (vs valueSorter) Len() int { return len(vs) } | ||
38 | func (vs valueSorter) Less(i, j int) bool { return isLess(vs[i], vs[j]) } | ||
39 | func (vs valueSorter) Swap(i, j int) { vs[i], vs[j] = vs[j], vs[i] } | ||
40 | |||
41 | // isLess is a generic function for sorting arbitrary map keys. | 34 | // isLess is a generic function for sorting arbitrary map keys. |
42 | // The inputs must be of the same type and must be comparable. | 35 | // The inputs must be of the same type and must be comparable. |
43 | func isLess(x, y reflect.Value) bool { | 36 | func isLess(x, y reflect.Value) bool { |
diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/zero.go b/vendor/github.com/google/go-cmp/cmp/internal/value/zero.go new file mode 100644 index 0000000..d13a12c --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/internal/value/zero.go | |||
@@ -0,0 +1,45 @@ | |||
1 | // Copyright 2017, The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE.md file. | ||
4 | |||
5 | package value | ||
6 | |||
7 | import "reflect" | ||
8 | |||
9 | // IsZero reports whether v is the zero value. | ||
10 | // This does not rely on Interface and so can be used on unexported fields. | ||
11 | func IsZero(v reflect.Value) bool { | ||
12 | switch v.Kind() { | ||
13 | case reflect.Bool: | ||
14 | return v.Bool() == false | ||
15 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||
16 | return v.Int() == 0 | ||
17 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | ||
18 | return v.Uint() == 0 | ||
19 | case reflect.Float32, reflect.Float64: | ||
20 | return v.Float() == 0 | ||
21 | case reflect.Complex64, reflect.Complex128: | ||
22 | return v.Complex() == 0 | ||
23 | case reflect.String: | ||
24 | return v.String() == "" | ||
25 | case reflect.UnsafePointer: | ||
26 | return v.Pointer() == 0 | ||
27 | case reflect.Chan, reflect.Func, reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: | ||
28 | return v.IsNil() | ||
29 | case reflect.Array: | ||
30 | for i := 0; i < v.Len(); i++ { | ||
31 | if !IsZero(v.Index(i)) { | ||
32 | return false | ||
33 | } | ||
34 | } | ||
35 | return true | ||
36 | case reflect.Struct: | ||
37 | for i := 0; i < v.NumField(); i++ { | ||
38 | if !IsZero(v.Field(i)) { | ||
39 | return false | ||
40 | } | ||
41 | } | ||
42 | return true | ||
43 | } | ||
44 | return false | ||
45 | } | ||
diff --git a/vendor/github.com/google/go-cmp/cmp/options.go b/vendor/github.com/google/go-cmp/cmp/options.go index 91d4b06..7934481 100644 --- a/vendor/github.com/google/go-cmp/cmp/options.go +++ b/vendor/github.com/google/go-cmp/cmp/options.go | |||
@@ -7,7 +7,7 @@ package cmp | |||
7 | import ( | 7 | import ( |
8 | "fmt" | 8 | "fmt" |
9 | "reflect" | 9 | "reflect" |
10 | "runtime" | 10 | "regexp" |
11 | "strings" | 11 | "strings" |
12 | 12 | ||
13 | "github.com/google/go-cmp/cmp/internal/function" | 13 | "github.com/google/go-cmp/cmp/internal/function" |
@@ -29,11 +29,11 @@ type Option interface { | |||
29 | // An Options is returned only if multiple comparers or transformers | 29 | // An Options is returned only if multiple comparers or transformers |
30 | // can apply simultaneously and will only contain values of those types | 30 | // can apply simultaneously and will only contain values of those types |
31 | // or sub-Options containing values of those types. | 31 | // or sub-Options containing values of those types. |
32 | filter(s *state, vx, vy reflect.Value, t reflect.Type) applicableOption | 32 | filter(s *state, t reflect.Type, vx, vy reflect.Value) applicableOption |
33 | } | 33 | } |
34 | 34 | ||
35 | // applicableOption represents the following types: | 35 | // applicableOption represents the following types: |
36 | // Fundamental: ignore | invalid | *comparer | *transformer | 36 | // Fundamental: ignore | validator | *comparer | *transformer |
37 | // Grouping: Options | 37 | // Grouping: Options |
38 | type applicableOption interface { | 38 | type applicableOption interface { |
39 | Option | 39 | Option |
@@ -43,7 +43,7 @@ type applicableOption interface { | |||
43 | } | 43 | } |
44 | 44 | ||
45 | // coreOption represents the following types: | 45 | // coreOption represents the following types: |
46 | // Fundamental: ignore | invalid | *comparer | *transformer | 46 | // Fundamental: ignore | validator | *comparer | *transformer |
47 | // Filters: *pathFilter | *valuesFilter | 47 | // Filters: *pathFilter | *valuesFilter |
48 | type coreOption interface { | 48 | type coreOption interface { |
49 | Option | 49 | Option |
@@ -63,19 +63,19 @@ func (core) isCore() {} | |||
63 | // on all individual options held within. | 63 | // on all individual options held within. |
64 | type Options []Option | 64 | type Options []Option |
65 | 65 | ||
66 | func (opts Options) filter(s *state, vx, vy reflect.Value, t reflect.Type) (out applicableOption) { | 66 | func (opts Options) filter(s *state, t reflect.Type, vx, vy reflect.Value) (out applicableOption) { |
67 | for _, opt := range opts { | 67 | for _, opt := range opts { |
68 | switch opt := opt.filter(s, vx, vy, t); opt.(type) { | 68 | switch opt := opt.filter(s, t, vx, vy); opt.(type) { |
69 | case ignore: | 69 | case ignore: |
70 | return ignore{} // Only ignore can short-circuit evaluation | 70 | return ignore{} // Only ignore can short-circuit evaluation |
71 | case invalid: | 71 | case validator: |
72 | out = invalid{} // Takes precedence over comparer or transformer | 72 | out = validator{} // Takes precedence over comparer or transformer |
73 | case *comparer, *transformer, Options: | 73 | case *comparer, *transformer, Options: |
74 | switch out.(type) { | 74 | switch out.(type) { |
75 | case nil: | 75 | case nil: |
76 | out = opt | 76 | out = opt |
77 | case invalid: | 77 | case validator: |
78 | // Keep invalid | 78 | // Keep validator |
79 | case *comparer, *transformer, Options: | 79 | case *comparer, *transformer, Options: |
80 | out = Options{out, opt} // Conflicting comparers or transformers | 80 | out = Options{out, opt} // Conflicting comparers or transformers |
81 | } | 81 | } |
@@ -106,6 +106,11 @@ func (opts Options) String() string { | |||
106 | // FilterPath returns a new Option where opt is only evaluated if filter f | 106 | // FilterPath returns a new Option where opt is only evaluated if filter f |
107 | // returns true for the current Path in the value tree. | 107 | // returns true for the current Path in the value tree. |
108 | // | 108 | // |
109 | // This filter is called even if a slice element or map entry is missing and | ||
110 | // provides an opportunity to ignore such cases. The filter function must be | ||
111 | // symmetric such that the filter result is identical regardless of whether the | ||
112 | // missing value is from x or y. | ||
113 | // | ||
109 | // The option passed in may be an Ignore, Transformer, Comparer, Options, or | 114 | // The option passed in may be an Ignore, Transformer, Comparer, Options, or |
110 | // a previously filtered Option. | 115 | // a previously filtered Option. |
111 | func FilterPath(f func(Path) bool, opt Option) Option { | 116 | func FilterPath(f func(Path) bool, opt Option) Option { |
@@ -124,22 +129,22 @@ type pathFilter struct { | |||
124 | opt Option | 129 | opt Option |
125 | } | 130 | } |
126 | 131 | ||
127 | func (f pathFilter) filter(s *state, vx, vy reflect.Value, t reflect.Type) applicableOption { | 132 | func (f pathFilter) filter(s *state, t reflect.Type, vx, vy reflect.Value) applicableOption { |
128 | if f.fnc(s.curPath) { | 133 | if f.fnc(s.curPath) { |
129 | return f.opt.filter(s, vx, vy, t) | 134 | return f.opt.filter(s, t, vx, vy) |
130 | } | 135 | } |
131 | return nil | 136 | return nil |
132 | } | 137 | } |
133 | 138 | ||
134 | func (f pathFilter) String() string { | 139 | func (f pathFilter) String() string { |
135 | fn := getFuncName(reflect.ValueOf(f.fnc).Pointer()) | 140 | return fmt.Sprintf("FilterPath(%s, %v)", function.NameOf(reflect.ValueOf(f.fnc)), f.opt) |
136 | return fmt.Sprintf("FilterPath(%s, %v)", fn, f.opt) | ||
137 | } | 141 | } |
138 | 142 | ||
139 | // FilterValues returns a new Option where opt is only evaluated if filter f, | 143 | // FilterValues returns a new Option where opt is only evaluated if filter f, |
140 | // which is a function of the form "func(T, T) bool", returns true for the | 144 | // which is a function of the form "func(T, T) bool", returns true for the |
141 | // current pair of values being compared. If the type of the values is not | 145 | // current pair of values being compared. If either value is invalid or |
142 | // assignable to T, then this filter implicitly returns false. | 146 | // the type of the values is not assignable to T, then this filter implicitly |
147 | // returns false. | ||
143 | // | 148 | // |
144 | // The filter function must be | 149 | // The filter function must be |
145 | // symmetric (i.e., agnostic to the order of the inputs) and | 150 | // symmetric (i.e., agnostic to the order of the inputs) and |
@@ -171,19 +176,18 @@ type valuesFilter struct { | |||
171 | opt Option | 176 | opt Option |
172 | } | 177 | } |
173 | 178 | ||
174 | func (f valuesFilter) filter(s *state, vx, vy reflect.Value, t reflect.Type) applicableOption { | 179 | func (f valuesFilter) filter(s *state, t reflect.Type, vx, vy reflect.Value) applicableOption { |
175 | if !vx.IsValid() || !vy.IsValid() { | 180 | if !vx.IsValid() || !vx.CanInterface() || !vy.IsValid() || !vy.CanInterface() { |
176 | return invalid{} | 181 | return nil |
177 | } | 182 | } |
178 | if (f.typ == nil || t.AssignableTo(f.typ)) && s.callTTBFunc(f.fnc, vx, vy) { | 183 | if (f.typ == nil || t.AssignableTo(f.typ)) && s.callTTBFunc(f.fnc, vx, vy) { |
179 | return f.opt.filter(s, vx, vy, t) | 184 | return f.opt.filter(s, t, vx, vy) |
180 | } | 185 | } |
181 | return nil | 186 | return nil |
182 | } | 187 | } |
183 | 188 | ||
184 | func (f valuesFilter) String() string { | 189 | func (f valuesFilter) String() string { |
185 | fn := getFuncName(f.fnc.Pointer()) | 190 | return fmt.Sprintf("FilterValues(%s, %v)", function.NameOf(f.fnc), f.opt) |
186 | return fmt.Sprintf("FilterValues(%s, %v)", fn, f.opt) | ||
187 | } | 191 | } |
188 | 192 | ||
189 | // Ignore is an Option that causes all comparisons to be ignored. | 193 | // Ignore is an Option that causes all comparisons to be ignored. |
@@ -194,20 +198,45 @@ func Ignore() Option { return ignore{} } | |||
194 | type ignore struct{ core } | 198 | type ignore struct{ core } |
195 | 199 | ||
196 | func (ignore) isFiltered() bool { return false } | 200 | func (ignore) isFiltered() bool { return false } |
197 | func (ignore) filter(_ *state, _, _ reflect.Value, _ reflect.Type) applicableOption { return ignore{} } | 201 | func (ignore) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption { return ignore{} } |
198 | func (ignore) apply(_ *state, _, _ reflect.Value) { return } | 202 | func (ignore) apply(s *state, _, _ reflect.Value) { s.report(true, reportByIgnore) } |
199 | func (ignore) String() string { return "Ignore()" } | 203 | func (ignore) String() string { return "Ignore()" } |
200 | 204 | ||
201 | // invalid is a sentinel Option type to indicate that some options could not | 205 | // validator is a sentinel Option type to indicate that some options could not |
202 | // be evaluated due to unexported fields. | 206 | // be evaluated due to unexported fields, missing slice elements, or |
203 | type invalid struct{ core } | 207 | // missing map entries. Both values are validator only for unexported fields. |
208 | type validator struct{ core } | ||
209 | |||
210 | func (validator) filter(_ *state, _ reflect.Type, vx, vy reflect.Value) applicableOption { | ||
211 | if !vx.IsValid() || !vy.IsValid() { | ||
212 | return validator{} | ||
213 | } | ||
214 | if !vx.CanInterface() || !vy.CanInterface() { | ||
215 | return validator{} | ||
216 | } | ||
217 | return nil | ||
218 | } | ||
219 | func (validator) apply(s *state, vx, vy reflect.Value) { | ||
220 | // Implies missing slice element or map entry. | ||
221 | if !vx.IsValid() || !vy.IsValid() { | ||
222 | s.report(vx.IsValid() == vy.IsValid(), 0) | ||
223 | return | ||
224 | } | ||
225 | |||
226 | // Unable to Interface implies unexported field without visibility access. | ||
227 | if !vx.CanInterface() || !vy.CanInterface() { | ||
228 | const help = "consider using a custom Comparer; if you control the implementation of type, you can also consider AllowUnexported or cmpopts.IgnoreUnexported" | ||
229 | panic(fmt.Sprintf("cannot handle unexported field: %#v\n%s", s.curPath, help)) | ||
230 | } | ||
204 | 231 | ||
205 | func (invalid) filter(_ *state, _, _ reflect.Value, _ reflect.Type) applicableOption { return invalid{} } | 232 | panic("not reachable") |
206 | func (invalid) apply(s *state, _, _ reflect.Value) { | ||
207 | const help = "consider using AllowUnexported or cmpopts.IgnoreUnexported" | ||
208 | panic(fmt.Sprintf("cannot handle unexported field: %#v\n%s", s.curPath, help)) | ||
209 | } | 233 | } |
210 | 234 | ||
235 | // identRx represents a valid identifier according to the Go specification. | ||
236 | const identRx = `[_\p{L}][_\p{L}\p{N}]*` | ||
237 | |||
238 | var identsRx = regexp.MustCompile(`^` + identRx + `(\.` + identRx + `)*$`) | ||
239 | |||
211 | // Transformer returns an Option that applies a transformation function that | 240 | // Transformer returns an Option that applies a transformation function that |
212 | // converts values of a certain type into that of another. | 241 | // converts values of a certain type into that of another. |
213 | // | 242 | // |
@@ -220,18 +249,25 @@ func (invalid) apply(s *state, _, _ reflect.Value) { | |||
220 | // input and output types are the same), an implicit filter is added such that | 249 | // input and output types are the same), an implicit filter is added such that |
221 | // a transformer is applicable only if that exact transformer is not already | 250 | // a transformer is applicable only if that exact transformer is not already |
222 | // in the tail of the Path since the last non-Transform step. | 251 | // in the tail of the Path since the last non-Transform step. |
252 | // For situations where the implicit filter is still insufficient, | ||
253 | // consider using cmpopts.AcyclicTransformer, which adds a filter | ||
254 | // to prevent the transformer from being recursively applied upon itself. | ||
223 | // | 255 | // |
224 | // The name is a user provided label that is used as the Transform.Name in the | 256 | // The name is a user provided label that is used as the Transform.Name in the |
225 | // transformation PathStep. If empty, an arbitrary name is used. | 257 | // transformation PathStep (and eventually shown in the Diff output). |
258 | // The name must be a valid identifier or qualified identifier in Go syntax. | ||
259 | // If empty, an arbitrary name is used. | ||
226 | func Transformer(name string, f interface{}) Option { | 260 | func Transformer(name string, f interface{}) Option { |
227 | v := reflect.ValueOf(f) | 261 | v := reflect.ValueOf(f) |
228 | if !function.IsType(v.Type(), function.Transformer) || v.IsNil() { | 262 | if !function.IsType(v.Type(), function.Transformer) || v.IsNil() { |
229 | panic(fmt.Sprintf("invalid transformer function: %T", f)) | 263 | panic(fmt.Sprintf("invalid transformer function: %T", f)) |
230 | } | 264 | } |
231 | if name == "" { | 265 | if name == "" { |
232 | name = "λ" // Lambda-symbol as place-holder for anonymous transformer | 266 | name = function.NameOf(v) |
233 | } | 267 | if !identsRx.MatchString(name) { |
234 | if !isValid(name) { | 268 | name = "λ" // Lambda-symbol as placeholder name |
269 | } | ||
270 | } else if !identsRx.MatchString(name) { | ||
235 | panic(fmt.Sprintf("invalid name: %q", name)) | 271 | panic(fmt.Sprintf("invalid name: %q", name)) |
236 | } | 272 | } |
237 | tr := &transformer{name: name, fnc: reflect.ValueOf(f)} | 273 | tr := &transformer{name: name, fnc: reflect.ValueOf(f)} |
@@ -250,9 +286,9 @@ type transformer struct { | |||
250 | 286 | ||
251 | func (tr *transformer) isFiltered() bool { return tr.typ != nil } | 287 | func (tr *transformer) isFiltered() bool { return tr.typ != nil } |
252 | 288 | ||
253 | func (tr *transformer) filter(s *state, _, _ reflect.Value, t reflect.Type) applicableOption { | 289 | func (tr *transformer) filter(s *state, t reflect.Type, _, _ reflect.Value) applicableOption { |
254 | for i := len(s.curPath) - 1; i >= 0; i-- { | 290 | for i := len(s.curPath) - 1; i >= 0; i-- { |
255 | if t, ok := s.curPath[i].(*transform); !ok { | 291 | if t, ok := s.curPath[i].(Transform); !ok { |
256 | break // Hit most recent non-Transform step | 292 | break // Hit most recent non-Transform step |
257 | } else if tr == t.trans { | 293 | } else if tr == t.trans { |
258 | return nil // Cannot directly use same Transform | 294 | return nil // Cannot directly use same Transform |
@@ -265,18 +301,15 @@ func (tr *transformer) filter(s *state, _, _ reflect.Value, t reflect.Type) appl | |||
265 | } | 301 | } |
266 | 302 | ||
267 | func (tr *transformer) apply(s *state, vx, vy reflect.Value) { | 303 | func (tr *transformer) apply(s *state, vx, vy reflect.Value) { |
268 | // Update path before calling the Transformer so that dynamic checks | 304 | step := Transform{&transform{pathStep{typ: tr.fnc.Type().Out(0)}, tr}} |
269 | // will use the updated path. | 305 | vvx := s.callTRFunc(tr.fnc, vx, step) |
270 | s.curPath.push(&transform{pathStep{tr.fnc.Type().Out(0)}, tr}) | 306 | vvy := s.callTRFunc(tr.fnc, vy, step) |
271 | defer s.curPath.pop() | 307 | step.vx, step.vy = vvx, vvy |
272 | 308 | s.compareAny(step) | |
273 | vx = s.callTRFunc(tr.fnc, vx) | ||
274 | vy = s.callTRFunc(tr.fnc, vy) | ||
275 | s.compareAny(vx, vy) | ||
276 | } | 309 | } |
277 | 310 | ||
278 | func (tr transformer) String() string { | 311 | func (tr transformer) String() string { |
279 | return fmt.Sprintf("Transformer(%s, %s)", tr.name, getFuncName(tr.fnc.Pointer())) | 312 | return fmt.Sprintf("Transformer(%s, %s)", tr.name, function.NameOf(tr.fnc)) |
280 | } | 313 | } |
281 | 314 | ||
282 | // Comparer returns an Option that determines whether two values are equal | 315 | // Comparer returns an Option that determines whether two values are equal |
@@ -311,7 +344,7 @@ type comparer struct { | |||
311 | 344 | ||
312 | func (cm *comparer) isFiltered() bool { return cm.typ != nil } | 345 | func (cm *comparer) isFiltered() bool { return cm.typ != nil } |
313 | 346 | ||
314 | func (cm *comparer) filter(_ *state, _, _ reflect.Value, t reflect.Type) applicableOption { | 347 | func (cm *comparer) filter(_ *state, t reflect.Type, _, _ reflect.Value) applicableOption { |
315 | if cm.typ == nil || t.AssignableTo(cm.typ) { | 348 | if cm.typ == nil || t.AssignableTo(cm.typ) { |
316 | return cm | 349 | return cm |
317 | } | 350 | } |
@@ -320,11 +353,11 @@ func (cm *comparer) filter(_ *state, _, _ reflect.Value, t reflect.Type) applica | |||
320 | 353 | ||
321 | func (cm *comparer) apply(s *state, vx, vy reflect.Value) { | 354 | func (cm *comparer) apply(s *state, vx, vy reflect.Value) { |
322 | eq := s.callTTBFunc(cm.fnc, vx, vy) | 355 | eq := s.callTTBFunc(cm.fnc, vx, vy) |
323 | s.report(eq, vx, vy) | 356 | s.report(eq, reportByFunc) |
324 | } | 357 | } |
325 | 358 | ||
326 | func (cm comparer) String() string { | 359 | func (cm comparer) String() string { |
327 | return fmt.Sprintf("Comparer(%s)", getFuncName(cm.fnc.Pointer())) | 360 | return fmt.Sprintf("Comparer(%s)", function.NameOf(cm.fnc)) |
328 | } | 361 | } |
329 | 362 | ||
330 | // AllowUnexported returns an Option that forcibly allows operations on | 363 | // AllowUnexported returns an Option that forcibly allows operations on |
@@ -338,7 +371,7 @@ func (cm comparer) String() string { | |||
338 | // defined in an internal package where the semantic meaning of an unexported | 371 | // defined in an internal package where the semantic meaning of an unexported |
339 | // field is in the control of the user. | 372 | // field is in the control of the user. |
340 | // | 373 | // |
341 | // For some cases, a custom Comparer should be used instead that defines | 374 | // In many cases, a custom Comparer should be used instead that defines |
342 | // equality as a function of the public API of a type rather than the underlying | 375 | // equality as a function of the public API of a type rather than the underlying |
343 | // unexported implementation. | 376 | // unexported implementation. |
344 | // | 377 | // |
@@ -370,27 +403,92 @@ func AllowUnexported(types ...interface{}) Option { | |||
370 | 403 | ||
371 | type visibleStructs map[reflect.Type]bool | 404 | type visibleStructs map[reflect.Type]bool |
372 | 405 | ||
373 | func (visibleStructs) filter(_ *state, _, _ reflect.Value, _ reflect.Type) applicableOption { | 406 | func (visibleStructs) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption { |
374 | panic("not implemented") | 407 | panic("not implemented") |
375 | } | 408 | } |
376 | 409 | ||
377 | // reporter is an Option that configures how differences are reported. | 410 | // Result represents the comparison result for a single node and |
378 | type reporter interface { | 411 | // is provided by cmp when calling Result (see Reporter). |
379 | // TODO: Not exported yet. | 412 | type Result struct { |
413 | _ [0]func() // Make Result incomparable | ||
414 | flags resultFlags | ||
415 | } | ||
416 | |||
417 | // Equal reports whether the node was determined to be equal or not. | ||
418 | // As a special case, ignored nodes are considered equal. | ||
419 | func (r Result) Equal() bool { | ||
420 | return r.flags&(reportEqual|reportByIgnore) != 0 | ||
421 | } | ||
422 | |||
423 | // ByIgnore reports whether the node is equal because it was ignored. | ||
424 | // This never reports true if Equal reports false. | ||
425 | func (r Result) ByIgnore() bool { | ||
426 | return r.flags&reportByIgnore != 0 | ||
427 | } | ||
428 | |||
429 | // ByMethod reports whether the Equal method determined equality. | ||
430 | func (r Result) ByMethod() bool { | ||
431 | return r.flags&reportByMethod != 0 | ||
432 | } | ||
433 | |||
434 | // ByFunc reports whether a Comparer function determined equality. | ||
435 | func (r Result) ByFunc() bool { | ||
436 | return r.flags&reportByFunc != 0 | ||
437 | } | ||
438 | |||
439 | type resultFlags uint | ||
440 | |||
441 | const ( | ||
442 | _ resultFlags = (1 << iota) / 2 | ||
443 | |||
444 | reportEqual | ||
445 | reportUnequal | ||
446 | reportByIgnore | ||
447 | reportByMethod | ||
448 | reportByFunc | ||
449 | ) | ||
450 | |||
451 | // Reporter is an Option that can be passed to Equal. When Equal traverses | ||
452 | // the value trees, it calls PushStep as it descends into each node in the | ||
453 | // tree and PopStep as it ascend out of the node. The leaves of the tree are | ||
454 | // either compared (determined to be equal or not equal) or ignored and reported | ||
455 | // as such by calling the Report method. | ||
456 | func Reporter(r interface { | ||
457 | // PushStep is called when a tree-traversal operation is performed. | ||
458 | // The PathStep itself is only valid until the step is popped. | ||
459 | // The PathStep.Values are valid for the duration of the entire traversal | ||
460 | // and must not be mutated. | ||
461 | // | ||
462 | // Equal always calls PushStep at the start to provide an operation-less | ||
463 | // PathStep used to report the root values. | ||
380 | // | 464 | // |
381 | // Perhaps add PushStep and PopStep and change Report to only accept | 465 | // Within a slice, the exact set of inserted, removed, or modified elements |
382 | // a PathStep instead of the full-path? Adding a PushStep and PopStep makes | 466 | // is unspecified and may change in future implementations. |
383 | // it clear that we are traversing the value tree in a depth-first-search | 467 | // The entries of a map are iterated through in an unspecified order. |
384 | // manner, which has an effect on how values are printed. | 468 | PushStep(PathStep) |
469 | |||
470 | // Report is called exactly once on leaf nodes to report whether the | ||
471 | // comparison identified the node as equal, unequal, or ignored. | ||
472 | // A leaf node is one that is immediately preceded by and followed by | ||
473 | // a pair of PushStep and PopStep calls. | ||
474 | Report(Result) | ||
475 | |||
476 | // PopStep ascends back up the value tree. | ||
477 | // There is always a matching pop call for every push call. | ||
478 | PopStep() | ||
479 | }) Option { | ||
480 | return reporter{r} | ||
481 | } | ||
385 | 482 | ||
386 | Option | 483 | type reporter struct{ reporterIface } |
484 | type reporterIface interface { | ||
485 | PushStep(PathStep) | ||
486 | Report(Result) | ||
487 | PopStep() | ||
488 | } | ||
387 | 489 | ||
388 | // Report is called for every comparison made and will be provided with | 490 | func (reporter) filter(_ *state, _ reflect.Type, _, _ reflect.Value) applicableOption { |
389 | // the two values being compared, the equality result, and the | 491 | panic("not implemented") |
390 | // current path in the value tree. It is possible for x or y to be an | ||
391 | // invalid reflect.Value if one of the values is non-existent; | ||
392 | // which is possible with maps and slices. | ||
393 | Report(x, y reflect.Value, eq bool, p Path) | ||
394 | } | 492 | } |
395 | 493 | ||
396 | // normalizeOption normalizes the input options such that all Options groups | 494 | // normalizeOption normalizes the input options such that all Options groups |
@@ -424,30 +522,3 @@ func flattenOptions(dst, src Options) Options { | |||
424 | } | 522 | } |
425 | return dst | 523 | return dst |
426 | } | 524 | } |
427 | |||
428 | // getFuncName returns a short function name from the pointer. | ||
429 | // The string parsing logic works up until Go1.9. | ||
430 | func getFuncName(p uintptr) string { | ||
431 | fnc := runtime.FuncForPC(p) | ||
432 | if fnc == nil { | ||
433 | return "<unknown>" | ||
434 | } | ||
435 | name := fnc.Name() // E.g., "long/path/name/mypkg.(mytype).(long/path/name/mypkg.myfunc)-fm" | ||
436 | if strings.HasSuffix(name, ")-fm") || strings.HasSuffix(name, ")·fm") { | ||
437 | // Strip the package name from method name. | ||
438 | name = strings.TrimSuffix(name, ")-fm") | ||
439 | name = strings.TrimSuffix(name, ")·fm") | ||
440 | if i := strings.LastIndexByte(name, '('); i >= 0 { | ||
441 | methodName := name[i+1:] // E.g., "long/path/name/mypkg.myfunc" | ||
442 | if j := strings.LastIndexByte(methodName, '.'); j >= 0 { | ||
443 | methodName = methodName[j+1:] // E.g., "myfunc" | ||
444 | } | ||
445 | name = name[:i] + methodName // E.g., "long/path/name/mypkg.(mytype)." + "myfunc" | ||
446 | } | ||
447 | } | ||
448 | if i := strings.LastIndexByte(name, '/'); i >= 0 { | ||
449 | // Strip the package name. | ||
450 | name = name[i+1:] // E.g., "mypkg.(mytype).myfunc" | ||
451 | } | ||
452 | return name | ||
453 | } | ||
diff --git a/vendor/github.com/google/go-cmp/cmp/path.go b/vendor/github.com/google/go-cmp/cmp/path.go index c08a3cf..96fffd2 100644 --- a/vendor/github.com/google/go-cmp/cmp/path.go +++ b/vendor/github.com/google/go-cmp/cmp/path.go | |||
@@ -12,80 +12,52 @@ import ( | |||
12 | "unicode/utf8" | 12 | "unicode/utf8" |
13 | ) | 13 | ) |
14 | 14 | ||
15 | type ( | 15 | // Path is a list of PathSteps describing the sequence of operations to get |
16 | // Path is a list of PathSteps describing the sequence of operations to get | 16 | // from some root type to the current position in the value tree. |
17 | // from some root type to the current position in the value tree. | 17 | // The first Path element is always an operation-less PathStep that exists |
18 | // The first Path element is always an operation-less PathStep that exists | 18 | // simply to identify the initial type. |
19 | // simply to identify the initial type. | 19 | // |
20 | // | 20 | // When traversing structs with embedded structs, the embedded struct will |
21 | // When traversing structs with embedded structs, the embedded struct will | 21 | // always be accessed as a field before traversing the fields of the |
22 | // always be accessed as a field before traversing the fields of the | 22 | // embedded struct themselves. That is, an exported field from the |
23 | // embedded struct themselves. That is, an exported field from the | 23 | // embedded struct will never be accessed directly from the parent struct. |
24 | // embedded struct will never be accessed directly from the parent struct. | 24 | type Path []PathStep |
25 | Path []PathStep | ||
26 | |||
27 | // PathStep is a union-type for specific operations to traverse | ||
28 | // a value's tree structure. Users of this package never need to implement | ||
29 | // these types as values of this type will be returned by this package. | ||
30 | PathStep interface { | ||
31 | String() string | ||
32 | Type() reflect.Type // Resulting type after performing the path step | ||
33 | isPathStep() | ||
34 | } | ||
35 | 25 | ||
36 | // SliceIndex is an index operation on a slice or array at some index Key. | 26 | // PathStep is a union-type for specific operations to traverse |
37 | SliceIndex interface { | 27 | // a value's tree structure. Users of this package never need to implement |
38 | PathStep | 28 | // these types as values of this type will be returned by this package. |
39 | Key() int // May return -1 if in a split state | 29 | // |
40 | 30 | // Implementations of this interface are | |
41 | // SplitKeys returns the indexes for indexing into slices in the | 31 | // StructField, SliceIndex, MapIndex, Indirect, TypeAssertion, and Transform. |
42 | // x and y values, respectively. These indexes may differ due to the | 32 | type PathStep interface { |
43 | // insertion or removal of an element in one of the slices, causing | 33 | String() string |
44 | // all of the indexes to be shifted. If an index is -1, then that | ||
45 | // indicates that the element does not exist in the associated slice. | ||
46 | // | ||
47 | // Key is guaranteed to return -1 if and only if the indexes returned | ||
48 | // by SplitKeys are not the same. SplitKeys will never return -1 for | ||
49 | // both indexes. | ||
50 | SplitKeys() (x int, y int) | ||
51 | |||
52 | isSliceIndex() | ||
53 | } | ||
54 | // MapIndex is an index operation on a map at some index Key. | ||
55 | MapIndex interface { | ||
56 | PathStep | ||
57 | Key() reflect.Value | ||
58 | isMapIndex() | ||
59 | } | ||
60 | // TypeAssertion represents a type assertion on an interface. | ||
61 | TypeAssertion interface { | ||
62 | PathStep | ||
63 | isTypeAssertion() | ||
64 | } | ||
65 | // StructField represents a struct field access on a field called Name. | ||
66 | StructField interface { | ||
67 | PathStep | ||
68 | Name() string | ||
69 | Index() int | ||
70 | isStructField() | ||
71 | } | ||
72 | // Indirect represents pointer indirection on the parent type. | ||
73 | Indirect interface { | ||
74 | PathStep | ||
75 | isIndirect() | ||
76 | } | ||
77 | // Transform is a transformation from the parent type to the current type. | ||
78 | Transform interface { | ||
79 | PathStep | ||
80 | Name() string | ||
81 | Func() reflect.Value | ||
82 | 34 | ||
83 | // Option returns the originally constructed Transformer option. | 35 | // Type is the resulting type after performing the path step. |
84 | // The == operator can be used to detect the exact option used. | 36 | Type() reflect.Type |
85 | Option() Option | ||
86 | 37 | ||
87 | isTransform() | 38 | // Values is the resulting values after performing the path step. |
88 | } | 39 | // The type of each valid value is guaranteed to be identical to Type. |
40 | // | ||
41 | // In some cases, one or both may be invalid or have restrictions: | ||
42 | // • For StructField, both are not interface-able if the current field | ||
43 | // is unexported and the struct type is not explicitly permitted by | ||
44 | // AllowUnexported to traverse unexported fields. | ||
45 | // • For SliceIndex, one may be invalid if an element is missing from | ||
46 | // either the x or y slice. | ||
47 | // • For MapIndex, one may be invalid if an entry is missing from | ||
48 | // either the x or y map. | ||
49 | // | ||
50 | // The provided values must not be mutated. | ||
51 | Values() (vx, vy reflect.Value) | ||
52 | } | ||
53 | |||
54 | var ( | ||
55 | _ PathStep = StructField{} | ||
56 | _ PathStep = SliceIndex{} | ||
57 | _ PathStep = MapIndex{} | ||
58 | _ PathStep = Indirect{} | ||
59 | _ PathStep = TypeAssertion{} | ||
60 | _ PathStep = Transform{} | ||
89 | ) | 61 | ) |
90 | 62 | ||
91 | func (pa *Path) push(s PathStep) { | 63 | func (pa *Path) push(s PathStep) { |
@@ -124,7 +96,7 @@ func (pa Path) Index(i int) PathStep { | |||
124 | func (pa Path) String() string { | 96 | func (pa Path) String() string { |
125 | var ss []string | 97 | var ss []string |
126 | for _, s := range pa { | 98 | for _, s := range pa { |
127 | if _, ok := s.(*structField); ok { | 99 | if _, ok := s.(StructField); ok { |
128 | ss = append(ss, s.String()) | 100 | ss = append(ss, s.String()) |
129 | } | 101 | } |
130 | } | 102 | } |
@@ -144,13 +116,13 @@ func (pa Path) GoString() string { | |||
144 | nextStep = pa[i+1] | 116 | nextStep = pa[i+1] |
145 | } | 117 | } |
146 | switch s := s.(type) { | 118 | switch s := s.(type) { |
147 | case *indirect: | 119 | case Indirect: |
148 | numIndirect++ | 120 | numIndirect++ |
149 | pPre, pPost := "(", ")" | 121 | pPre, pPost := "(", ")" |
150 | switch nextStep.(type) { | 122 | switch nextStep.(type) { |
151 | case *indirect: | 123 | case Indirect: |
152 | continue // Next step is indirection, so let them batch up | 124 | continue // Next step is indirection, so let them batch up |
153 | case *structField: | 125 | case StructField: |
154 | numIndirect-- // Automatic indirection on struct fields | 126 | numIndirect-- // Automatic indirection on struct fields |
155 | case nil: | 127 | case nil: |
156 | pPre, pPost = "", "" // Last step; no need for parenthesis | 128 | pPre, pPost = "", "" // Last step; no need for parenthesis |
@@ -161,19 +133,10 @@ func (pa Path) GoString() string { | |||
161 | } | 133 | } |
162 | numIndirect = 0 | 134 | numIndirect = 0 |
163 | continue | 135 | continue |
164 | case *transform: | 136 | case Transform: |
165 | ssPre = append(ssPre, s.trans.name+"(") | 137 | ssPre = append(ssPre, s.trans.name+"(") |
166 | ssPost = append(ssPost, ")") | 138 | ssPost = append(ssPost, ")") |
167 | continue | 139 | continue |
168 | case *typeAssertion: | ||
169 | // As a special-case, elide type assertions on anonymous types | ||
170 | // since they are typically generated dynamically and can be very | ||
171 | // verbose. For example, some transforms return interface{} because | ||
172 | // of Go's lack of generics, but typically take in and return the | ||
173 | // exact same concrete type. | ||
174 | if s.Type().PkgPath() == "" { | ||
175 | continue | ||
176 | } | ||
177 | } | 140 | } |
178 | ssPost = append(ssPost, s.String()) | 141 | ssPost = append(ssPost, s.String()) |
179 | } | 142 | } |
@@ -183,44 +146,13 @@ func (pa Path) GoString() string { | |||
183 | return strings.Join(ssPre, "") + strings.Join(ssPost, "") | 146 | return strings.Join(ssPre, "") + strings.Join(ssPost, "") |
184 | } | 147 | } |
185 | 148 | ||
186 | type ( | 149 | type pathStep struct { |
187 | pathStep struct { | 150 | typ reflect.Type |
188 | typ reflect.Type | 151 | vx, vy reflect.Value |
189 | } | 152 | } |
190 | |||
191 | sliceIndex struct { | ||
192 | pathStep | ||
193 | xkey, ykey int | ||
194 | } | ||
195 | mapIndex struct { | ||
196 | pathStep | ||
197 | key reflect.Value | ||
198 | } | ||
199 | typeAssertion struct { | ||
200 | pathStep | ||
201 | } | ||
202 | structField struct { | ||
203 | pathStep | ||
204 | name string | ||
205 | idx int | ||
206 | |||
207 | // These fields are used for forcibly accessing an unexported field. | ||
208 | // pvx, pvy, and field are only valid if unexported is true. | ||
209 | unexported bool | ||
210 | force bool // Forcibly allow visibility | ||
211 | pvx, pvy reflect.Value // Parent values | ||
212 | field reflect.StructField // Field information | ||
213 | } | ||
214 | indirect struct { | ||
215 | pathStep | ||
216 | } | ||
217 | transform struct { | ||
218 | pathStep | ||
219 | trans *transformer | ||
220 | } | ||
221 | ) | ||
222 | 153 | ||
223 | func (ps pathStep) Type() reflect.Type { return ps.typ } | 154 | func (ps pathStep) Type() reflect.Type { return ps.typ } |
155 | func (ps pathStep) Values() (vx, vy reflect.Value) { return ps.vx, ps.vy } | ||
224 | func (ps pathStep) String() string { | 156 | func (ps pathStep) String() string { |
225 | if ps.typ == nil { | 157 | if ps.typ == nil { |
226 | return "<nil>" | 158 | return "<nil>" |
@@ -232,7 +164,54 @@ func (ps pathStep) String() string { | |||
232 | return fmt.Sprintf("{%s}", s) | 164 | return fmt.Sprintf("{%s}", s) |
233 | } | 165 | } |
234 | 166 | ||
235 | func (si sliceIndex) String() string { | 167 | // StructField represents a struct field access on a field called Name. |
168 | type StructField struct{ *structField } | ||
169 | type structField struct { | ||
170 | pathStep | ||
171 | name string | ||
172 | idx int | ||
173 | |||
174 | // These fields are used for forcibly accessing an unexported field. | ||
175 | // pvx, pvy, and field are only valid if unexported is true. | ||
176 | unexported bool | ||
177 | mayForce bool // Forcibly allow visibility | ||
178 | pvx, pvy reflect.Value // Parent values | ||
179 | field reflect.StructField // Field information | ||
180 | } | ||
181 | |||
182 | func (sf StructField) Type() reflect.Type { return sf.typ } | ||
183 | func (sf StructField) Values() (vx, vy reflect.Value) { | ||
184 | if !sf.unexported { | ||
185 | return sf.vx, sf.vy // CanInterface reports true | ||
186 | } | ||
187 | |||
188 | // Forcibly obtain read-write access to an unexported struct field. | ||
189 | if sf.mayForce { | ||
190 | vx = retrieveUnexportedField(sf.pvx, sf.field) | ||
191 | vy = retrieveUnexportedField(sf.pvy, sf.field) | ||
192 | return vx, vy // CanInterface reports true | ||
193 | } | ||
194 | return sf.vx, sf.vy // CanInterface reports false | ||
195 | } | ||
196 | func (sf StructField) String() string { return fmt.Sprintf(".%s", sf.name) } | ||
197 | |||
198 | // Name is the field name. | ||
199 | func (sf StructField) Name() string { return sf.name } | ||
200 | |||
201 | // Index is the index of the field in the parent struct type. | ||
202 | // See reflect.Type.Field. | ||
203 | func (sf StructField) Index() int { return sf.idx } | ||
204 | |||
205 | // SliceIndex is an index operation on a slice or array at some index Key. | ||
206 | type SliceIndex struct{ *sliceIndex } | ||
207 | type sliceIndex struct { | ||
208 | pathStep | ||
209 | xkey, ykey int | ||
210 | } | ||
211 | |||
212 | func (si SliceIndex) Type() reflect.Type { return si.typ } | ||
213 | func (si SliceIndex) Values() (vx, vy reflect.Value) { return si.vx, si.vy } | ||
214 | func (si SliceIndex) String() string { | ||
236 | switch { | 215 | switch { |
237 | case si.xkey == si.ykey: | 216 | case si.xkey == si.ykey: |
238 | return fmt.Sprintf("[%d]", si.xkey) | 217 | return fmt.Sprintf("[%d]", si.xkey) |
@@ -247,63 +226,83 @@ func (si sliceIndex) String() string { | |||
247 | return fmt.Sprintf("[%d->%d]", si.xkey, si.ykey) | 226 | return fmt.Sprintf("[%d->%d]", si.xkey, si.ykey) |
248 | } | 227 | } |
249 | } | 228 | } |
250 | func (mi mapIndex) String() string { return fmt.Sprintf("[%#v]", mi.key) } | ||
251 | func (ta typeAssertion) String() string { return fmt.Sprintf(".(%v)", ta.typ) } | ||
252 | func (sf structField) String() string { return fmt.Sprintf(".%s", sf.name) } | ||
253 | func (in indirect) String() string { return "*" } | ||
254 | func (tf transform) String() string { return fmt.Sprintf("%s()", tf.trans.name) } | ||
255 | 229 | ||
256 | func (si sliceIndex) Key() int { | 230 | // Key is the index key; it may return -1 if in a split state |
231 | func (si SliceIndex) Key() int { | ||
257 | if si.xkey != si.ykey { | 232 | if si.xkey != si.ykey { |
258 | return -1 | 233 | return -1 |
259 | } | 234 | } |
260 | return si.xkey | 235 | return si.xkey |
261 | } | 236 | } |
262 | func (si sliceIndex) SplitKeys() (x, y int) { return si.xkey, si.ykey } | ||
263 | func (mi mapIndex) Key() reflect.Value { return mi.key } | ||
264 | func (sf structField) Name() string { return sf.name } | ||
265 | func (sf structField) Index() int { return sf.idx } | ||
266 | func (tf transform) Name() string { return tf.trans.name } | ||
267 | func (tf transform) Func() reflect.Value { return tf.trans.fnc } | ||
268 | func (tf transform) Option() Option { return tf.trans } | ||
269 | |||
270 | func (pathStep) isPathStep() {} | ||
271 | func (sliceIndex) isSliceIndex() {} | ||
272 | func (mapIndex) isMapIndex() {} | ||
273 | func (typeAssertion) isTypeAssertion() {} | ||
274 | func (structField) isStructField() {} | ||
275 | func (indirect) isIndirect() {} | ||
276 | func (transform) isTransform() {} | ||
277 | 237 | ||
278 | var ( | 238 | // SplitKeys are the indexes for indexing into slices in the |
279 | _ SliceIndex = sliceIndex{} | 239 | // x and y values, respectively. These indexes may differ due to the |
280 | _ MapIndex = mapIndex{} | 240 | // insertion or removal of an element in one of the slices, causing |
281 | _ TypeAssertion = typeAssertion{} | 241 | // all of the indexes to be shifted. If an index is -1, then that |
282 | _ StructField = structField{} | 242 | // indicates that the element does not exist in the associated slice. |
283 | _ Indirect = indirect{} | 243 | // |
284 | _ Transform = transform{} | 244 | // Key is guaranteed to return -1 if and only if the indexes returned |
285 | 245 | // by SplitKeys are not the same. SplitKeys will never return -1 for | |
286 | _ PathStep = sliceIndex{} | 246 | // both indexes. |
287 | _ PathStep = mapIndex{} | 247 | func (si SliceIndex) SplitKeys() (ix, iy int) { return si.xkey, si.ykey } |
288 | _ PathStep = typeAssertion{} | 248 | |
289 | _ PathStep = structField{} | 249 | // MapIndex is an index operation on a map at some index Key. |
290 | _ PathStep = indirect{} | 250 | type MapIndex struct{ *mapIndex } |
291 | _ PathStep = transform{} | 251 | type mapIndex struct { |
292 | ) | 252 | pathStep |
253 | key reflect.Value | ||
254 | } | ||
255 | |||
256 | func (mi MapIndex) Type() reflect.Type { return mi.typ } | ||
257 | func (mi MapIndex) Values() (vx, vy reflect.Value) { return mi.vx, mi.vy } | ||
258 | func (mi MapIndex) String() string { return fmt.Sprintf("[%#v]", mi.key) } | ||
259 | |||
260 | // Key is the value of the map key. | ||
261 | func (mi MapIndex) Key() reflect.Value { return mi.key } | ||
262 | |||
263 | // Indirect represents pointer indirection on the parent type. | ||
264 | type Indirect struct{ *indirect } | ||
265 | type indirect struct { | ||
266 | pathStep | ||
267 | } | ||
268 | |||
269 | func (in Indirect) Type() reflect.Type { return in.typ } | ||
270 | func (in Indirect) Values() (vx, vy reflect.Value) { return in.vx, in.vy } | ||
271 | func (in Indirect) String() string { return "*" } | ||
272 | |||
273 | // TypeAssertion represents a type assertion on an interface. | ||
274 | type TypeAssertion struct{ *typeAssertion } | ||
275 | type typeAssertion struct { | ||
276 | pathStep | ||
277 | } | ||
278 | |||
279 | func (ta TypeAssertion) Type() reflect.Type { return ta.typ } | ||
280 | func (ta TypeAssertion) Values() (vx, vy reflect.Value) { return ta.vx, ta.vy } | ||
281 | func (ta TypeAssertion) String() string { return fmt.Sprintf(".(%v)", ta.typ) } | ||
282 | |||
283 | // Transform is a transformation from the parent type to the current type. | ||
284 | type Transform struct{ *transform } | ||
285 | type transform struct { | ||
286 | pathStep | ||
287 | trans *transformer | ||
288 | } | ||
289 | |||
290 | func (tf Transform) Type() reflect.Type { return tf.typ } | ||
291 | func (tf Transform) Values() (vx, vy reflect.Value) { return tf.vx, tf.vy } | ||
292 | func (tf Transform) String() string { return fmt.Sprintf("%s()", tf.trans.name) } | ||
293 | |||
294 | // Name is the name of the Transformer. | ||
295 | func (tf Transform) Name() string { return tf.trans.name } | ||
296 | |||
297 | // Func is the function pointer to the transformer function. | ||
298 | func (tf Transform) Func() reflect.Value { return tf.trans.fnc } | ||
299 | |||
300 | // Option returns the originally constructed Transformer option. | ||
301 | // The == operator can be used to detect the exact option used. | ||
302 | func (tf Transform) Option() Option { return tf.trans } | ||
293 | 303 | ||
294 | // isExported reports whether the identifier is exported. | 304 | // isExported reports whether the identifier is exported. |
295 | func isExported(id string) bool { | 305 | func isExported(id string) bool { |
296 | r, _ := utf8.DecodeRuneInString(id) | 306 | r, _ := utf8.DecodeRuneInString(id) |
297 | return unicode.IsUpper(r) | 307 | return unicode.IsUpper(r) |
298 | } | 308 | } |
299 | |||
300 | // isValid reports whether the identifier is valid. | ||
301 | // Empty and underscore-only strings are not valid. | ||
302 | func isValid(id string) bool { | ||
303 | ok := id != "" && id != "_" | ||
304 | for j, c := range id { | ||
305 | ok = ok && (j > 0 || !unicode.IsDigit(c)) | ||
306 | ok = ok && (c == '_' || unicode.IsLetter(c) || unicode.IsDigit(c)) | ||
307 | } | ||
308 | return ok | ||
309 | } | ||
diff --git a/vendor/github.com/google/go-cmp/cmp/report.go b/vendor/github.com/google/go-cmp/cmp/report.go new file mode 100644 index 0000000..6ddf299 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/report.go | |||
@@ -0,0 +1,51 @@ | |||
1 | // Copyright 2017, The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE.md file. | ||
4 | |||
5 | package cmp | ||
6 | |||
7 | // defaultReporter implements the reporter interface. | ||
8 | // | ||
9 | // As Equal serially calls the PushStep, Report, and PopStep methods, the | ||
10 | // defaultReporter constructs a tree-based representation of the compared value | ||
11 | // and the result of each comparison (see valueNode). | ||
12 | // | ||
13 | // When the String method is called, the FormatDiff method transforms the | ||
14 | // valueNode tree into a textNode tree, which is a tree-based representation | ||
15 | // of the textual output (see textNode). | ||
16 | // | ||
17 | // Lastly, the textNode.String method produces the final report as a string. | ||
18 | type defaultReporter struct { | ||
19 | root *valueNode | ||
20 | curr *valueNode | ||
21 | } | ||
22 | |||
23 | func (r *defaultReporter) PushStep(ps PathStep) { | ||
24 | r.curr = r.curr.PushStep(ps) | ||
25 | if r.root == nil { | ||
26 | r.root = r.curr | ||
27 | } | ||
28 | } | ||
29 | func (r *defaultReporter) Report(rs Result) { | ||
30 | r.curr.Report(rs) | ||
31 | } | ||
32 | func (r *defaultReporter) PopStep() { | ||
33 | r.curr = r.curr.PopStep() | ||
34 | } | ||
35 | |||
36 | // String provides a full report of the differences detected as a structured | ||
37 | // literal in pseudo-Go syntax. String may only be called after the entire tree | ||
38 | // has been traversed. | ||
39 | func (r *defaultReporter) String() string { | ||
40 | assert(r.root != nil && r.curr == nil) | ||
41 | if r.root.NumDiff == 0 { | ||
42 | return "" | ||
43 | } | ||
44 | return formatOptions{}.FormatDiff(r.root).String() | ||
45 | } | ||
46 | |||
47 | func assert(ok bool) { | ||
48 | if !ok { | ||
49 | panic("assertion failure") | ||
50 | } | ||
51 | } | ||
diff --git a/vendor/github.com/google/go-cmp/cmp/report_compare.go b/vendor/github.com/google/go-cmp/cmp/report_compare.go new file mode 100644 index 0000000..05efb99 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/report_compare.go | |||
@@ -0,0 +1,296 @@ | |||
1 | // Copyright 2019, The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE.md file. | ||
4 | |||
5 | package cmp | ||
6 | |||
7 | import ( | ||
8 | "fmt" | ||
9 | "reflect" | ||
10 | |||
11 | "github.com/google/go-cmp/cmp/internal/value" | ||
12 | ) | ||
13 | |||
14 | // TODO: Enforce limits? | ||
15 | // * Enforce maximum number of records to print per node? | ||
16 | // * Enforce maximum size in bytes allowed? | ||
17 | // * As a heuristic, use less verbosity for equal nodes than unequal nodes. | ||
18 | // TODO: Enforce unique outputs? | ||
19 | // * Avoid Stringer methods if it results in same output? | ||
20 | // * Print pointer address if outputs still equal? | ||
21 | |||
22 | // numContextRecords is the number of surrounding equal records to print. | ||
23 | const numContextRecords = 2 | ||
24 | |||
25 | type diffMode byte | ||
26 | |||
27 | const ( | ||
28 | diffUnknown diffMode = 0 | ||
29 | diffIdentical diffMode = ' ' | ||
30 | diffRemoved diffMode = '-' | ||
31 | diffInserted diffMode = '+' | ||
32 | ) | ||
33 | |||
34 | type typeMode int | ||
35 | |||
36 | const ( | ||
37 | // emitType always prints the type. | ||
38 | emitType typeMode = iota | ||
39 | // elideType never prints the type. | ||
40 | elideType | ||
41 | // autoType prints the type only for composite kinds | ||
42 | // (i.e., structs, slices, arrays, and maps). | ||
43 | autoType | ||
44 | ) | ||
45 | |||
46 | type formatOptions struct { | ||
47 | // DiffMode controls the output mode of FormatDiff. | ||
48 | // | ||
49 | // If diffUnknown, then produce a diff of the x and y values. | ||
50 | // If diffIdentical, then emit values as if they were equal. | ||
51 | // If diffRemoved, then only emit x values (ignoring y values). | ||
52 | // If diffInserted, then only emit y values (ignoring x values). | ||
53 | DiffMode diffMode | ||
54 | |||
55 | // TypeMode controls whether to print the type for the current node. | ||
56 | // | ||
57 | // As a general rule of thumb, we always print the type of the next node | ||
58 | // after an interface, and always elide the type of the next node after | ||
59 | // a slice or map node. | ||
60 | TypeMode typeMode | ||
61 | |||
62 | // formatValueOptions are options specific to printing reflect.Values. | ||
63 | formatValueOptions | ||
64 | } | ||
65 | |||
66 | func (opts formatOptions) WithDiffMode(d diffMode) formatOptions { | ||
67 | opts.DiffMode = d | ||
68 | return opts | ||
69 | } | ||
70 | func (opts formatOptions) WithTypeMode(t typeMode) formatOptions { | ||
71 | opts.TypeMode = t | ||
72 | return opts | ||
73 | } | ||
74 | |||
75 | // FormatDiff converts a valueNode tree into a textNode tree, where the later | ||
76 | // is a textual representation of the differences detected in the former. | ||
77 | func (opts formatOptions) FormatDiff(v *valueNode) textNode { | ||
78 | // Check whether we have specialized formatting for this node. | ||
79 | // This is not necessary, but helpful for producing more readable outputs. | ||
80 | if opts.CanFormatDiffSlice(v) { | ||
81 | return opts.FormatDiffSlice(v) | ||
82 | } | ||
83 | |||
84 | // For leaf nodes, format the value based on the reflect.Values alone. | ||
85 | if v.MaxDepth == 0 { | ||
86 | switch opts.DiffMode { | ||
87 | case diffUnknown, diffIdentical: | ||
88 | // Format Equal. | ||
89 | if v.NumDiff == 0 { | ||
90 | outx := opts.FormatValue(v.ValueX, visitedPointers{}) | ||
91 | outy := opts.FormatValue(v.ValueY, visitedPointers{}) | ||
92 | if v.NumIgnored > 0 && v.NumSame == 0 { | ||
93 | return textEllipsis | ||
94 | } else if outx.Len() < outy.Len() { | ||
95 | return outx | ||
96 | } else { | ||
97 | return outy | ||
98 | } | ||
99 | } | ||
100 | |||
101 | // Format unequal. | ||
102 | assert(opts.DiffMode == diffUnknown) | ||
103 | var list textList | ||
104 | outx := opts.WithTypeMode(elideType).FormatValue(v.ValueX, visitedPointers{}) | ||
105 | outy := opts.WithTypeMode(elideType).FormatValue(v.ValueY, visitedPointers{}) | ||
106 | if outx != nil { | ||
107 | list = append(list, textRecord{Diff: '-', Value: outx}) | ||
108 | } | ||
109 | if outy != nil { | ||
110 | list = append(list, textRecord{Diff: '+', Value: outy}) | ||
111 | } | ||
112 | return opts.WithTypeMode(emitType).FormatType(v.Type, list) | ||
113 | case diffRemoved: | ||
114 | return opts.FormatValue(v.ValueX, visitedPointers{}) | ||
115 | case diffInserted: | ||
116 | return opts.FormatValue(v.ValueY, visitedPointers{}) | ||
117 | default: | ||
118 | panic("invalid diff mode") | ||
119 | } | ||
120 | } | ||
121 | |||
122 | // Descend into the child value node. | ||
123 | if v.TransformerName != "" { | ||
124 | out := opts.WithTypeMode(emitType).FormatDiff(v.Value) | ||
125 | out = textWrap{"Inverse(" + v.TransformerName + ", ", out, ")"} | ||
126 | return opts.FormatType(v.Type, out) | ||
127 | } else { | ||
128 | switch k := v.Type.Kind(); k { | ||
129 | case reflect.Struct, reflect.Array, reflect.Slice, reflect.Map: | ||
130 | return opts.FormatType(v.Type, opts.formatDiffList(v.Records, k)) | ||
131 | case reflect.Ptr: | ||
132 | return textWrap{"&", opts.FormatDiff(v.Value), ""} | ||
133 | case reflect.Interface: | ||
134 | return opts.WithTypeMode(emitType).FormatDiff(v.Value) | ||
135 | default: | ||
136 | panic(fmt.Sprintf("%v cannot have children", k)) | ||
137 | } | ||
138 | } | ||
139 | } | ||
140 | |||
141 | func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind) textNode { | ||
142 | // Derive record name based on the data structure kind. | ||
143 | var name string | ||
144 | var formatKey func(reflect.Value) string | ||
145 | switch k { | ||
146 | case reflect.Struct: | ||
147 | name = "field" | ||
148 | opts = opts.WithTypeMode(autoType) | ||
149 | formatKey = func(v reflect.Value) string { return v.String() } | ||
150 | case reflect.Slice, reflect.Array: | ||
151 | name = "element" | ||
152 | opts = opts.WithTypeMode(elideType) | ||
153 | formatKey = func(reflect.Value) string { return "" } | ||
154 | case reflect.Map: | ||
155 | name = "entry" | ||
156 | opts = opts.WithTypeMode(elideType) | ||
157 | formatKey = formatMapKey | ||
158 | } | ||
159 | |||
160 | // Handle unification. | ||
161 | switch opts.DiffMode { | ||
162 | case diffIdentical, diffRemoved, diffInserted: | ||
163 | var list textList | ||
164 | var deferredEllipsis bool // Add final "..." to indicate records were dropped | ||
165 | for _, r := range recs { | ||
166 | // Elide struct fields that are zero value. | ||
167 | if k == reflect.Struct { | ||
168 | var isZero bool | ||
169 | switch opts.DiffMode { | ||
170 | case diffIdentical: | ||
171 | isZero = value.IsZero(r.Value.ValueX) || value.IsZero(r.Value.ValueX) | ||
172 | case diffRemoved: | ||
173 | isZero = value.IsZero(r.Value.ValueX) | ||
174 | case diffInserted: | ||
175 | isZero = value.IsZero(r.Value.ValueY) | ||
176 | } | ||
177 | if isZero { | ||
178 | continue | ||
179 | } | ||
180 | } | ||
181 | // Elide ignored nodes. | ||
182 | if r.Value.NumIgnored > 0 && r.Value.NumSame+r.Value.NumDiff == 0 { | ||
183 | deferredEllipsis = !(k == reflect.Slice || k == reflect.Array) | ||
184 | if !deferredEllipsis { | ||
185 | list.AppendEllipsis(diffStats{}) | ||
186 | } | ||
187 | continue | ||
188 | } | ||
189 | if out := opts.FormatDiff(r.Value); out != nil { | ||
190 | list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) | ||
191 | } | ||
192 | } | ||
193 | if deferredEllipsis { | ||
194 | list.AppendEllipsis(diffStats{}) | ||
195 | } | ||
196 | return textWrap{"{", list, "}"} | ||
197 | case diffUnknown: | ||
198 | default: | ||
199 | panic("invalid diff mode") | ||
200 | } | ||
201 | |||
202 | // Handle differencing. | ||
203 | var list textList | ||
204 | groups := coalesceAdjacentRecords(name, recs) | ||
205 | for i, ds := range groups { | ||
206 | // Handle equal records. | ||
207 | if ds.NumDiff() == 0 { | ||
208 | // Compute the number of leading and trailing records to print. | ||
209 | var numLo, numHi int | ||
210 | numEqual := ds.NumIgnored + ds.NumIdentical | ||
211 | for numLo < numContextRecords && numLo+numHi < numEqual && i != 0 { | ||
212 | if r := recs[numLo].Value; r.NumIgnored > 0 && r.NumSame+r.NumDiff == 0 { | ||
213 | break | ||
214 | } | ||
215 | numLo++ | ||
216 | } | ||
217 | for numHi < numContextRecords && numLo+numHi < numEqual && i != len(groups)-1 { | ||
218 | if r := recs[numEqual-numHi-1].Value; r.NumIgnored > 0 && r.NumSame+r.NumDiff == 0 { | ||
219 | break | ||
220 | } | ||
221 | numHi++ | ||
222 | } | ||
223 | if numEqual-(numLo+numHi) == 1 && ds.NumIgnored == 0 { | ||
224 | numHi++ // Avoid pointless coalescing of a single equal record | ||
225 | } | ||
226 | |||
227 | // Format the equal values. | ||
228 | for _, r := range recs[:numLo] { | ||
229 | out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value) | ||
230 | list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) | ||
231 | } | ||
232 | if numEqual > numLo+numHi { | ||
233 | ds.NumIdentical -= numLo + numHi | ||
234 | list.AppendEllipsis(ds) | ||
235 | } | ||
236 | for _, r := range recs[numEqual-numHi : numEqual] { | ||
237 | out := opts.WithDiffMode(diffIdentical).FormatDiff(r.Value) | ||
238 | list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) | ||
239 | } | ||
240 | recs = recs[numEqual:] | ||
241 | continue | ||
242 | } | ||
243 | |||
244 | // Handle unequal records. | ||
245 | for _, r := range recs[:ds.NumDiff()] { | ||
246 | switch { | ||
247 | case opts.CanFormatDiffSlice(r.Value): | ||
248 | out := opts.FormatDiffSlice(r.Value) | ||
249 | list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) | ||
250 | case r.Value.NumChildren == r.Value.MaxDepth: | ||
251 | outx := opts.WithDiffMode(diffRemoved).FormatDiff(r.Value) | ||
252 | outy := opts.WithDiffMode(diffInserted).FormatDiff(r.Value) | ||
253 | if outx != nil { | ||
254 | list = append(list, textRecord{Diff: diffRemoved, Key: formatKey(r.Key), Value: outx}) | ||
255 | } | ||
256 | if outy != nil { | ||
257 | list = append(list, textRecord{Diff: diffInserted, Key: formatKey(r.Key), Value: outy}) | ||
258 | } | ||
259 | default: | ||
260 | out := opts.FormatDiff(r.Value) | ||
261 | list = append(list, textRecord{Key: formatKey(r.Key), Value: out}) | ||
262 | } | ||
263 | } | ||
264 | recs = recs[ds.NumDiff():] | ||
265 | } | ||
266 | assert(len(recs) == 0) | ||
267 | return textWrap{"{", list, "}"} | ||
268 | } | ||
269 | |||
270 | // coalesceAdjacentRecords coalesces the list of records into groups of | ||
271 | // adjacent equal, or unequal counts. | ||
272 | func coalesceAdjacentRecords(name string, recs []reportRecord) (groups []diffStats) { | ||
273 | var prevCase int // Arbitrary index into which case last occurred | ||
274 | lastStats := func(i int) *diffStats { | ||
275 | if prevCase != i { | ||
276 | groups = append(groups, diffStats{Name: name}) | ||
277 | prevCase = i | ||
278 | } | ||
279 | return &groups[len(groups)-1] | ||
280 | } | ||
281 | for _, r := range recs { | ||
282 | switch rv := r.Value; { | ||
283 | case rv.NumIgnored > 0 && rv.NumSame+rv.NumDiff == 0: | ||
284 | lastStats(1).NumIgnored++ | ||
285 | case rv.NumDiff == 0: | ||
286 | lastStats(1).NumIdentical++ | ||
287 | case rv.NumDiff > 0 && !rv.ValueY.IsValid(): | ||
288 | lastStats(2).NumRemoved++ | ||
289 | case rv.NumDiff > 0 && !rv.ValueX.IsValid(): | ||
290 | lastStats(2).NumInserted++ | ||
291 | default: | ||
292 | lastStats(2).NumModified++ | ||
293 | } | ||
294 | } | ||
295 | return groups | ||
296 | } | ||
diff --git a/vendor/github.com/google/go-cmp/cmp/report_reflect.go b/vendor/github.com/google/go-cmp/cmp/report_reflect.go new file mode 100644 index 0000000..5521c60 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/report_reflect.go | |||
@@ -0,0 +1,279 @@ | |||
1 | // Copyright 2019, The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE.md file. | ||
4 | |||
5 | package cmp | ||
6 | |||
7 | import ( | ||
8 | "fmt" | ||
9 | "reflect" | ||
10 | "strconv" | ||
11 | "strings" | ||
12 | "unicode" | ||
13 | |||
14 | "github.com/google/go-cmp/cmp/internal/flags" | ||
15 | "github.com/google/go-cmp/cmp/internal/value" | ||
16 | ) | ||
17 | |||
18 | type formatValueOptions struct { | ||
19 | // AvoidStringer controls whether to avoid calling custom stringer | ||
20 | // methods like error.Error or fmt.Stringer.String. | ||
21 | AvoidStringer bool | ||
22 | |||
23 | // ShallowPointers controls whether to avoid descending into pointers. | ||
24 | // Useful when printing map keys, where pointer comparison is performed | ||
25 | // on the pointer address rather than the pointed-at value. | ||
26 | ShallowPointers bool | ||
27 | |||
28 | // PrintAddresses controls whether to print the address of all pointers, | ||
29 | // slice elements, and maps. | ||
30 | PrintAddresses bool | ||
31 | } | ||
32 | |||
33 | // FormatType prints the type as if it were wrapping s. | ||
34 | // This may return s as-is depending on the current type and TypeMode mode. | ||
35 | func (opts formatOptions) FormatType(t reflect.Type, s textNode) textNode { | ||
36 | // Check whether to emit the type or not. | ||
37 | switch opts.TypeMode { | ||
38 | case autoType: | ||
39 | switch t.Kind() { | ||
40 | case reflect.Struct, reflect.Slice, reflect.Array, reflect.Map: | ||
41 | if s.Equal(textNil) { | ||
42 | return s | ||
43 | } | ||
44 | default: | ||
45 | return s | ||
46 | } | ||
47 | case elideType: | ||
48 | return s | ||
49 | } | ||
50 | |||
51 | // Determine the type label, applying special handling for unnamed types. | ||
52 | typeName := t.String() | ||
53 | if t.Name() == "" { | ||
54 | // According to Go grammar, certain type literals contain symbols that | ||
55 | // do not strongly bind to the next lexicographical token (e.g., *T). | ||
56 | switch t.Kind() { | ||
57 | case reflect.Chan, reflect.Func, reflect.Ptr: | ||
58 | typeName = "(" + typeName + ")" | ||
59 | } | ||
60 | typeName = strings.Replace(typeName, "struct {", "struct{", -1) | ||
61 | typeName = strings.Replace(typeName, "interface {", "interface{", -1) | ||
62 | } | ||
63 | |||
64 | // Avoid wrap the value in parenthesis if unnecessary. | ||
65 | if s, ok := s.(textWrap); ok { | ||
66 | hasParens := strings.HasPrefix(s.Prefix, "(") && strings.HasSuffix(s.Suffix, ")") | ||
67 | hasBraces := strings.HasPrefix(s.Prefix, "{") && strings.HasSuffix(s.Suffix, "}") | ||
68 | if hasParens || hasBraces { | ||
69 | return textWrap{typeName, s, ""} | ||
70 | } | ||
71 | } | ||
72 | return textWrap{typeName + "(", s, ")"} | ||
73 | } | ||
74 | |||
75 | // FormatValue prints the reflect.Value, taking extra care to avoid descending | ||
76 | // into pointers already in m. As pointers are visited, m is also updated. | ||
77 | func (opts formatOptions) FormatValue(v reflect.Value, m visitedPointers) (out textNode) { | ||
78 | if !v.IsValid() { | ||
79 | return nil | ||
80 | } | ||
81 | t := v.Type() | ||
82 | |||
83 | // Check whether there is an Error or String method to call. | ||
84 | if !opts.AvoidStringer && v.CanInterface() { | ||
85 | // Avoid calling Error or String methods on nil receivers since many | ||
86 | // implementations crash when doing so. | ||
87 | if (t.Kind() != reflect.Ptr && t.Kind() != reflect.Interface) || !v.IsNil() { | ||
88 | switch v := v.Interface().(type) { | ||
89 | case error: | ||
90 | return textLine("e" + formatString(v.Error())) | ||
91 | case fmt.Stringer: | ||
92 | return textLine("s" + formatString(v.String())) | ||
93 | } | ||
94 | } | ||
95 | } | ||
96 | |||
97 | // Check whether to explicitly wrap the result with the type. | ||
98 | var skipType bool | ||
99 | defer func() { | ||
100 | if !skipType { | ||
101 | out = opts.FormatType(t, out) | ||
102 | } | ||
103 | }() | ||
104 | |||
105 | var ptr string | ||
106 | switch t.Kind() { | ||
107 | case reflect.Bool: | ||
108 | return textLine(fmt.Sprint(v.Bool())) | ||
109 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||
110 | return textLine(fmt.Sprint(v.Int())) | ||
111 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | ||
112 | // Unnamed uints are usually bytes or words, so use hexadecimal. | ||
113 | if t.PkgPath() == "" || t.Kind() == reflect.Uintptr { | ||
114 | return textLine(formatHex(v.Uint())) | ||
115 | } | ||
116 | return textLine(fmt.Sprint(v.Uint())) | ||
117 | case reflect.Float32, reflect.Float64: | ||
118 | return textLine(fmt.Sprint(v.Float())) | ||
119 | case reflect.Complex64, reflect.Complex128: | ||
120 | return textLine(fmt.Sprint(v.Complex())) | ||
121 | case reflect.String: | ||
122 | return textLine(formatString(v.String())) | ||
123 | case reflect.UnsafePointer, reflect.Chan, reflect.Func: | ||
124 | return textLine(formatPointer(v)) | ||
125 | case reflect.Struct: | ||
126 | var list textList | ||
127 | for i := 0; i < v.NumField(); i++ { | ||
128 | vv := v.Field(i) | ||
129 | if value.IsZero(vv) { | ||
130 | continue // Elide fields with zero values | ||
131 | } | ||
132 | s := opts.WithTypeMode(autoType).FormatValue(vv, m) | ||
133 | list = append(list, textRecord{Key: t.Field(i).Name, Value: s}) | ||
134 | } | ||
135 | return textWrap{"{", list, "}"} | ||
136 | case reflect.Slice: | ||
137 | if v.IsNil() { | ||
138 | return textNil | ||
139 | } | ||
140 | if opts.PrintAddresses { | ||
141 | ptr = formatPointer(v) | ||
142 | } | ||
143 | fallthrough | ||
144 | case reflect.Array: | ||
145 | var list textList | ||
146 | for i := 0; i < v.Len(); i++ { | ||
147 | vi := v.Index(i) | ||
148 | if vi.CanAddr() { // Check for cyclic elements | ||
149 | p := vi.Addr() | ||
150 | if m.Visit(p) { | ||
151 | var out textNode | ||
152 | out = textLine(formatPointer(p)) | ||
153 | out = opts.WithTypeMode(emitType).FormatType(p.Type(), out) | ||
154 | out = textWrap{"*", out, ""} | ||
155 | list = append(list, textRecord{Value: out}) | ||
156 | continue | ||
157 | } | ||
158 | } | ||
159 | s := opts.WithTypeMode(elideType).FormatValue(vi, m) | ||
160 | list = append(list, textRecord{Value: s}) | ||
161 | } | ||
162 | return textWrap{ptr + "{", list, "}"} | ||
163 | case reflect.Map: | ||
164 | if v.IsNil() { | ||
165 | return textNil | ||
166 | } | ||
167 | if m.Visit(v) { | ||
168 | return textLine(formatPointer(v)) | ||
169 | } | ||
170 | |||
171 | var list textList | ||
172 | for _, k := range value.SortKeys(v.MapKeys()) { | ||
173 | sk := formatMapKey(k) | ||
174 | sv := opts.WithTypeMode(elideType).FormatValue(v.MapIndex(k), m) | ||
175 | list = append(list, textRecord{Key: sk, Value: sv}) | ||
176 | } | ||
177 | if opts.PrintAddresses { | ||
178 | ptr = formatPointer(v) | ||
179 | } | ||
180 | return textWrap{ptr + "{", list, "}"} | ||
181 | case reflect.Ptr: | ||
182 | if v.IsNil() { | ||
183 | return textNil | ||
184 | } | ||
185 | if m.Visit(v) || opts.ShallowPointers { | ||
186 | return textLine(formatPointer(v)) | ||
187 | } | ||
188 | if opts.PrintAddresses { | ||
189 | ptr = formatPointer(v) | ||
190 | } | ||
191 | skipType = true // Let the underlying value print the type instead | ||
192 | return textWrap{"&" + ptr, opts.FormatValue(v.Elem(), m), ""} | ||
193 | case reflect.Interface: | ||
194 | if v.IsNil() { | ||
195 | return textNil | ||
196 | } | ||
197 | // Interfaces accept different concrete types, | ||
198 | // so configure the underlying value to explicitly print the type. | ||
199 | skipType = true // Print the concrete type instead | ||
200 | return opts.WithTypeMode(emitType).FormatValue(v.Elem(), m) | ||
201 | default: | ||
202 | panic(fmt.Sprintf("%v kind not handled", v.Kind())) | ||
203 | } | ||
204 | } | ||
205 | |||
206 | // formatMapKey formats v as if it were a map key. | ||
207 | // The result is guaranteed to be a single line. | ||
208 | func formatMapKey(v reflect.Value) string { | ||
209 | var opts formatOptions | ||
210 | opts.TypeMode = elideType | ||
211 | opts.AvoidStringer = true | ||
212 | opts.ShallowPointers = true | ||
213 | s := opts.FormatValue(v, visitedPointers{}).String() | ||
214 | return strings.TrimSpace(s) | ||
215 | } | ||
216 | |||
217 | // formatString prints s as a double-quoted or backtick-quoted string. | ||
218 | func formatString(s string) string { | ||
219 | // Use quoted string if it the same length as a raw string literal. | ||
220 | // Otherwise, attempt to use the raw string form. | ||
221 | qs := strconv.Quote(s) | ||
222 | if len(qs) == 1+len(s)+1 { | ||
223 | return qs | ||
224 | } | ||
225 | |||
226 | // Disallow newlines to ensure output is a single line. | ||
227 | // Only allow printable runes for readability purposes. | ||
228 | rawInvalid := func(r rune) bool { | ||
229 | return r == '`' || r == '\n' || !(unicode.IsPrint(r) || r == '\t') | ||
230 | } | ||
231 | if strings.IndexFunc(s, rawInvalid) < 0 { | ||
232 | return "`" + s + "`" | ||
233 | } | ||
234 | return qs | ||
235 | } | ||
236 | |||
237 | // formatHex prints u as a hexadecimal integer in Go notation. | ||
238 | func formatHex(u uint64) string { | ||
239 | var f string | ||
240 | switch { | ||
241 | case u <= 0xff: | ||
242 | f = "0x%02x" | ||
243 | case u <= 0xffff: | ||
244 | f = "0x%04x" | ||
245 | case u <= 0xffffff: | ||
246 | f = "0x%06x" | ||
247 | case u <= 0xffffffff: | ||
248 | f = "0x%08x" | ||
249 | case u <= 0xffffffffff: | ||
250 | f = "0x%010x" | ||
251 | case u <= 0xffffffffffff: | ||
252 | f = "0x%012x" | ||
253 | case u <= 0xffffffffffffff: | ||
254 | f = "0x%014x" | ||
255 | case u <= 0xffffffffffffffff: | ||
256 | f = "0x%016x" | ||
257 | } | ||
258 | return fmt.Sprintf(f, u) | ||
259 | } | ||
260 | |||
261 | // formatPointer prints the address of the pointer. | ||
262 | func formatPointer(v reflect.Value) string { | ||
263 | p := v.Pointer() | ||
264 | if flags.Deterministic { | ||
265 | p = 0xdeadf00f // Only used for stable testing purposes | ||
266 | } | ||
267 | return fmt.Sprintf("⟪0x%x⟫", p) | ||
268 | } | ||
269 | |||
270 | type visitedPointers map[value.Pointer]struct{} | ||
271 | |||
272 | // Visit inserts pointer v into the visited map and reports whether it had | ||
273 | // already been visited before. | ||
274 | func (m visitedPointers) Visit(v reflect.Value) bool { | ||
275 | p := value.PointerOf(v) | ||
276 | _, visited := m[p] | ||
277 | m[p] = struct{}{} | ||
278 | return visited | ||
279 | } | ||
diff --git a/vendor/github.com/google/go-cmp/cmp/report_slices.go b/vendor/github.com/google/go-cmp/cmp/report_slices.go new file mode 100644 index 0000000..8cb3265 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/report_slices.go | |||
@@ -0,0 +1,333 @@ | |||
1 | // Copyright 2019, The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE.md file. | ||
4 | |||
5 | package cmp | ||
6 | |||
7 | import ( | ||
8 | "bytes" | ||
9 | "fmt" | ||
10 | "reflect" | ||
11 | "strings" | ||
12 | "unicode" | ||
13 | "unicode/utf8" | ||
14 | |||
15 | "github.com/google/go-cmp/cmp/internal/diff" | ||
16 | ) | ||
17 | |||
18 | // CanFormatDiffSlice reports whether we support custom formatting for nodes | ||
19 | // that are slices of primitive kinds or strings. | ||
20 | func (opts formatOptions) CanFormatDiffSlice(v *valueNode) bool { | ||
21 | switch { | ||
22 | case opts.DiffMode != diffUnknown: | ||
23 | return false // Must be formatting in diff mode | ||
24 | case v.NumDiff == 0: | ||
25 | return false // No differences detected | ||
26 | case v.NumIgnored+v.NumCompared+v.NumTransformed > 0: | ||
27 | // TODO: Handle the case where someone uses bytes.Equal on a large slice. | ||
28 | return false // Some custom option was used to determined equality | ||
29 | case !v.ValueX.IsValid() || !v.ValueY.IsValid(): | ||
30 | return false // Both values must be valid | ||
31 | } | ||
32 | |||
33 | switch t := v.Type; t.Kind() { | ||
34 | case reflect.String: | ||
35 | case reflect.Array, reflect.Slice: | ||
36 | // Only slices of primitive types have specialized handling. | ||
37 | switch t.Elem().Kind() { | ||
38 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, | ||
39 | reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, | ||
40 | reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: | ||
41 | default: | ||
42 | return false | ||
43 | } | ||
44 | |||
45 | // If a sufficient number of elements already differ, | ||
46 | // use specialized formatting even if length requirement is not met. | ||
47 | if v.NumDiff > v.NumSame { | ||
48 | return true | ||
49 | } | ||
50 | default: | ||
51 | return false | ||
52 | } | ||
53 | |||
54 | // Use specialized string diffing for longer slices or strings. | ||
55 | const minLength = 64 | ||
56 | return v.ValueX.Len() >= minLength && v.ValueY.Len() >= minLength | ||
57 | } | ||
58 | |||
59 | // FormatDiffSlice prints a diff for the slices (or strings) represented by v. | ||
60 | // This provides custom-tailored logic to make printing of differences in | ||
61 | // textual strings and slices of primitive kinds more readable. | ||
62 | func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode { | ||
63 | assert(opts.DiffMode == diffUnknown) | ||
64 | t, vx, vy := v.Type, v.ValueX, v.ValueY | ||
65 | |||
66 | // Auto-detect the type of the data. | ||
67 | var isLinedText, isText, isBinary bool | ||
68 | var sx, sy string | ||
69 | switch { | ||
70 | case t.Kind() == reflect.String: | ||
71 | sx, sy = vx.String(), vy.String() | ||
72 | isText = true // Initial estimate, verify later | ||
73 | case t.Kind() == reflect.Slice && t.Elem() == reflect.TypeOf(byte(0)): | ||
74 | sx, sy = string(vx.Bytes()), string(vy.Bytes()) | ||
75 | isBinary = true // Initial estimate, verify later | ||
76 | case t.Kind() == reflect.Array: | ||
77 | // Arrays need to be addressable for slice operations to work. | ||
78 | vx2, vy2 := reflect.New(t).Elem(), reflect.New(t).Elem() | ||
79 | vx2.Set(vx) | ||
80 | vy2.Set(vy) | ||
81 | vx, vy = vx2, vy2 | ||
82 | } | ||
83 | if isText || isBinary { | ||
84 | var numLines, lastLineIdx, maxLineLen int | ||
85 | isBinary = false | ||
86 | for i, r := range sx + sy { | ||
87 | if !(unicode.IsPrint(r) || unicode.IsSpace(r)) || r == utf8.RuneError { | ||
88 | isBinary = true | ||
89 | break | ||
90 | } | ||
91 | if r == '\n' { | ||
92 | if maxLineLen < i-lastLineIdx { | ||
93 | lastLineIdx = i - lastLineIdx | ||
94 | } | ||
95 | lastLineIdx = i + 1 | ||
96 | numLines++ | ||
97 | } | ||
98 | } | ||
99 | isText = !isBinary | ||
100 | isLinedText = isText && numLines >= 4 && maxLineLen <= 256 | ||
101 | } | ||
102 | |||
103 | // Format the string into printable records. | ||
104 | var list textList | ||
105 | var delim string | ||
106 | switch { | ||
107 | // If the text appears to be multi-lined text, | ||
108 | // then perform differencing across individual lines. | ||
109 | case isLinedText: | ||
110 | ssx := strings.Split(sx, "\n") | ||
111 | ssy := strings.Split(sy, "\n") | ||
112 | list = opts.formatDiffSlice( | ||
113 | reflect.ValueOf(ssx), reflect.ValueOf(ssy), 1, "line", | ||
114 | func(v reflect.Value, d diffMode) textRecord { | ||
115 | s := formatString(v.Index(0).String()) | ||
116 | return textRecord{Diff: d, Value: textLine(s)} | ||
117 | }, | ||
118 | ) | ||
119 | delim = "\n" | ||
120 | // If the text appears to be single-lined text, | ||
121 | // then perform differencing in approximately fixed-sized chunks. | ||
122 | // The output is printed as quoted strings. | ||
123 | case isText: | ||
124 | list = opts.formatDiffSlice( | ||
125 | reflect.ValueOf(sx), reflect.ValueOf(sy), 64, "byte", | ||
126 | func(v reflect.Value, d diffMode) textRecord { | ||
127 | s := formatString(v.String()) | ||
128 | return textRecord{Diff: d, Value: textLine(s)} | ||
129 | }, | ||
130 | ) | ||
131 | delim = "" | ||
132 | // If the text appears to be binary data, | ||
133 | // then perform differencing in approximately fixed-sized chunks. | ||
134 | // The output is inspired by hexdump. | ||
135 | case isBinary: | ||
136 | list = opts.formatDiffSlice( | ||
137 | reflect.ValueOf(sx), reflect.ValueOf(sy), 16, "byte", | ||
138 | func(v reflect.Value, d diffMode) textRecord { | ||
139 | var ss []string | ||
140 | for i := 0; i < v.Len(); i++ { | ||
141 | ss = append(ss, formatHex(v.Index(i).Uint())) | ||
142 | } | ||
143 | s := strings.Join(ss, ", ") | ||
144 | comment := commentString(fmt.Sprintf("%c|%v|", d, formatASCII(v.String()))) | ||
145 | return textRecord{Diff: d, Value: textLine(s), Comment: comment} | ||
146 | }, | ||
147 | ) | ||
148 | // For all other slices of primitive types, | ||
149 | // then perform differencing in approximately fixed-sized chunks. | ||
150 | // The size of each chunk depends on the width of the element kind. | ||
151 | default: | ||
152 | var chunkSize int | ||
153 | if t.Elem().Kind() == reflect.Bool { | ||
154 | chunkSize = 16 | ||
155 | } else { | ||
156 | switch t.Elem().Bits() { | ||
157 | case 8: | ||
158 | chunkSize = 16 | ||
159 | case 16: | ||
160 | chunkSize = 12 | ||
161 | case 32: | ||
162 | chunkSize = 8 | ||
163 | default: | ||
164 | chunkSize = 8 | ||
165 | } | ||
166 | } | ||
167 | list = opts.formatDiffSlice( | ||
168 | vx, vy, chunkSize, t.Elem().Kind().String(), | ||
169 | func(v reflect.Value, d diffMode) textRecord { | ||
170 | var ss []string | ||
171 | for i := 0; i < v.Len(); i++ { | ||
172 | switch t.Elem().Kind() { | ||
173 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||
174 | ss = append(ss, fmt.Sprint(v.Index(i).Int())) | ||
175 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | ||
176 | ss = append(ss, formatHex(v.Index(i).Uint())) | ||
177 | case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: | ||
178 | ss = append(ss, fmt.Sprint(v.Index(i).Interface())) | ||
179 | } | ||
180 | } | ||
181 | s := strings.Join(ss, ", ") | ||
182 | return textRecord{Diff: d, Value: textLine(s)} | ||
183 | }, | ||
184 | ) | ||
185 | } | ||
186 | |||
187 | // Wrap the output with appropriate type information. | ||
188 | var out textNode = textWrap{"{", list, "}"} | ||
189 | if !isText { | ||
190 | // The "{...}" byte-sequence literal is not valid Go syntax for strings. | ||
191 | // Emit the type for extra clarity (e.g. "string{...}"). | ||
192 | if t.Kind() == reflect.String { | ||
193 | opts = opts.WithTypeMode(emitType) | ||
194 | } | ||
195 | return opts.FormatType(t, out) | ||
196 | } | ||
197 | switch t.Kind() { | ||
198 | case reflect.String: | ||
199 | out = textWrap{"strings.Join(", out, fmt.Sprintf(", %q)", delim)} | ||
200 | if t != reflect.TypeOf(string("")) { | ||
201 | out = opts.FormatType(t, out) | ||
202 | } | ||
203 | case reflect.Slice: | ||
204 | out = textWrap{"bytes.Join(", out, fmt.Sprintf(", %q)", delim)} | ||
205 | if t != reflect.TypeOf([]byte(nil)) { | ||
206 | out = opts.FormatType(t, out) | ||
207 | } | ||
208 | } | ||
209 | return out | ||
210 | } | ||
211 | |||
212 | // formatASCII formats s as an ASCII string. | ||
213 | // This is useful for printing binary strings in a semi-legible way. | ||
214 | func formatASCII(s string) string { | ||
215 | b := bytes.Repeat([]byte{'.'}, len(s)) | ||
216 | for i := 0; i < len(s); i++ { | ||
217 | if ' ' <= s[i] && s[i] <= '~' { | ||
218 | b[i] = s[i] | ||
219 | } | ||
220 | } | ||
221 | return string(b) | ||
222 | } | ||
223 | |||
224 | func (opts formatOptions) formatDiffSlice( | ||
225 | vx, vy reflect.Value, chunkSize int, name string, | ||
226 | makeRec func(reflect.Value, diffMode) textRecord, | ||
227 | ) (list textList) { | ||
228 | es := diff.Difference(vx.Len(), vy.Len(), func(ix int, iy int) diff.Result { | ||
229 | return diff.BoolResult(vx.Index(ix).Interface() == vy.Index(iy).Interface()) | ||
230 | }) | ||
231 | |||
232 | appendChunks := func(v reflect.Value, d diffMode) int { | ||
233 | n0 := v.Len() | ||
234 | for v.Len() > 0 { | ||
235 | n := chunkSize | ||
236 | if n > v.Len() { | ||
237 | n = v.Len() | ||
238 | } | ||
239 | list = append(list, makeRec(v.Slice(0, n), d)) | ||
240 | v = v.Slice(n, v.Len()) | ||
241 | } | ||
242 | return n0 - v.Len() | ||
243 | } | ||
244 | |||
245 | groups := coalesceAdjacentEdits(name, es) | ||
246 | groups = coalesceInterveningIdentical(groups, chunkSize/4) | ||
247 | for i, ds := range groups { | ||
248 | // Print equal. | ||
249 | if ds.NumDiff() == 0 { | ||
250 | // Compute the number of leading and trailing equal bytes to print. | ||
251 | var numLo, numHi int | ||
252 | numEqual := ds.NumIgnored + ds.NumIdentical | ||
253 | for numLo < chunkSize*numContextRecords && numLo+numHi < numEqual && i != 0 { | ||
254 | numLo++ | ||
255 | } | ||
256 | for numHi < chunkSize*numContextRecords && numLo+numHi < numEqual && i != len(groups)-1 { | ||
257 | numHi++ | ||
258 | } | ||
259 | if numEqual-(numLo+numHi) <= chunkSize && ds.NumIgnored == 0 { | ||
260 | numHi = numEqual - numLo // Avoid pointless coalescing of single equal row | ||
261 | } | ||
262 | |||
263 | // Print the equal bytes. | ||
264 | appendChunks(vx.Slice(0, numLo), diffIdentical) | ||
265 | if numEqual > numLo+numHi { | ||
266 | ds.NumIdentical -= numLo + numHi | ||
267 | list.AppendEllipsis(ds) | ||
268 | } | ||
269 | appendChunks(vx.Slice(numEqual-numHi, numEqual), diffIdentical) | ||
270 | vx = vx.Slice(numEqual, vx.Len()) | ||
271 | vy = vy.Slice(numEqual, vy.Len()) | ||
272 | continue | ||
273 | } | ||
274 | |||
275 | // Print unequal. | ||
276 | nx := appendChunks(vx.Slice(0, ds.NumIdentical+ds.NumRemoved+ds.NumModified), diffRemoved) | ||
277 | vx = vx.Slice(nx, vx.Len()) | ||
278 | ny := appendChunks(vy.Slice(0, ds.NumIdentical+ds.NumInserted+ds.NumModified), diffInserted) | ||
279 | vy = vy.Slice(ny, vy.Len()) | ||
280 | } | ||
281 | assert(vx.Len() == 0 && vy.Len() == 0) | ||
282 | return list | ||
283 | } | ||
284 | |||
285 | // coalesceAdjacentEdits coalesces the list of edits into groups of adjacent | ||
286 | // equal or unequal counts. | ||
287 | func coalesceAdjacentEdits(name string, es diff.EditScript) (groups []diffStats) { | ||
288 | var prevCase int // Arbitrary index into which case last occurred | ||
289 | lastStats := func(i int) *diffStats { | ||
290 | if prevCase != i { | ||
291 | groups = append(groups, diffStats{Name: name}) | ||
292 | prevCase = i | ||
293 | } | ||
294 | return &groups[len(groups)-1] | ||
295 | } | ||
296 | for _, e := range es { | ||
297 | switch e { | ||
298 | case diff.Identity: | ||
299 | lastStats(1).NumIdentical++ | ||
300 | case diff.UniqueX: | ||
301 | lastStats(2).NumRemoved++ | ||
302 | case diff.UniqueY: | ||
303 | lastStats(2).NumInserted++ | ||
304 | case diff.Modified: | ||
305 | lastStats(2).NumModified++ | ||
306 | } | ||
307 | } | ||
308 | return groups | ||
309 | } | ||
310 | |||
311 | // coalesceInterveningIdentical coalesces sufficiently short (<= windowSize) | ||
312 | // equal groups into adjacent unequal groups that currently result in a | ||
313 | // dual inserted/removed printout. This acts as a high-pass filter to smooth | ||
314 | // out high-frequency changes within the windowSize. | ||
315 | func coalesceInterveningIdentical(groups []diffStats, windowSize int) []diffStats { | ||
316 | groups, groupsOrig := groups[:0], groups | ||
317 | for i, ds := range groupsOrig { | ||
318 | if len(groups) >= 2 && ds.NumDiff() > 0 { | ||
319 | prev := &groups[len(groups)-2] // Unequal group | ||
320 | curr := &groups[len(groups)-1] // Equal group | ||
321 | next := &groupsOrig[i] // Unequal group | ||
322 | hadX, hadY := prev.NumRemoved > 0, prev.NumInserted > 0 | ||
323 | hasX, hasY := next.NumRemoved > 0, next.NumInserted > 0 | ||
324 | if ((hadX || hasX) && (hadY || hasY)) && curr.NumIdentical <= windowSize { | ||
325 | *prev = (*prev).Append(*curr).Append(*next) | ||
326 | groups = groups[:len(groups)-1] // Truncate off equal group | ||
327 | continue | ||
328 | } | ||
329 | } | ||
330 | groups = append(groups, ds) | ||
331 | } | ||
332 | return groups | ||
333 | } | ||
diff --git a/vendor/github.com/google/go-cmp/cmp/report_text.go b/vendor/github.com/google/go-cmp/cmp/report_text.go new file mode 100644 index 0000000..80605d0 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/report_text.go | |||
@@ -0,0 +1,382 @@ | |||
1 | // Copyright 2019, The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE.md file. | ||
4 | |||
5 | package cmp | ||
6 | |||
7 | import ( | ||
8 | "bytes" | ||
9 | "fmt" | ||
10 | "math/rand" | ||
11 | "strings" | ||
12 | "time" | ||
13 | |||
14 | "github.com/google/go-cmp/cmp/internal/flags" | ||
15 | ) | ||
16 | |||
17 | var randBool = rand.New(rand.NewSource(time.Now().Unix())).Intn(2) == 0 | ||
18 | |||
19 | type indentMode int | ||
20 | |||
21 | func (n indentMode) appendIndent(b []byte, d diffMode) []byte { | ||
22 | if flags.Deterministic || randBool { | ||
23 | // Use regular spaces (U+0020). | ||
24 | switch d { | ||
25 | case diffUnknown, diffIdentical: | ||
26 | b = append(b, " "...) | ||
27 | case diffRemoved: | ||
28 | b = append(b, "- "...) | ||
29 | case diffInserted: | ||
30 | b = append(b, "+ "...) | ||
31 | } | ||
32 | } else { | ||
33 | // Use non-breaking spaces (U+00a0). | ||
34 | switch d { | ||
35 | case diffUnknown, diffIdentical: | ||
36 | b = append(b, " "...) | ||
37 | case diffRemoved: | ||
38 | b = append(b, "- "...) | ||
39 | case diffInserted: | ||
40 | b = append(b, "+ "...) | ||
41 | } | ||
42 | } | ||
43 | return repeatCount(n).appendChar(b, '\t') | ||
44 | } | ||
45 | |||
46 | type repeatCount int | ||
47 | |||
48 | func (n repeatCount) appendChar(b []byte, c byte) []byte { | ||
49 | for ; n > 0; n-- { | ||
50 | b = append(b, c) | ||
51 | } | ||
52 | return b | ||
53 | } | ||
54 | |||
55 | // textNode is a simplified tree-based representation of structured text. | ||
56 | // Possible node types are textWrap, textList, or textLine. | ||
57 | type textNode interface { | ||
58 | // Len reports the length in bytes of a single-line version of the tree. | ||
59 | // Nested textRecord.Diff and textRecord.Comment fields are ignored. | ||
60 | Len() int | ||
61 | // Equal reports whether the two trees are structurally identical. | ||
62 | // Nested textRecord.Diff and textRecord.Comment fields are compared. | ||
63 | Equal(textNode) bool | ||
64 | // String returns the string representation of the text tree. | ||
65 | // It is not guaranteed that len(x.String()) == x.Len(), | ||
66 | // nor that x.String() == y.String() implies that x.Equal(y). | ||
67 | String() string | ||
68 | |||
69 | // formatCompactTo formats the contents of the tree as a single-line string | ||
70 | // to the provided buffer. Any nested textRecord.Diff and textRecord.Comment | ||
71 | // fields are ignored. | ||
72 | // | ||
73 | // However, not all nodes in the tree should be collapsed as a single-line. | ||
74 | // If a node can be collapsed as a single-line, it is replaced by a textLine | ||
75 | // node. Since the top-level node cannot replace itself, this also returns | ||
76 | // the current node itself. | ||
77 | // | ||
78 | // This does not mutate the receiver. | ||
79 | formatCompactTo([]byte, diffMode) ([]byte, textNode) | ||
80 | // formatExpandedTo formats the contents of the tree as a multi-line string | ||
81 | // to the provided buffer. In order for column alignment to operate well, | ||
82 | // formatCompactTo must be called before calling formatExpandedTo. | ||
83 | formatExpandedTo([]byte, diffMode, indentMode) []byte | ||
84 | } | ||
85 | |||
86 | // textWrap is a wrapper that concatenates a prefix and/or a suffix | ||
87 | // to the underlying node. | ||
88 | type textWrap struct { | ||
89 | Prefix string // e.g., "bytes.Buffer{" | ||
90 | Value textNode // textWrap | textList | textLine | ||
91 | Suffix string // e.g., "}" | ||
92 | } | ||
93 | |||
94 | func (s textWrap) Len() int { | ||
95 | return len(s.Prefix) + s.Value.Len() + len(s.Suffix) | ||
96 | } | ||
97 | func (s1 textWrap) Equal(s2 textNode) bool { | ||
98 | if s2, ok := s2.(textWrap); ok { | ||
99 | return s1.Prefix == s2.Prefix && s1.Value.Equal(s2.Value) && s1.Suffix == s2.Suffix | ||
100 | } | ||
101 | return false | ||
102 | } | ||
103 | func (s textWrap) String() string { | ||
104 | var d diffMode | ||
105 | var n indentMode | ||
106 | _, s2 := s.formatCompactTo(nil, d) | ||
107 | b := n.appendIndent(nil, d) // Leading indent | ||
108 | b = s2.formatExpandedTo(b, d, n) // Main body | ||
109 | b = append(b, '\n') // Trailing newline | ||
110 | return string(b) | ||
111 | } | ||
112 | func (s textWrap) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) { | ||
113 | n0 := len(b) // Original buffer length | ||
114 | b = append(b, s.Prefix...) | ||
115 | b, s.Value = s.Value.formatCompactTo(b, d) | ||
116 | b = append(b, s.Suffix...) | ||
117 | if _, ok := s.Value.(textLine); ok { | ||
118 | return b, textLine(b[n0:]) | ||
119 | } | ||
120 | return b, s | ||
121 | } | ||
122 | func (s textWrap) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte { | ||
123 | b = append(b, s.Prefix...) | ||
124 | b = s.Value.formatExpandedTo(b, d, n) | ||
125 | b = append(b, s.Suffix...) | ||
126 | return b | ||
127 | } | ||
128 | |||
129 | // textList is a comma-separated list of textWrap or textLine nodes. | ||
130 | // The list may be formatted as multi-lines or single-line at the discretion | ||
131 | // of the textList.formatCompactTo method. | ||
132 | type textList []textRecord | ||
133 | type textRecord struct { | ||
134 | Diff diffMode // e.g., 0 or '-' or '+' | ||
135 | Key string // e.g., "MyField" | ||
136 | Value textNode // textWrap | textLine | ||
137 | Comment fmt.Stringer // e.g., "6 identical fields" | ||
138 | } | ||
139 | |||
140 | // AppendEllipsis appends a new ellipsis node to the list if none already | ||
141 | // exists at the end. If cs is non-zero it coalesces the statistics with the | ||
142 | // previous diffStats. | ||
143 | func (s *textList) AppendEllipsis(ds diffStats) { | ||
144 | hasStats := ds != diffStats{} | ||
145 | if len(*s) == 0 || !(*s)[len(*s)-1].Value.Equal(textEllipsis) { | ||
146 | if hasStats { | ||
147 | *s = append(*s, textRecord{Value: textEllipsis, Comment: ds}) | ||
148 | } else { | ||
149 | *s = append(*s, textRecord{Value: textEllipsis}) | ||
150 | } | ||
151 | return | ||
152 | } | ||
153 | if hasStats { | ||
154 | (*s)[len(*s)-1].Comment = (*s)[len(*s)-1].Comment.(diffStats).Append(ds) | ||
155 | } | ||
156 | } | ||
157 | |||
158 | func (s textList) Len() (n int) { | ||
159 | for i, r := range s { | ||
160 | n += len(r.Key) | ||
161 | if r.Key != "" { | ||
162 | n += len(": ") | ||
163 | } | ||
164 | n += r.Value.Len() | ||
165 | if i < len(s)-1 { | ||
166 | n += len(", ") | ||
167 | } | ||
168 | } | ||
169 | return n | ||
170 | } | ||
171 | |||
172 | func (s1 textList) Equal(s2 textNode) bool { | ||
173 | if s2, ok := s2.(textList); ok { | ||
174 | if len(s1) != len(s2) { | ||
175 | return false | ||
176 | } | ||
177 | for i := range s1 { | ||
178 | r1, r2 := s1[i], s2[i] | ||
179 | if !(r1.Diff == r2.Diff && r1.Key == r2.Key && r1.Value.Equal(r2.Value) && r1.Comment == r2.Comment) { | ||
180 | return false | ||
181 | } | ||
182 | } | ||
183 | return true | ||
184 | } | ||
185 | return false | ||
186 | } | ||
187 | |||
188 | func (s textList) String() string { | ||
189 | return textWrap{"{", s, "}"}.String() | ||
190 | } | ||
191 | |||
192 | func (s textList) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) { | ||
193 | s = append(textList(nil), s...) // Avoid mutating original | ||
194 | |||
195 | // Determine whether we can collapse this list as a single line. | ||
196 | n0 := len(b) // Original buffer length | ||
197 | var multiLine bool | ||
198 | for i, r := range s { | ||
199 | if r.Diff == diffInserted || r.Diff == diffRemoved { | ||
200 | multiLine = true | ||
201 | } | ||
202 | b = append(b, r.Key...) | ||
203 | if r.Key != "" { | ||
204 | b = append(b, ": "...) | ||
205 | } | ||
206 | b, s[i].Value = r.Value.formatCompactTo(b, d|r.Diff) | ||
207 | if _, ok := s[i].Value.(textLine); !ok { | ||
208 | multiLine = true | ||
209 | } | ||
210 | if r.Comment != nil { | ||
211 | multiLine = true | ||
212 | } | ||
213 | if i < len(s)-1 { | ||
214 | b = append(b, ", "...) | ||
215 | } | ||
216 | } | ||
217 | // Force multi-lined output when printing a removed/inserted node that | ||
218 | // is sufficiently long. | ||
219 | if (d == diffInserted || d == diffRemoved) && len(b[n0:]) > 80 { | ||
220 | multiLine = true | ||
221 | } | ||
222 | if !multiLine { | ||
223 | return b, textLine(b[n0:]) | ||
224 | } | ||
225 | return b, s | ||
226 | } | ||
227 | |||
228 | func (s textList) formatExpandedTo(b []byte, d diffMode, n indentMode) []byte { | ||
229 | alignKeyLens := s.alignLens( | ||
230 | func(r textRecord) bool { | ||
231 | _, isLine := r.Value.(textLine) | ||
232 | return r.Key == "" || !isLine | ||
233 | }, | ||
234 | func(r textRecord) int { return len(r.Key) }, | ||
235 | ) | ||
236 | alignValueLens := s.alignLens( | ||
237 | func(r textRecord) bool { | ||
238 | _, isLine := r.Value.(textLine) | ||
239 | return !isLine || r.Value.Equal(textEllipsis) || r.Comment == nil | ||
240 | }, | ||
241 | func(r textRecord) int { return len(r.Value.(textLine)) }, | ||
242 | ) | ||
243 | |||
244 | // Format the list as a multi-lined output. | ||
245 | n++ | ||
246 | for i, r := range s { | ||
247 | b = n.appendIndent(append(b, '\n'), d|r.Diff) | ||
248 | if r.Key != "" { | ||
249 | b = append(b, r.Key+": "...) | ||
250 | } | ||
251 | b = alignKeyLens[i].appendChar(b, ' ') | ||
252 | |||
253 | b = r.Value.formatExpandedTo(b, d|r.Diff, n) | ||
254 | if !r.Value.Equal(textEllipsis) { | ||
255 | b = append(b, ',') | ||
256 | } | ||
257 | b = alignValueLens[i].appendChar(b, ' ') | ||
258 | |||
259 | if r.Comment != nil { | ||
260 | b = append(b, " // "+r.Comment.String()...) | ||
261 | } | ||
262 | } | ||
263 | n-- | ||
264 | |||
265 | return n.appendIndent(append(b, '\n'), d) | ||
266 | } | ||
267 | |||
268 | func (s textList) alignLens( | ||
269 | skipFunc func(textRecord) bool, | ||
270 | lenFunc func(textRecord) int, | ||
271 | ) []repeatCount { | ||
272 | var startIdx, endIdx, maxLen int | ||
273 | lens := make([]repeatCount, len(s)) | ||
274 | for i, r := range s { | ||
275 | if skipFunc(r) { | ||
276 | for j := startIdx; j < endIdx && j < len(s); j++ { | ||
277 | lens[j] = repeatCount(maxLen - lenFunc(s[j])) | ||
278 | } | ||
279 | startIdx, endIdx, maxLen = i+1, i+1, 0 | ||
280 | } else { | ||
281 | if maxLen < lenFunc(r) { | ||
282 | maxLen = lenFunc(r) | ||
283 | } | ||
284 | endIdx = i + 1 | ||
285 | } | ||
286 | } | ||
287 | for j := startIdx; j < endIdx && j < len(s); j++ { | ||
288 | lens[j] = repeatCount(maxLen - lenFunc(s[j])) | ||
289 | } | ||
290 | return lens | ||
291 | } | ||
292 | |||
293 | // textLine is a single-line segment of text and is always a leaf node | ||
294 | // in the textNode tree. | ||
295 | type textLine []byte | ||
296 | |||
297 | var ( | ||
298 | textNil = textLine("nil") | ||
299 | textEllipsis = textLine("...") | ||
300 | ) | ||
301 | |||
302 | func (s textLine) Len() int { | ||
303 | return len(s) | ||
304 | } | ||
305 | func (s1 textLine) Equal(s2 textNode) bool { | ||
306 | if s2, ok := s2.(textLine); ok { | ||
307 | return bytes.Equal([]byte(s1), []byte(s2)) | ||
308 | } | ||
309 | return false | ||
310 | } | ||
311 | func (s textLine) String() string { | ||
312 | return string(s) | ||
313 | } | ||
314 | func (s textLine) formatCompactTo(b []byte, d diffMode) ([]byte, textNode) { | ||
315 | return append(b, s...), s | ||
316 | } | ||
317 | func (s textLine) formatExpandedTo(b []byte, _ diffMode, _ indentMode) []byte { | ||
318 | return append(b, s...) | ||
319 | } | ||
320 | |||
321 | type diffStats struct { | ||
322 | Name string | ||
323 | NumIgnored int | ||
324 | NumIdentical int | ||
325 | NumRemoved int | ||
326 | NumInserted int | ||
327 | NumModified int | ||
328 | } | ||
329 | |||
330 | func (s diffStats) NumDiff() int { | ||
331 | return s.NumRemoved + s.NumInserted + s.NumModified | ||
332 | } | ||
333 | |||
334 | func (s diffStats) Append(ds diffStats) diffStats { | ||
335 | assert(s.Name == ds.Name) | ||
336 | s.NumIgnored += ds.NumIgnored | ||
337 | s.NumIdentical += ds.NumIdentical | ||
338 | s.NumRemoved += ds.NumRemoved | ||
339 | s.NumInserted += ds.NumInserted | ||
340 | s.NumModified += ds.NumModified | ||
341 | return s | ||
342 | } | ||
343 | |||
344 | // String prints a humanly-readable summary of coalesced records. | ||
345 | // | ||
346 | // Example: | ||
347 | // diffStats{Name: "Field", NumIgnored: 5}.String() => "5 ignored fields" | ||
348 | func (s diffStats) String() string { | ||
349 | var ss []string | ||
350 | var sum int | ||
351 | labels := [...]string{"ignored", "identical", "removed", "inserted", "modified"} | ||
352 | counts := [...]int{s.NumIgnored, s.NumIdentical, s.NumRemoved, s.NumInserted, s.NumModified} | ||
353 | for i, n := range counts { | ||
354 | if n > 0 { | ||
355 | ss = append(ss, fmt.Sprintf("%d %v", n, labels[i])) | ||
356 | } | ||
357 | sum += n | ||
358 | } | ||
359 | |||
360 | // Pluralize the name (adjusting for some obscure English grammar rules). | ||
361 | name := s.Name | ||
362 | if sum > 1 { | ||
363 | name = name + "s" | ||
364 | if strings.HasSuffix(name, "ys") { | ||
365 | name = name[:len(name)-2] + "ies" // e.g., "entrys" => "entries" | ||
366 | } | ||
367 | } | ||
368 | |||
369 | // Format the list according to English grammar (with Oxford comma). | ||
370 | switch n := len(ss); n { | ||
371 | case 0: | ||
372 | return "" | ||
373 | case 1, 2: | ||
374 | return strings.Join(ss, " and ") + " " + name | ||
375 | default: | ||
376 | return strings.Join(ss[:n-1], ", ") + ", and " + ss[n-1] + " " + name | ||
377 | } | ||
378 | } | ||
379 | |||
380 | type commentString string | ||
381 | |||
382 | func (s commentString) String() string { return string(s) } | ||
diff --git a/vendor/github.com/google/go-cmp/cmp/report_value.go b/vendor/github.com/google/go-cmp/cmp/report_value.go new file mode 100644 index 0000000..83031a7 --- /dev/null +++ b/vendor/github.com/google/go-cmp/cmp/report_value.go | |||
@@ -0,0 +1,121 @@ | |||
1 | // Copyright 2019, The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE.md file. | ||
4 | |||
5 | package cmp | ||
6 | |||
7 | import "reflect" | ||
8 | |||
9 | // valueNode represents a single node within a report, which is a | ||
10 | // structured representation of the value tree, containing information | ||
11 | // regarding which nodes are equal or not. | ||
12 | type valueNode struct { | ||
13 | parent *valueNode | ||
14 | |||
15 | Type reflect.Type | ||
16 | ValueX reflect.Value | ||
17 | ValueY reflect.Value | ||
18 | |||
19 | // NumSame is the number of leaf nodes that are equal. | ||
20 | // All descendants are equal only if NumDiff is 0. | ||
21 | NumSame int | ||
22 | // NumDiff is the number of leaf nodes that are not equal. | ||
23 | NumDiff int | ||
24 | // NumIgnored is the number of leaf nodes that are ignored. | ||
25 | NumIgnored int | ||
26 | // NumCompared is the number of leaf nodes that were compared | ||
27 | // using an Equal method or Comparer function. | ||
28 | NumCompared int | ||
29 | // NumTransformed is the number of non-leaf nodes that were transformed. | ||
30 | NumTransformed int | ||
31 | // NumChildren is the number of transitive descendants of this node. | ||
32 | // This counts from zero; thus, leaf nodes have no descendants. | ||
33 | NumChildren int | ||
34 | // MaxDepth is the maximum depth of the tree. This counts from zero; | ||
35 | // thus, leaf nodes have a depth of zero. | ||
36 | MaxDepth int | ||
37 | |||
38 | // Records is a list of struct fields, slice elements, or map entries. | ||
39 | Records []reportRecord // If populated, implies Value is not populated | ||
40 | |||
41 | // Value is the result of a transformation, pointer indirect, of | ||
42 | // type assertion. | ||
43 | Value *valueNode // If populated, implies Records is not populated | ||
44 | |||
45 | // TransformerName is the name of the transformer. | ||
46 | TransformerName string // If non-empty, implies Value is populated | ||
47 | } | ||
48 | type reportRecord struct { | ||
49 | Key reflect.Value // Invalid for slice element | ||
50 | Value *valueNode | ||
51 | } | ||
52 | |||
53 | func (parent *valueNode) PushStep(ps PathStep) (child *valueNode) { | ||
54 | vx, vy := ps.Values() | ||
55 | child = &valueNode{parent: parent, Type: ps.Type(), ValueX: vx, ValueY: vy} | ||
56 | switch s := ps.(type) { | ||
57 | case StructField: | ||
58 | assert(parent.Value == nil) | ||
59 | parent.Records = append(parent.Records, reportRecord{Key: reflect.ValueOf(s.Name()), Value: child}) | ||
60 | case SliceIndex: | ||
61 | assert(parent.Value == nil) | ||
62 | parent.Records = append(parent.Records, reportRecord{Value: child}) | ||
63 | case MapIndex: | ||
64 | assert(parent.Value == nil) | ||
65 | parent.Records = append(parent.Records, reportRecord{Key: s.Key(), Value: child}) | ||
66 | case Indirect: | ||
67 | assert(parent.Value == nil && parent.Records == nil) | ||
68 | parent.Value = child | ||
69 | case TypeAssertion: | ||
70 | assert(parent.Value == nil && parent.Records == nil) | ||
71 | parent.Value = child | ||
72 | case Transform: | ||
73 | assert(parent.Value == nil && parent.Records == nil) | ||
74 | parent.Value = child | ||
75 | parent.TransformerName = s.Name() | ||
76 | parent.NumTransformed++ | ||
77 | default: | ||
78 | assert(parent == nil) // Must be the root step | ||
79 | } | ||
80 | return child | ||
81 | } | ||
82 | |||
83 | func (r *valueNode) Report(rs Result) { | ||
84 | assert(r.MaxDepth == 0) // May only be called on leaf nodes | ||
85 | |||
86 | if rs.ByIgnore() { | ||
87 | r.NumIgnored++ | ||
88 | } else { | ||
89 | if rs.Equal() { | ||
90 | r.NumSame++ | ||
91 | } else { | ||
92 | r.NumDiff++ | ||
93 | } | ||
94 | } | ||
95 | assert(r.NumSame+r.NumDiff+r.NumIgnored == 1) | ||
96 | |||
97 | if rs.ByMethod() { | ||
98 | r.NumCompared++ | ||
99 | } | ||
100 | if rs.ByFunc() { | ||
101 | r.NumCompared++ | ||
102 | } | ||
103 | assert(r.NumCompared <= 1) | ||
104 | } | ||
105 | |||
106 | func (child *valueNode) PopStep() (parent *valueNode) { | ||
107 | if child.parent == nil { | ||
108 | return nil | ||
109 | } | ||
110 | parent = child.parent | ||
111 | parent.NumSame += child.NumSame | ||
112 | parent.NumDiff += child.NumDiff | ||
113 | parent.NumIgnored += child.NumIgnored | ||
114 | parent.NumCompared += child.NumCompared | ||
115 | parent.NumTransformed += child.NumTransformed | ||
116 | parent.NumChildren += child.NumChildren + 1 | ||
117 | if parent.MaxDepth < child.MaxDepth+1 { | ||
118 | parent.MaxDepth = child.MaxDepth + 1 | ||
119 | } | ||
120 | return parent | ||
121 | } | ||
diff --git a/vendor/github.com/google/go-cmp/cmp/reporter.go b/vendor/github.com/google/go-cmp/cmp/reporter.go deleted file mode 100644 index 20e9f18..0000000 --- a/vendor/github.com/google/go-cmp/cmp/reporter.go +++ /dev/null | |||
@@ -1,53 +0,0 @@ | |||
1 | // Copyright 2017, The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE.md file. | ||
4 | |||
5 | package cmp | ||
6 | |||
7 | import ( | ||
8 | "fmt" | ||
9 | "reflect" | ||
10 | "strings" | ||
11 | |||
12 | "github.com/google/go-cmp/cmp/internal/value" | ||
13 | ) | ||
14 | |||
15 | type defaultReporter struct { | ||
16 | Option | ||
17 | diffs []string // List of differences, possibly truncated | ||
18 | ndiffs int // Total number of differences | ||
19 | nbytes int // Number of bytes in diffs | ||
20 | nlines int // Number of lines in diffs | ||
21 | } | ||
22 | |||
23 | var _ reporter = (*defaultReporter)(nil) | ||
24 | |||
25 | func (r *defaultReporter) Report(x, y reflect.Value, eq bool, p Path) { | ||
26 | if eq { | ||
27 | return // Ignore equal results | ||
28 | } | ||
29 | const maxBytes = 4096 | ||
30 | const maxLines = 256 | ||
31 | r.ndiffs++ | ||
32 | if r.nbytes < maxBytes && r.nlines < maxLines { | ||
33 | sx := value.Format(x, value.FormatConfig{UseStringer: true}) | ||
34 | sy := value.Format(y, value.FormatConfig{UseStringer: true}) | ||
35 | if sx == sy { | ||
36 | // Unhelpful output, so use more exact formatting. | ||
37 | sx = value.Format(x, value.FormatConfig{PrintPrimitiveType: true}) | ||
38 | sy = value.Format(y, value.FormatConfig{PrintPrimitiveType: true}) | ||
39 | } | ||
40 | s := fmt.Sprintf("%#v:\n\t-: %s\n\t+: %s\n", p, sx, sy) | ||
41 | r.diffs = append(r.diffs, s) | ||
42 | r.nbytes += len(s) | ||
43 | r.nlines += strings.Count(s, "\n") | ||
44 | } | ||
45 | } | ||
46 | |||
47 | func (r *defaultReporter) String() string { | ||
48 | s := strings.Join(r.diffs, "") | ||
49 | if r.ndiffs == len(r.diffs) { | ||
50 | return s | ||
51 | } | ||
52 | return fmt.Sprintf("%s... %d more differences ...", s, r.ndiffs-len(r.diffs)) | ||
53 | } | ||