launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/testing/checkers/checker.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package checkers 5 6 import ( 7 "fmt" 8 "reflect" 9 "strings" 10 "time" 11 12 gc "launchpad.net/gocheck" 13 ) 14 15 func TimeBetween(start, end time.Time) gc.Checker { 16 if end.Before(start) { 17 return &timeBetweenChecker{end, start} 18 } 19 return &timeBetweenChecker{start, end} 20 } 21 22 type timeBetweenChecker struct { 23 start, end time.Time 24 } 25 26 func (checker *timeBetweenChecker) Info() *gc.CheckerInfo { 27 info := gc.CheckerInfo{ 28 Name: "TimeBetween", 29 Params: []string{"obtained"}, 30 } 31 return &info 32 } 33 34 func (checker *timeBetweenChecker) Check(params []interface{}, names []string) (result bool, error string) { 35 when, ok := params[0].(time.Time) 36 if !ok { 37 return false, "obtained value type must be time.Time" 38 } 39 if when.Before(checker.start) { 40 return false, fmt.Sprintf("obtained value %#v type must before start value of %#v", when, checker.start) 41 } 42 if when.After(checker.end) { 43 return false, fmt.Sprintf("obtained value %#v type must after end value of %#v", when, checker.end) 44 } 45 return true, "" 46 } 47 48 // DurationLessThan checker 49 50 type durationLessThanChecker struct { 51 *gc.CheckerInfo 52 } 53 54 var DurationLessThan gc.Checker = &durationLessThanChecker{ 55 &gc.CheckerInfo{Name: "DurationLessThan", Params: []string{"obtained", "expected"}}, 56 } 57 58 func (checker *durationLessThanChecker) Check(params []interface{}, names []string) (result bool, error string) { 59 obtained, ok := params[0].(time.Duration) 60 if !ok { 61 return false, "obtained value type must be time.Duration" 62 } 63 expected, ok := params[1].(time.Duration) 64 if !ok { 65 return false, "expected value type must be time.Duration" 66 } 67 return obtained.Nanoseconds() < expected.Nanoseconds(), "" 68 } 69 70 // HasPrefix checker for checking strings 71 72 func stringOrStringer(value interface{}) (string, bool) { 73 result, isString := value.(string) 74 if !isString { 75 if stringer, isStringer := value.(fmt.Stringer); isStringer { 76 result, isString = stringer.String(), true 77 } 78 } 79 return result, isString 80 } 81 82 type hasPrefixChecker struct { 83 *gc.CheckerInfo 84 } 85 86 var HasPrefix gc.Checker = &hasPrefixChecker{ 87 &gc.CheckerInfo{Name: "HasPrefix", Params: []string{"obtained", "expected"}}, 88 } 89 90 func (checker *hasPrefixChecker) Check(params []interface{}, names []string) (result bool, error string) { 91 expected, ok := params[1].(string) 92 if !ok { 93 return false, "expected must be a string" 94 } 95 96 obtained, isString := stringOrStringer(params[0]) 97 if isString { 98 return strings.HasPrefix(obtained, expected), "" 99 } 100 101 return false, "Obtained value is not a string and has no .String()" 102 } 103 104 type hasSuffixChecker struct { 105 *gc.CheckerInfo 106 } 107 108 var HasSuffix gc.Checker = &hasSuffixChecker{ 109 &gc.CheckerInfo{Name: "HasSuffix", Params: []string{"obtained", "expected"}}, 110 } 111 112 func (checker *hasSuffixChecker) Check(params []interface{}, names []string) (result bool, error string) { 113 expected, ok := params[1].(string) 114 if !ok { 115 return false, "expected must be a string" 116 } 117 118 obtained, isString := stringOrStringer(params[0]) 119 if isString { 120 return strings.HasSuffix(obtained, expected), "" 121 } 122 123 return false, "Obtained value is not a string and has no .String()" 124 } 125 126 type containsChecker struct { 127 *gc.CheckerInfo 128 } 129 130 var Contains gc.Checker = &containsChecker{ 131 &gc.CheckerInfo{Name: "Contains", Params: []string{"obtained", "expected"}}, 132 } 133 134 func (checker *containsChecker) Check(params []interface{}, names []string) (result bool, error string) { 135 expected, ok := params[1].(string) 136 if !ok { 137 return false, "expected must be a string" 138 } 139 140 obtained, isString := stringOrStringer(params[0]) 141 if isString { 142 return strings.Contains(obtained, expected), "" 143 } 144 145 return false, "Obtained value is not a string and has no .String()" 146 } 147 148 type sameContents struct { 149 *gc.CheckerInfo 150 } 151 152 // SameContents checks that the obtained slice contains all the values (and 153 // same number of values) of the expected slice and vice versa, without respect 154 // to order or duplicates. Uses DeepEquals on mapped contents to compare. 155 var SameContents gc.Checker = &sameContents{ 156 &gc.CheckerInfo{Name: "SameContents", Params: []string{"obtained", "expected"}}, 157 } 158 159 func (checker *sameContents) Check(params []interface{}, names []string) (result bool, error string) { 160 if len(params) != 2 { 161 return false, "SameContents expects two slice arguments" 162 } 163 obtained := params[0] 164 expected := params[1] 165 166 tob := reflect.TypeOf(obtained) 167 if tob.Kind() != reflect.Slice { 168 return false, fmt.Sprintf("SameContents expects the obtained value to be a slice, got %q", 169 tob.Kind()) 170 } 171 172 texp := reflect.TypeOf(expected) 173 if texp.Kind() != reflect.Slice { 174 return false, fmt.Sprintf("SameContents expects the expected value to be a slice, got %q", 175 texp.Kind()) 176 } 177 178 if texp != tob { 179 return false, fmt.Sprintf( 180 "SameContents expects two slices of the same type, expected: %q, got: %q", 181 texp, tob) 182 } 183 184 vexp := reflect.ValueOf(expected) 185 vob := reflect.ValueOf(obtained) 186 length := vexp.Len() 187 188 if vob.Len() != length { 189 // Slice has incorrect number of elements 190 return false, "" 191 } 192 193 // spin up maps with the entries as keys and the counts as values 194 mob := make(map[interface{}]int, length) 195 mexp := make(map[interface{}]int, length) 196 197 for i := 0; i < length; i++ { 198 mexp[vexp.Index(i).Interface()]++ 199 mob[vob.Index(i).Interface()]++ 200 } 201 return reflect.DeepEqual(mob, mexp), "" 202 }