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  }