github.com/looshlee/beatles@v0.0.0-20220727174639-742810ab631c/pkg/checker/checker.go (about) 1 // Copyright 2018-2019 Authors of Cilium 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package checker 16 17 import ( 18 "reflect" 19 20 "github.com/cilium/cilium/pkg/comparator" 21 22 "github.com/google/go-cmp/cmp" 23 24 "gopkg.in/check.v1" 25 ) 26 27 type diffChecker struct { 28 *check.CheckerInfo 29 } 30 31 // DeepEquals is a GoCheck checker that does a diff between two objects and 32 // pretty-prints any difference between the two. It can act as a substitute 33 // for DeepEquals. 34 var ( 35 defaultParams = []string{"obtained", "expected"} 36 DeepEquals check.Checker = &diffChecker{ 37 &check.CheckerInfo{Name: "Diff", Params: defaultParams}, 38 } 39 ) 40 41 // Check performs a diff between two objects provided as parameters, and 42 // returns either true if the objects are identical, or false otherwise. If 43 // it returns false, it also returns the unified diff between the expected 44 // and obtained output. 45 func (checker *diffChecker) Check(params []interface{}, names []string) (result bool, error string) { 46 if len(params) != 2 || len(names) != 2 { 47 return false, "params and names must be of length 2" 48 } 49 50 if reflect.DeepEqual(params[0], params[1]) { 51 return true, "" 52 } 53 54 return false, comparator.CompareWithNames(params[0], params[1], names[0], names[1]) 55 } 56 57 // DeepEqual tests whether two parameters are deeply equal, and returns true if 58 // they are. If the objects are not deeply equal, then the second return value 59 // includes a json representation of the difference between the parameters. 60 func DeepEqual(params ...interface{}) (bool, string) { 61 return DeepEquals.Check(params, defaultParams) 62 } 63 64 type cmpChecker struct { 65 *check.CheckerInfo 66 } 67 68 // Equals is a GoCheck checker that does a diff between two objects and 69 // pretty-prints any difference between the two. It can act as a substitute 70 // for DeepEquals. 71 var ( 72 cmpParams = []string{"obtained", "expected"} 73 Equals check.Checker = &cmpChecker{ 74 &check.CheckerInfo{Name: "Equals", Params: cmpParams}, 75 } 76 ) 77 78 // Check performs a diff between two objects provided as parameters, and 79 // returns either true if the objects are identical, or false otherwise. If 80 // it returns false, it also returns the unified diff between the expected 81 // and obtained output. 82 func (checker *cmpChecker) Check(params []interface{}, _ []string) (result bool, error string) { 83 if len(params) < 2 { 84 return false, "Parameter missing" 85 } 86 87 // Diff expects to receive parameters in order ("expected", 88 // "obtained"), but our convention is to pass them as 89 // ("obtained", "expected"), so reverse them here. 90 diff := cmp.Diff(params[1], params[0], DeepAllowUnexported(params[1], params[0])) 91 92 return diff == "", diff 93 } 94 95 // Equal tests whether two parameters are deeply equal, and returns true if 96 // they are. If the objects are not deeply equal, then the second return value 97 // includes a json representation of the difference between the parameters. 98 func Equal(params ...interface{}) (bool, string) { 99 return Equals.Check(params, cmpParams) 100 } 101 102 func DeepAllowUnexported(vs ...interface{}) cmp.Option { 103 m := make(map[reflect.Type]struct{}) 104 for _, v := range vs { 105 structTypes(reflect.ValueOf(v), m) 106 } 107 var typs []interface{} 108 for t := range m { 109 typs = append(typs, reflect.New(t).Elem().Interface()) 110 } 111 return cmp.AllowUnexported(typs...) 112 } 113 114 func structTypes(v reflect.Value, m map[reflect.Type]struct{}) { 115 if !v.IsValid() { 116 return 117 } 118 switch v.Kind() { 119 case reflect.Ptr: 120 if !v.IsNil() { 121 structTypes(v.Elem(), m) 122 } 123 case reflect.Interface: 124 if !v.IsNil() { 125 structTypes(v.Elem(), m) 126 } 127 case reflect.Slice, reflect.Array: 128 for i := 0; i < v.Len(); i++ { 129 structTypes(v.Index(i), m) 130 } 131 case reflect.Map: 132 for _, k := range v.MapKeys() { 133 structTypes(v.MapIndex(k), m) 134 } 135 case reflect.Struct: 136 m[v.Type()] = struct{}{} 137 for i := 0; i < v.NumField(); i++ { 138 structTypes(v.Field(i), m) 139 } 140 } 141 }