launchpad.net/gocheck@v0.0.0-20140225173054-000000000087/checkers.go (about) 1 package gocheck 2 3 import ( 4 "fmt" 5 "reflect" 6 "regexp" 7 ) 8 9 // ----------------------------------------------------------------------- 10 // CommentInterface and Commentf helper, to attach extra information to checks. 11 12 type comment struct { 13 format string 14 args []interface{} 15 } 16 17 // Commentf returns an infomational value to use with Assert or Check calls. 18 // If the checker test fails, the provided arguments will be passed to 19 // fmt.Sprintf, and will be presented next to the logged failure. 20 // 21 // For example: 22 // 23 // c.Assert(v, Equals, 42, Commentf("Iteration #%d failed.", i)) 24 // 25 // Note that if the comment is constant, a better option is to 26 // simply use a normal comment right above or next to the line, as 27 // it will also get printed with any errors: 28 // 29 // c.Assert(l, Equals, 8192) // Ensure buffer size is correct (bug #123) 30 // 31 func Commentf(format string, args ...interface{}) CommentInterface { 32 return &comment{format, args} 33 } 34 35 // CommentInterface must be implemented by types that attach extra 36 // information to failed checks. See the Commentf function for details. 37 type CommentInterface interface { 38 CheckCommentString() string 39 } 40 41 func (c *comment) CheckCommentString() string { 42 return fmt.Sprintf(c.format, c.args...) 43 } 44 45 // ----------------------------------------------------------------------- 46 // The Checker interface. 47 48 // The Checker interface must be provided by checkers used with 49 // the Assert and Check verification methods. 50 type Checker interface { 51 Info() *CheckerInfo 52 Check(params []interface{}, names []string) (result bool, error string) 53 } 54 55 // See the Checker interface. 56 type CheckerInfo struct { 57 Name string 58 Params []string 59 } 60 61 func (info *CheckerInfo) Info() *CheckerInfo { 62 return info 63 } 64 65 // ----------------------------------------------------------------------- 66 // Not checker logic inverter. 67 68 // The Not checker inverts the logic of the provided checker. The 69 // resulting checker will succeed where the original one failed, and 70 // vice-versa. 71 // 72 // For example: 73 // 74 // c.Assert(a, Not(Equals), b) 75 // 76 func Not(checker Checker) Checker { 77 return ¬Checker{checker} 78 } 79 80 type notChecker struct { 81 sub Checker 82 } 83 84 func (checker *notChecker) Info() *CheckerInfo { 85 info := *checker.sub.Info() 86 info.Name = "Not(" + info.Name + ")" 87 return &info 88 } 89 90 func (checker *notChecker) Check(params []interface{}, names []string) (result bool, error string) { 91 result, error = checker.sub.Check(params, names) 92 result = !result 93 return 94 } 95 96 // ----------------------------------------------------------------------- 97 // IsNil checker. 98 99 type isNilChecker struct { 100 *CheckerInfo 101 } 102 103 // The IsNil checker tests whether the obtained value is nil. 104 // 105 // For example: 106 // 107 // c.Assert(err, IsNil) 108 // 109 var IsNil Checker = &isNilChecker{ 110 &CheckerInfo{Name: "IsNil", Params: []string{"value"}}, 111 } 112 113 func (checker *isNilChecker) Check(params []interface{}, names []string) (result bool, error string) { 114 return isNil(params[0]), "" 115 } 116 117 func isNil(obtained interface{}) (result bool) { 118 if obtained == nil { 119 result = true 120 } else { 121 switch v := reflect.ValueOf(obtained); v.Kind() { 122 case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: 123 return v.IsNil() 124 } 125 } 126 return 127 } 128 129 // ----------------------------------------------------------------------- 130 // NotNil checker. Alias for Not(IsNil), since it's so common. 131 132 type notNilChecker struct { 133 *CheckerInfo 134 } 135 136 // The NotNil checker verifies that the obtained value is not nil. 137 // 138 // For example: 139 // 140 // c.Assert(iface, NotNil) 141 // 142 // This is an alias for Not(IsNil), made available since it's a 143 // fairly common check. 144 // 145 var NotNil Checker = ¬NilChecker{ 146 &CheckerInfo{Name: "NotNil", Params: []string{"value"}}, 147 } 148 149 func (checker *notNilChecker) Check(params []interface{}, names []string) (result bool, error string) { 150 return !isNil(params[0]), "" 151 } 152 153 // ----------------------------------------------------------------------- 154 // Equals checker. 155 156 type equalsChecker struct { 157 *CheckerInfo 158 } 159 160 // The Equals checker verifies that the obtained value is equal to 161 // the expected value, according to usual Go semantics for ==. 162 // 163 // For example: 164 // 165 // c.Assert(value, Equals, 42) 166 // 167 var Equals Checker = &equalsChecker{ 168 &CheckerInfo{Name: "Equals", Params: []string{"obtained", "expected"}}, 169 } 170 171 func (checker *equalsChecker) Check(params []interface{}, names []string) (result bool, error string) { 172 defer func() { 173 if v := recover(); v != nil { 174 result = false 175 error = fmt.Sprint(v) 176 } 177 }() 178 return params[0] == params[1], "" 179 } 180 181 // ----------------------------------------------------------------------- 182 // DeepEquals checker. 183 184 type deepEqualsChecker struct { 185 *CheckerInfo 186 } 187 188 // The DeepEquals checker verifies that the obtained value is deep-equal to 189 // the expected value. The check will work correctly even when facing 190 // slices, interfaces, and values of different types (which always fail 191 // the test). 192 // 193 // For example: 194 // 195 // c.Assert(value, DeepEquals, 42) 196 // c.Assert(array, DeepEquals, []string{"hi", "there"}) 197 // 198 var DeepEquals Checker = &deepEqualsChecker{ 199 &CheckerInfo{Name: "DeepEquals", Params: []string{"obtained", "expected"}}, 200 } 201 202 func (checker *deepEqualsChecker) Check(params []interface{}, names []string) (result bool, error string) { 203 return reflect.DeepEqual(params[0], params[1]), "" 204 } 205 206 // ----------------------------------------------------------------------- 207 // HasLen checker. 208 209 type hasLenChecker struct { 210 *CheckerInfo 211 } 212 213 // The HasLen checker verifies that the obtained value has the 214 // provided length. In many cases this is superior to using Equals 215 // in conjuction with the len function because in case the check 216 // fails the value itself will be printed, instead of its length, 217 // providing more details for figuring the problem. 218 // 219 // For example: 220 // 221 // c.Assert(list, HasLen, 5) 222 // 223 var HasLen Checker = &hasLenChecker{ 224 &CheckerInfo{Name: "HasLen", Params: []string{"obtained", "n"}}, 225 } 226 227 func (checker *hasLenChecker) Check(params []interface{}, names []string) (result bool, error string) { 228 n, ok := params[1].(int) 229 if !ok { 230 return false, "n must be an int" 231 } 232 value := reflect.ValueOf(params[0]) 233 switch value.Kind() { 234 case reflect.Map, reflect.Array, reflect.Slice, reflect.Chan, reflect.String: 235 default: 236 return false, "obtained value type has no length" 237 } 238 return value.Len() == n, "" 239 } 240 241 // ----------------------------------------------------------------------- 242 // ErrorMatches checker. 243 244 type errorMatchesChecker struct { 245 *CheckerInfo 246 } 247 248 // The ErrorMatches checker verifies that the error value 249 // is non nil and matches the regular expression provided. 250 // 251 // For example: 252 // 253 // c.Assert(err, ErrorMatches, "perm.*denied") 254 // 255 var ErrorMatches Checker = errorMatchesChecker{ 256 &CheckerInfo{Name: "ErrorMatches", Params: []string{"value", "regex"}}, 257 } 258 259 func (checker errorMatchesChecker) Check(params []interface{}, names []string) (result bool, errStr string) { 260 if params[0] == nil { 261 return false, "Error value is nil" 262 } 263 err, ok := params[0].(error) 264 if !ok { 265 return false, "Value is not an error" 266 } 267 params[0] = err.Error() 268 names[0] = "error" 269 return matches(params[0], params[1]) 270 } 271 272 // ----------------------------------------------------------------------- 273 // Matches checker. 274 275 type matchesChecker struct { 276 *CheckerInfo 277 } 278 279 // The Matches checker verifies that the string provided as the obtained 280 // value (or the string resulting from obtained.String()) matches the 281 // regular expression provided. 282 // 283 // For example: 284 // 285 // c.Assert(err, Matches, "perm.*denied") 286 // 287 var Matches Checker = &matchesChecker{ 288 &CheckerInfo{Name: "Matches", Params: []string{"value", "regex"}}, 289 } 290 291 func (checker *matchesChecker) Check(params []interface{}, names []string) (result bool, error string) { 292 return matches(params[0], params[1]) 293 } 294 295 func matches(value, regex interface{}) (result bool, error string) { 296 reStr, ok := regex.(string) 297 if !ok { 298 return false, "Regex must be a string" 299 } 300 valueStr, valueIsStr := value.(string) 301 if !valueIsStr { 302 if valueWithStr, valueHasStr := value.(fmt.Stringer); valueHasStr { 303 valueStr, valueIsStr = valueWithStr.String(), true 304 } 305 } 306 if valueIsStr { 307 matches, err := regexp.MatchString("^"+reStr+"$", valueStr) 308 if err != nil { 309 return false, "Can't compile regex: " + err.Error() 310 } 311 return matches, "" 312 } 313 return false, "Obtained value is not a string and has no .String()" 314 } 315 316 // ----------------------------------------------------------------------- 317 // Panics checker. 318 319 type panicsChecker struct { 320 *CheckerInfo 321 } 322 323 // The Panics checker verifies that calling the provided zero-argument 324 // function will cause a panic which is deep-equal to the provided value. 325 // 326 // For example: 327 // 328 // c.Assert(func() { f(1, 2) }, Panics, &SomeErrorType{"BOOM"}). 329 // 330 // 331 var Panics Checker = &panicsChecker{ 332 &CheckerInfo{Name: "Panics", Params: []string{"function", "expected"}}, 333 } 334 335 func (checker *panicsChecker) Check(params []interface{}, names []string) (result bool, error string) { 336 f := reflect.ValueOf(params[0]) 337 if f.Kind() != reflect.Func || f.Type().NumIn() != 0 { 338 return false, "Function must take zero arguments" 339 } 340 defer func() { 341 // If the function has not panicked, then don't do the check. 342 if error != "" { 343 return 344 } 345 params[0] = recover() 346 names[0] = "panic" 347 result = reflect.DeepEqual(params[0], params[1]) 348 }() 349 f.Call(nil) 350 return false, "Function has not panicked" 351 } 352 353 type panicMatchesChecker struct { 354 *CheckerInfo 355 } 356 357 // The PanicMatches checker verifies that calling the provided zero-argument 358 // function will cause a panic with an error value matching 359 // the regular expression provided. 360 // 361 // For example: 362 // 363 // c.Assert(func() { f(1, 2) }, PanicMatches, `open.*: no such file or directory`). 364 // 365 // 366 var PanicMatches Checker = &panicMatchesChecker{ 367 &CheckerInfo{Name: "PanicMatches", Params: []string{"function", "expected"}}, 368 } 369 370 func (checker *panicMatchesChecker) Check(params []interface{}, names []string) (result bool, errmsg string) { 371 f := reflect.ValueOf(params[0]) 372 if f.Kind() != reflect.Func || f.Type().NumIn() != 0 { 373 return false, "Function must take zero arguments" 374 } 375 defer func() { 376 // If the function has not panicked, then don't do the check. 377 if errmsg != "" { 378 return 379 } 380 obtained := recover() 381 names[0] = "panic" 382 if e, ok := obtained.(error); ok { 383 params[0] = e.Error() 384 } else if _, ok := obtained.(string); ok { 385 params[0] = obtained 386 } else { 387 errmsg = "Panic value is not a string or an error" 388 return 389 } 390 result, errmsg = matches(params[0], params[1]) 391 }() 392 f.Call(nil) 393 return false, "Function has not panicked" 394 } 395 396 // ----------------------------------------------------------------------- 397 // FitsTypeOf checker. 398 399 type fitsTypeChecker struct { 400 *CheckerInfo 401 } 402 403 // The FitsTypeOf checker verifies that the obtained value is 404 // assignable to a variable with the same type as the provided 405 // sample value. 406 // 407 // For example: 408 // 409 // c.Assert(value, FitsTypeOf, int64(0)) 410 // c.Assert(value, FitsTypeOf, os.Error(nil)) 411 // 412 var FitsTypeOf Checker = &fitsTypeChecker{ 413 &CheckerInfo{Name: "FitsTypeOf", Params: []string{"obtained", "sample"}}, 414 } 415 416 func (checker *fitsTypeChecker) Check(params []interface{}, names []string) (result bool, error string) { 417 obtained := reflect.ValueOf(params[0]) 418 sample := reflect.ValueOf(params[1]) 419 if !obtained.IsValid() { 420 return false, "" 421 } 422 if !sample.IsValid() { 423 return false, "Invalid sample value" 424 } 425 return obtained.Type().AssignableTo(sample.Type()), "" 426 } 427 428 // ----------------------------------------------------------------------- 429 // Implements checker. 430 431 type implementsChecker struct { 432 *CheckerInfo 433 } 434 435 // The Implements checker verifies that the obtained value 436 // implements the interface specified via a pointer to an interface 437 // variable. 438 // 439 // For example: 440 // 441 // var e os.Error 442 // c.Assert(err, Implements, &e) 443 // 444 var Implements Checker = &implementsChecker{ 445 &CheckerInfo{Name: "Implements", Params: []string{"obtained", "ifaceptr"}}, 446 } 447 448 func (checker *implementsChecker) Check(params []interface{}, names []string) (result bool, error string) { 449 obtained := reflect.ValueOf(params[0]) 450 ifaceptr := reflect.ValueOf(params[1]) 451 if !obtained.IsValid() { 452 return false, "" 453 } 454 if !ifaceptr.IsValid() || ifaceptr.Kind() != reflect.Ptr || ifaceptr.Elem().Kind() != reflect.Interface { 455 return false, "ifaceptr should be a pointer to an interface variable" 456 } 457 return obtained.Type().Implements(ifaceptr.Elem().Type()), "" 458 }