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 &notChecker{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 = &notNilChecker{
   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  }