github.com/buildpacks/pack@v0.33.3-0.20240516162812-884dd1837311/testhelpers/comparehelpers/deep_compare.go (about) 1 package comparehelpers 2 3 import ( 4 "fmt" 5 "reflect" 6 ) 7 8 // Returns true if 'containee' is contained in 'container' 9 // Note this method searches all objects in 'container' for containee 10 // Contains is defined by the following relationship 11 // basic data types (string, float, int,...): 12 // 13 // container == containee 14 // 15 // maps: 16 // 17 // every key-value pair from containee is in container 18 // Ex: {"a": 1, "b": 2, "c": 3} contains {"a": 1, "c": 3} 19 // 20 // arrays: 21 // 22 // every element in containee is present and ordered in an array in container 23 // Ex: [1, 1, 4, 3, 10, 4] contains [1, 3, 4 ] 24 // 25 // Limitaions: 26 // Cannot handle the following types: Pointers, Func 27 // Assumes we are compairing structs generated from JSON, YAML, or TOML. 28 func DeepContains(container, containee interface{}) bool { 29 if container == nil || containee == nil { 30 return container == containee 31 } 32 v1 := reflect.ValueOf(container) 33 v2 := reflect.ValueOf(containee) 34 35 return deepContains(v1, v2, 0) 36 } 37 38 func deepContains(v1, v2 reflect.Value, depth int) bool { 39 if depth > 200 { 40 panic("deep Contains depth exceeded, likely a circular reference") 41 } 42 if !v1.IsValid() || !v2.IsValid() { 43 return v1.IsValid() == v2.IsValid() 44 } 45 46 switch v1.Kind() { 47 case reflect.Array, reflect.Slice: 48 // check for subset matches in arrays 49 return arrayLikeContains(v1, v2, depth+1) 50 case reflect.Map: 51 return mapContains(v1, v2, depth+1) 52 case reflect.Interface: 53 return deepContains(v1.Elem(), v2, depth+1) 54 case reflect.Ptr, reflect.Struct, reflect.Func: 55 panic(fmt.Sprintf("unimplmemented comparison for type: %s", v1.Kind().String())) 56 default: // assume it is a atomic datatype 57 return reflect.DeepEqual(v1, v2) 58 } 59 } 60 61 func mapContains(v1, v2 reflect.Value, depth int) bool { 62 t2 := v2.Kind() 63 if t2 == reflect.Interface { 64 return mapContains(v1, v2.Elem(), depth+1) 65 } else if t2 == reflect.Map { 66 result := true 67 for _, k := range v2.MapKeys() { 68 k2Val := v2.MapIndex(k) 69 k1Val := v1.MapIndex(k) 70 if !k1Val.IsValid() || !reflect.DeepEqual(k1Val.Interface(), k2Val.Interface()) { 71 result = false 72 break 73 } 74 } 75 if result { 76 return true 77 } 78 } 79 for _, k := range v1.MapKeys() { 80 val := v1.MapIndex(k) 81 if deepContains(val, v2, depth+1) { 82 return true 83 } 84 } 85 return false 86 } 87 88 func arrayLikeContains(v1, v2 reflect.Value, depth int) bool { 89 t2 := v2.Kind() 90 if t2 == reflect.Interface { 91 return mapContains(v1, v2.Elem(), depth+1) 92 } else if t2 == reflect.Array || t2 == reflect.Slice { 93 v1Index := 0 94 v2Index := 0 95 for v1Index < v1.Len() && v2Index < v2.Len() { 96 if reflect.DeepEqual(v1.Index(v1Index).Interface(), v2.Index(v2Index).Interface()) { 97 v2Index++ 98 } 99 v1Index++ 100 } 101 if v2Index == v2.Len() { 102 return true 103 } 104 } 105 for i := 0; i < v1.Len(); i++ { 106 if deepContains(v1.Index(i), v2, depth+1) { 107 return true 108 } 109 } 110 return false 111 }