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