| package shakers |
| |
| import ( |
| "fmt" |
| "time" |
| |
| "github.com/go-check/check" |
| ) |
| |
| // Default format when parsing (in addition to RFC and default time formats..) |
| const shortForm = "2006-01-02" |
| |
| // IsBefore checker verifies the specified value is before the specified time. |
| // It is exclusive. |
| // |
| // c.Assert(myTime, IsBefore, theTime, check.Commentf("bouuuhhh")) |
| // |
| var IsBefore check.Checker = &isBeforeChecker{ |
| &check.CheckerInfo{ |
| Name: "IsBefore", |
| Params: []string{"obtained", "expected"}, |
| }, |
| } |
| |
| type isBeforeChecker struct { |
| *check.CheckerInfo |
| } |
| |
| func (checker *isBeforeChecker) Check(params []interface{}, names []string) (bool, string) { |
| return isBefore(params[0], params[1]) |
| } |
| |
| func isBefore(value, t interface{}) (bool, string) { |
| tTime, ok := parseTime(t) |
| if !ok { |
| return false, "expected must be a Time struct, or parseable." |
| } |
| valueTime, valueIsTime := parseTime(value) |
| if valueIsTime { |
| return valueTime.Before(tTime), "" |
| } |
| return false, "obtained value is not a time.Time struct or parseable as a time." |
| } |
| |
| // IsAfter checker verifies the specified value is before the specified time. |
| // It is exclusive. |
| // |
| // c.Assert(myTime, IsAfter, theTime, check.Commentf("bouuuhhh")) |
| // |
| var IsAfter check.Checker = &isAfterChecker{ |
| &check.CheckerInfo{ |
| Name: "IsAfter", |
| Params: []string{"obtained", "expected"}, |
| }, |
| } |
| |
| type isAfterChecker struct { |
| *check.CheckerInfo |
| } |
| |
| func (checker *isAfterChecker) Check(params []interface{}, names []string) (bool, string) { |
| return isAfter(params[0], params[1]) |
| } |
| |
| func isAfter(value, t interface{}) (bool, string) { |
| tTime, ok := parseTime(t) |
| if !ok { |
| return false, "expected must be a Time struct, or parseable." |
| } |
| valueTime, valueIsTime := parseTime(value) |
| if valueIsTime { |
| return valueTime.After(tTime), "" |
| } |
| return false, "obtained value is not a time.Time struct or parseable as a time." |
| } |
| |
| // IsBetween checker verifies the specified time is between the specified start |
| // and end. It's exclusive so if the specified time is at the tip of the interval. |
| // |
| // c.Assert(myTime, IsBetween, startTime, endTime, check.Commentf("bouuuhhh")) |
| // |
| var IsBetween check.Checker = &isBetweenChecker{ |
| &check.CheckerInfo{ |
| Name: "IsBetween", |
| Params: []string{"obtained", "start", "end"}, |
| }, |
| } |
| |
| type isBetweenChecker struct { |
| *check.CheckerInfo |
| } |
| |
| func (checker *isBetweenChecker) Check(params []interface{}, names []string) (bool, string) { |
| return isBetween(params[0], params[1], params[2]) |
| } |
| |
| func isBetween(value, start, end interface{}) (bool, string) { |
| startTime, ok := parseTime(start) |
| if !ok { |
| return false, "start must be a Time struct, or parseable." |
| } |
| endTime, ok := parseTime(end) |
| if !ok { |
| return false, "end must be a Time struct, or parseable." |
| } |
| valueTime, valueIsTime := parseTime(value) |
| if valueIsTime { |
| return valueTime.After(startTime) && valueTime.Before(endTime), "" |
| } |
| return false, "obtained value is not a time.Time struct or parseable as a time." |
| } |
| |
| // TimeEquals checker verifies the specified time is the equal to the expected |
| // time. |
| // |
| // c.Assert(myTime, TimeEquals, expected, check.Commentf("bouhhh")) |
| // |
| // It's possible to ignore some part of the time (like hours, minutes, etc..) using |
| // the TimeIgnore checker with it. |
| // |
| // c.Assert(myTime, TimeIgnore(TimeEquals, time.Hour), expected, check.Commentf("... bouh..")) |
| // |
| var TimeEquals check.Checker = &timeEqualsChecker{ |
| &check.CheckerInfo{ |
| Name: "TimeEquals", |
| Params: []string{"obtained", "expected"}, |
| }, |
| } |
| |
| type timeEqualsChecker struct { |
| *check.CheckerInfo |
| } |
| |
| func (checker *timeEqualsChecker) Check(params []interface{}, names []string) (bool, string) { |
| return timeEquals(params[0], params[1]) |
| } |
| |
| func timeEquals(obtained, expected interface{}) (bool, string) { |
| expectedTime, ok := parseTime(expected) |
| if !ok { |
| return false, "expected must be a Time struct, or parseable." |
| } |
| valueTime, valueIsTime := parseTime(obtained) |
| if valueIsTime { |
| return valueTime.Equal(expectedTime), "" |
| } |
| return false, "obtained value is not a time.Time struct or parseable as a time." |
| } |
| |
| // TimeIgnore checker will ignore some part of the time on the encapsulated checker. |
| // |
| // c.Assert(myTime, TimeIgnore(IsBetween, time.Second), start, end) |
| // |
| // FIXME use interface{} for ignore (to enable "Month", .. |
| func TimeIgnore(checker check.Checker, ignore time.Duration) check.Checker { |
| return &timeIgnoreChecker{ |
| sub: checker, |
| ignore: ignore, |
| } |
| } |
| |
| type timeIgnoreChecker struct { |
| sub check.Checker |
| ignore time.Duration |
| } |
| |
| func (checker *timeIgnoreChecker) Info() *check.CheckerInfo { |
| info := *checker.sub.Info() |
| info.Name = fmt.Sprintf("TimeIgnore(%s, %v)", info.Name, checker.ignore) |
| return &info |
| } |
| |
| func (checker *timeIgnoreChecker) Check(params []interface{}, names []string) (bool, string) { |
| // Naive implementation : all params are supposed to be date |
| mParams := make([]interface{}, len(params)) |
| for index, param := range params { |
| paramTime, ok := parseTime(param) |
| if !ok { |
| return false, fmt.Sprintf("%s must be a Time struct, or parseable.", names[index]) |
| } |
| year := paramTime.Year() |
| month := paramTime.Month() |
| day := paramTime.Day() |
| hour := paramTime.Hour() |
| min := paramTime.Minute() |
| sec := paramTime.Second() |
| nsec := paramTime.Nanosecond() |
| location := paramTime.Location() |
| switch checker.ignore { |
| case time.Hour: |
| hour = 0 |
| fallthrough |
| case time.Minute: |
| min = 0 |
| fallthrough |
| case time.Second: |
| sec = 0 |
| fallthrough |
| case time.Millisecond: |
| fallthrough |
| case time.Microsecond: |
| fallthrough |
| case time.Nanosecond: |
| nsec = 0 |
| } |
| mParams[index] = time.Date(year, month, day, hour, min, sec, nsec, location) |
| } |
| return checker.sub.Check(mParams, names) |
| } |
| |
| func parseTime(datetime interface{}) (time.Time, bool) { |
| switch datetime.(type) { |
| case time.Time: |
| return datetime.(time.Time), true |
| case string: |
| return parseTimeAsString(datetime.(string)) |
| default: |
| if datetimeWithStr, ok := datetime.(fmt.Stringer); ok { |
| return parseTimeAsString(datetimeWithStr.String()) |
| } |
| return time.Time{}, false |
| } |
| } |
| |
| func parseTimeAsString(timeAsStr string) (time.Time, bool) { |
| forms := []string{shortForm, time.RFC3339, time.RFC3339Nano, time.RFC822, time.RFC822Z} |
| for _, form := range forms { |
| datetime, err := time.Parse(form, timeAsStr) |
| if err == nil { |
| return datetime, true |
| } |
| } |
| return time.Time{}, false |
| } |