github.com/rigado/snapd@v2.42.5-go-mod+incompatible/testutil/containschecker.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2015-2018 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package testutil 21 22 import ( 23 "fmt" 24 "reflect" 25 "strings" 26 27 "gopkg.in/check.v1" 28 ) 29 30 type containsChecker struct { 31 *check.CheckerInfo 32 } 33 34 // Contains is a Checker that looks for a elem in a container. 35 // The elem can be any object. The container can be an array, slice or string. 36 var Contains check.Checker = &containsChecker{ 37 &check.CheckerInfo{Name: "Contains", Params: []string{"container", "elem"}}, 38 } 39 40 func commonEquals(container, elem interface{}, result *bool, error *string) bool { 41 containerV := reflect.ValueOf(container) 42 elemV := reflect.ValueOf(elem) 43 switch containerV.Kind() { 44 case reflect.Slice, reflect.Array, reflect.Map: 45 containerElemType := containerV.Type().Elem() 46 if containerElemType.Kind() == reflect.Interface { 47 // Ensure that element implements the type of elements stored in the container. 48 if !elemV.Type().Implements(containerElemType) { 49 *result = false 50 *error = fmt.Sprintf(""+ 51 "container has items of interface type %s but expected"+ 52 " element does not implement it", containerElemType) 53 return true 54 } 55 } else { 56 // Ensure that type of elements in container is compatible with elem 57 if containerElemType != elemV.Type() { 58 *result = false 59 *error = fmt.Sprintf( 60 "container has items of type %s but expected element is a %s", 61 containerElemType, elemV.Type()) 62 return true 63 } 64 } 65 case reflect.String: 66 // When container is a string, we expect elem to be a string as well 67 if elemV.Kind() != reflect.String { 68 *result = false 69 *error = fmt.Sprintf("element is a %T but expected a string", elem) 70 } else { 71 *result = strings.Contains(containerV.String(), elemV.String()) 72 *error = "" 73 } 74 return true 75 } 76 return false 77 } 78 79 func (c *containsChecker) Check(params []interface{}, names []string) (result bool, error string) { 80 defer func() { 81 if v := recover(); v != nil { 82 result = false 83 error = fmt.Sprint(v) 84 } 85 }() 86 var container interface{} = params[0] 87 var elem interface{} = params[1] 88 if commonEquals(container, elem, &result, &error) { 89 return 90 } 91 // Do the actual test using == 92 switch containerV := reflect.ValueOf(container); containerV.Kind() { 93 case reflect.Slice, reflect.Array: 94 for length, i := containerV.Len(), 0; i < length; i++ { 95 itemV := containerV.Index(i) 96 if itemV.Interface() == elem { 97 return true, "" 98 } 99 } 100 return false, "" 101 case reflect.Map: 102 for _, keyV := range containerV.MapKeys() { 103 itemV := containerV.MapIndex(keyV) 104 if itemV.Interface() == elem { 105 return true, "" 106 } 107 } 108 return false, "" 109 default: 110 return false, fmt.Sprintf("%T is not a supported container", container) 111 } 112 } 113 114 type deepContainsChecker struct { 115 *check.CheckerInfo 116 } 117 118 // DeepContains is a Checker that looks for a elem in a container using 119 // DeepEqual. The elem can be any object. The container can be an array, slice 120 // or string. 121 var DeepContains check.Checker = &deepContainsChecker{ 122 &check.CheckerInfo{Name: "DeepContains", Params: []string{"container", "elem"}}, 123 } 124 125 func (c *deepContainsChecker) Check(params []interface{}, names []string) (result bool, error string) { 126 var container interface{} = params[0] 127 var elem interface{} = params[1] 128 if commonEquals(container, elem, &result, &error) { 129 return 130 } 131 // Do the actual test using reflect.DeepEqual 132 switch containerV := reflect.ValueOf(container); containerV.Kind() { 133 case reflect.Slice, reflect.Array: 134 for length, i := containerV.Len(), 0; i < length; i++ { 135 itemV := containerV.Index(i) 136 if reflect.DeepEqual(itemV.Interface(), elem) { 137 return true, "" 138 } 139 } 140 return false, "" 141 case reflect.Map: 142 for _, keyV := range containerV.MapKeys() { 143 itemV := containerV.MapIndex(keyV) 144 if reflect.DeepEqual(itemV.Interface(), elem) { 145 return true, "" 146 } 147 } 148 return false, "" 149 default: 150 return false, fmt.Sprintf("%T is not a supported container", container) 151 } 152 }