The shakers library defines a bunch of go-check checkers to ease
writing tests.
Signed-off-by: Vincent Demeester <vincent@sbr.pm>
| ... | ... |
@@ -9,7 +9,7 @@ source 'hack/.vendor-helpers.sh' |
| 9 | 9 |
clone git github.com/Azure/go-ansiterm 70b2c90b260171e829f1ebd7c17f600c11858dbe |
| 10 | 10 |
clone git github.com/Sirupsen/logrus v0.8.2 # logrus is a common dependency among multiple deps |
| 11 | 11 |
clone git github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a |
| 12 |
-clone git github.com/go-check/check 64131543e7896d5bcc6bd5a76287eb75ea96c673 |
|
| 12 |
+clone git github.com/go-check/check 11d3bc7aa68e238947792f30573146a3231fc0f1 |
|
| 13 | 13 |
clone git github.com/gorilla/context 14f550f51a |
| 14 | 14 |
clone git github.com/gorilla/mux e444e69cbd |
| 15 | 15 |
clone git github.com/kr/pty 5cf931ef8f |
| ... | ... |
@@ -17,6 +17,7 @@ clone git github.com/mattn/go-sqlite3 v1.1.0 |
| 17 | 17 |
clone git github.com/microsoft/hcsshim 7f646aa6b26bcf90caee91e93cde4a80d0d8a83e |
| 18 | 18 |
clone git github.com/mistifyio/go-zfs v2.1.1 |
| 19 | 19 |
clone git github.com/tchap/go-patricia v2.1.0 |
| 20 |
+clone git github.com/vdemeester/shakers 3c10293ce22b900c27acad7b28656196fcc2f73b |
|
| 20 | 21 |
clone git golang.org/x/net 3cffabab72adf04f8e3b01c5baf775361837b5fe https://github.com/golang/net.git |
| 21 | 22 |
|
| 22 | 23 |
#get libnetwork packages |
| ... | ... |
@@ -2,17 +2,14 @@ |
| 2 | 2 |
package checker |
| 3 | 3 |
|
| 4 | 4 |
import ( |
| 5 |
- "fmt" |
|
| 6 |
- "strings" |
|
| 7 |
- |
|
| 8 | 5 |
"github.com/go-check/check" |
| 6 |
+ "github.com/vdemeester/shakers" |
|
| 9 | 7 |
) |
| 10 | 8 |
|
| 11 | 9 |
// As a commodity, we bring all check.Checker variables into the current namespace to avoid having |
| 12 | 10 |
// to think about check.X versus checker.X. |
| 13 | 11 |
var ( |
| 14 | 12 |
DeepEquals = check.DeepEquals |
| 15 |
- Equals = check.Equals |
|
| 16 | 13 |
ErrorMatches = check.ErrorMatches |
| 17 | 14 |
FitsTypeOf = check.FitsTypeOf |
| 18 | 15 |
HasLen = check.HasLen |
| ... | ... |
@@ -23,37 +20,27 @@ var ( |
| 23 | 23 |
NotNil = check.NotNil |
| 24 | 24 |
PanicMatches = check.PanicMatches |
| 25 | 25 |
Panics = check.Panics |
| 26 |
-) |
|
| 27 |
- |
|
| 28 |
-// Contains checker verifies that string value contains a substring. |
|
| 29 |
-var Contains check.Checker = &containsChecker{
|
|
| 30 |
- &check.CheckerInfo{
|
|
| 31 |
- Name: "Contains", |
|
| 32 |
- Params: []string{"value", "substring"},
|
|
| 33 |
- }, |
|
| 34 |
-} |
|
| 35 | 26 |
|
| 36 |
-type containsChecker struct {
|
|
| 37 |
- *check.CheckerInfo |
|
| 38 |
-} |
|
| 39 |
- |
|
| 40 |
-func (checker *containsChecker) Check(params []interface{}, names []string) (bool, string) {
|
|
| 41 |
- return contains(params[0], params[1]) |
|
| 42 |
-} |
|
| 43 |
- |
|
| 44 |
-func contains(value, substring interface{}) (bool, string) {
|
|
| 45 |
- substringStr, ok := substring.(string) |
|
| 46 |
- if !ok {
|
|
| 47 |
- return false, "Substring must be a string" |
|
| 48 |
- } |
|
| 49 |
- valueStr, valueIsStr := value.(string) |
|
| 50 |
- if !valueIsStr {
|
|
| 51 |
- if valueWithStr, valueHasStr := value.(fmt.Stringer); valueHasStr {
|
|
| 52 |
- valueStr, valueIsStr = valueWithStr.String(), true |
|
| 53 |
- } |
|
| 54 |
- } |
|
| 55 |
- if valueIsStr {
|
|
| 56 |
- return strings.Contains(valueStr, substringStr), "" |
|
| 57 |
- } |
|
| 58 |
- return false, "Obtained value is not a string and has no .String()" |
|
| 59 |
-} |
|
| 27 |
+ Contains = shakers.Contains |
|
| 28 |
+ ContainsAny = shakers.ContainsAny |
|
| 29 |
+ Count = shakers.Count |
|
| 30 |
+ Equals = shakers.Equals |
|
| 31 |
+ EqualFold = shakers.EqualFold |
|
| 32 |
+ False = shakers.False |
|
| 33 |
+ GreaterOrEqualThan = shakers.GreaterOrEqualThan |
|
| 34 |
+ GreaterThan = shakers.GreaterThan |
|
| 35 |
+ HasPrefix = shakers.HasPrefix |
|
| 36 |
+ HasSuffix = shakers.HasSuffix |
|
| 37 |
+ Index = shakers.Index |
|
| 38 |
+ IndexAny = shakers.IndexAny |
|
| 39 |
+ IsAfter = shakers.IsAfter |
|
| 40 |
+ IsBefore = shakers.IsBefore |
|
| 41 |
+ IsBetween = shakers.IsBetween |
|
| 42 |
+ IsLower = shakers.IsLower |
|
| 43 |
+ IsUpper = shakers.IsUpper |
|
| 44 |
+ LessOrEqualThan = shakers.LessOrEqualThan |
|
| 45 |
+ LessThan = shakers.LessThan |
|
| 46 |
+ TimeEquals = shakers.TimeEquals |
|
| 47 |
+ True = shakers.True |
|
| 48 |
+ TimeIgnore = shakers.TimeIgnore |
|
| 49 |
+) |
| 60 | 50 |
deleted file mode 100644 |
| ... | ... |
@@ -1,57 +0,0 @@ |
| 1 |
-package checker |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "reflect" |
|
| 5 |
- "testing" |
|
| 6 |
- |
|
| 7 |
- "github.com/go-check/check" |
|
| 8 |
-) |
|
| 9 |
- |
|
| 10 |
-func Test(t *testing.T) {
|
|
| 11 |
- check.TestingT(t) |
|
| 12 |
-} |
|
| 13 |
- |
|
| 14 |
-func init() {
|
|
| 15 |
- check.Suite(&CheckersS{})
|
|
| 16 |
-} |
|
| 17 |
- |
|
| 18 |
-type CheckersS struct{}
|
|
| 19 |
- |
|
| 20 |
-var _ = check.Suite(&CheckersS{})
|
|
| 21 |
- |
|
| 22 |
-func testInfo(c *check.C, checker check.Checker, name string, paramNames []string) {
|
|
| 23 |
- info := checker.Info() |
|
| 24 |
- if info.Name != name {
|
|
| 25 |
- c.Fatalf("Got name %s, expected %s", info.Name, name)
|
|
| 26 |
- } |
|
| 27 |
- if !reflect.DeepEqual(info.Params, paramNames) {
|
|
| 28 |
- c.Fatalf("Got param names %#v, expected %#v", info.Params, paramNames)
|
|
| 29 |
- } |
|
| 30 |
-} |
|
| 31 |
- |
|
| 32 |
-func testCheck(c *check.C, checker check.Checker, expectedResult bool, expectedError string, params ...interface{}) ([]interface{}, []string) {
|
|
| 33 |
- info := checker.Info() |
|
| 34 |
- if len(params) != len(info.Params) {
|
|
| 35 |
- c.Fatalf("unexpected param count in test; expected %d got %d", len(info.Params), len(params))
|
|
| 36 |
- } |
|
| 37 |
- names := append([]string{}, info.Params...)
|
|
| 38 |
- result, error := checker.Check(params, names) |
|
| 39 |
- if result != expectedResult || error != expectedError {
|
|
| 40 |
- c.Fatalf("%s.Check(%#v) returned (%#v, %#v) rather than (%#v, %#v)",
|
|
| 41 |
- info.Name, params, result, error, expectedResult, expectedError) |
|
| 42 |
- } |
|
| 43 |
- return params, names |
|
| 44 |
-} |
|
| 45 |
- |
|
| 46 |
-func (s *CheckersS) TestContains(c *check.C) {
|
|
| 47 |
- testInfo(c, Contains, "Contains", []string{"value", "substring"})
|
|
| 48 |
- |
|
| 49 |
- testCheck(c, Contains, true, "", "abcd", "bc") |
|
| 50 |
- testCheck(c, Contains, false, "", "abcd", "efg") |
|
| 51 |
- testCheck(c, Contains, false, "", "", "bc") |
|
| 52 |
- testCheck(c, Contains, true, "", "abcd", "") |
|
| 53 |
- testCheck(c, Contains, true, "", "", "") |
|
| 54 |
- |
|
| 55 |
- testCheck(c, Contains, false, "Obtained value is not a string and has no .String()", 12, "1") |
|
| 56 |
- testCheck(c, Contains, false, "Substring must be a string", "", 1) |
|
| 57 |
-} |
| ... | ... |
@@ -1,6 +1,30 @@ |
| 1 |
-// Copyright 2009 The Go Authors. All rights reserved. |
|
| 2 |
-// Use of this source code is governed by a BSD-style |
|
| 3 |
-// license that can be found in the LICENSE file. |
|
| 1 |
+// Copyright (c) 2012 The Go Authors. All rights reserved. |
|
| 2 |
+// |
|
| 3 |
+// Redistribution and use in source and binary forms, with or without |
|
| 4 |
+// modification, are permitted provided that the following conditions are |
|
| 5 |
+// met: |
|
| 6 |
+// |
|
| 7 |
+// * Redistributions of source code must retain the above copyright |
|
| 8 |
+// notice, this list of conditions and the following disclaimer. |
|
| 9 |
+// * Redistributions in binary form must reproduce the above |
|
| 10 |
+// copyright notice, this list of conditions and the following disclaimer |
|
| 11 |
+// in the documentation and/or other materials provided with the |
|
| 12 |
+// distribution. |
|
| 13 |
+// * Neither the name of Google Inc. nor the names of its |
|
| 14 |
+// contributors may be used to endorse or promote products derived from |
|
| 15 |
+// this software without specific prior written permission. |
|
| 16 |
+// |
|
| 17 |
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
| 18 |
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
| 19 |
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
| 20 |
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
| 21 |
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
| 22 |
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 23 |
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
| 24 |
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
| 25 |
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
| 26 |
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
| 27 |
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
| 4 | 28 |
|
| 5 | 29 |
package check |
| 6 | 30 |
|
| ... | ... |
@@ -21,6 +21,7 @@ import ( |
| 21 | 21 |
"strconv" |
| 22 | 22 |
"strings" |
| 23 | 23 |
"sync" |
| 24 |
+ "sync/atomic" |
|
| 24 | 25 |
"time" |
| 25 | 26 |
) |
| 26 | 27 |
|
| ... | ... |
@@ -43,7 +44,7 @@ const ( |
| 43 | 43 |
missedSt |
| 44 | 44 |
) |
| 45 | 45 |
|
| 46 |
-type funcStatus int |
|
| 46 |
+type funcStatus uint32 |
|
| 47 | 47 |
|
| 48 | 48 |
// A method value can't reach its own Method structure. |
| 49 | 49 |
type methodType struct {
|
| ... | ... |
@@ -81,7 +82,7 @@ type C struct {
|
| 81 | 81 |
method *methodType |
| 82 | 82 |
kind funcKind |
| 83 | 83 |
testName string |
| 84 |
- status funcStatus |
|
| 84 |
+ _status funcStatus |
|
| 85 | 85 |
logb *logger |
| 86 | 86 |
logw io.Writer |
| 87 | 87 |
done chan *C |
| ... | ... |
@@ -93,6 +94,14 @@ type C struct {
|
| 93 | 93 |
timer |
| 94 | 94 |
} |
| 95 | 95 |
|
| 96 |
+func (c *C) status() funcStatus {
|
|
| 97 |
+ return funcStatus(atomic.LoadUint32((*uint32)(&c._status))) |
|
| 98 |
+} |
|
| 99 |
+ |
|
| 100 |
+func (c *C) setStatus(s funcStatus) {
|
|
| 101 |
+ atomic.StoreUint32((*uint32)(&c._status), uint32(s)) |
|
| 102 |
+} |
|
| 103 |
+ |
|
| 96 | 104 |
func (c *C) stopNow() {
|
| 97 | 105 |
runtime.Goexit() |
| 98 | 106 |
} |
| ... | ... |
@@ -326,7 +335,7 @@ func (c *C) logPanic(skip int, value interface{}) {
|
| 326 | 326 |
if name == "Value.call" && strings.HasSuffix(path, valueGo) {
|
| 327 | 327 |
continue |
| 328 | 328 |
} |
| 329 |
- if name == "call16" && strings.Contains(path, asmGo) {
|
|
| 329 |
+ if (name == "call16" || name == "call32") && strings.Contains(path, asmGo) {
|
|
| 330 | 330 |
continue |
| 331 | 331 |
} |
| 332 | 332 |
c.logf("%s:%d\n in %s", nicePath(file), line, name)
|
| ... | ... |
@@ -455,7 +464,7 @@ func (tracker *resultTracker) _loopRoutine() {
|
| 455 | 455 |
tracker._waiting += 1 |
| 456 | 456 |
case c = <-tracker._doneChan: |
| 457 | 457 |
tracker._waiting -= 1 |
| 458 |
- switch c.status {
|
|
| 458 |
+ switch c.status() {
|
|
| 459 | 459 |
case succeededSt: |
| 460 | 460 |
if c.kind == testKd {
|
| 461 | 461 |
if c.mustFail {
|
| ... | ... |
@@ -601,15 +610,15 @@ func (runner *suiteRunner) run() *Result {
|
| 601 | 601 |
runner.tracker.start() |
| 602 | 602 |
if runner.checkFixtureArgs() {
|
| 603 | 603 |
c := runner.runFixture(runner.setUpSuite, "", nil) |
| 604 |
- if c == nil || c.status == succeededSt {
|
|
| 604 |
+ if c == nil || c.status() == succeededSt {
|
|
| 605 | 605 |
for i := 0; i != len(runner.tests); i++ {
|
| 606 | 606 |
c := runner.runTest(runner.tests[i]) |
| 607 |
- if c.status == fixturePanickedSt {
|
|
| 607 |
+ if c.status() == fixturePanickedSt {
|
|
| 608 | 608 |
runner.skipTests(missedSt, runner.tests[i+1:]) |
| 609 | 609 |
break |
| 610 | 610 |
} |
| 611 | 611 |
} |
| 612 |
- } else if c != nil && c.status == skippedSt {
|
|
| 612 |
+ } else if c != nil && c.status() == skippedSt {
|
|
| 613 | 613 |
runner.skipTests(skippedSt, runner.tests) |
| 614 | 614 |
} else {
|
| 615 | 615 |
runner.skipTests(missedSt, runner.tests) |
| ... | ... |
@@ -674,22 +683,22 @@ func (runner *suiteRunner) callDone(c *C) {
|
| 674 | 674 |
switch v := value.(type) {
|
| 675 | 675 |
case *fixturePanic: |
| 676 | 676 |
if v.status == skippedSt {
|
| 677 |
- c.status = skippedSt |
|
| 677 |
+ c.setStatus(skippedSt) |
|
| 678 | 678 |
} else {
|
| 679 | 679 |
c.logSoftPanic("Fixture has panicked (see related PANIC)")
|
| 680 |
- c.status = fixturePanickedSt |
|
| 680 |
+ c.setStatus(fixturePanickedSt) |
|
| 681 | 681 |
} |
| 682 | 682 |
default: |
| 683 | 683 |
c.logPanic(1, value) |
| 684 |
- c.status = panickedSt |
|
| 684 |
+ c.setStatus(panickedSt) |
|
| 685 | 685 |
} |
| 686 | 686 |
} |
| 687 | 687 |
if c.mustFail {
|
| 688 |
- switch c.status {
|
|
| 688 |
+ switch c.status() {
|
|
| 689 | 689 |
case failedSt: |
| 690 |
- c.status = succeededSt |
|
| 690 |
+ c.setStatus(succeededSt) |
|
| 691 | 691 |
case succeededSt: |
| 692 |
- c.status = failedSt |
|
| 692 |
+ c.setStatus(failedSt) |
|
| 693 | 693 |
c.logString("Error: Test succeeded, but was expected to fail")
|
| 694 | 694 |
c.logString("Reason: " + c.reason)
|
| 695 | 695 |
} |
| ... | ... |
@@ -724,11 +733,11 @@ func (runner *suiteRunner) runFixtureWithPanic(method *methodType, testName stri |
| 724 | 724 |
return nil |
| 725 | 725 |
} |
| 726 | 726 |
c := runner.runFixture(method, testName, logb) |
| 727 |
- if c != nil && c.status != succeededSt {
|
|
| 727 |
+ if c != nil && c.status() != succeededSt {
|
|
| 728 | 728 |
if skipped != nil {
|
| 729 |
- *skipped = c.status == skippedSt |
|
| 729 |
+ *skipped = c.status() == skippedSt |
|
| 730 | 730 |
} |
| 731 |
- panic(&fixturePanic{c.status, method})
|
|
| 731 |
+ panic(&fixturePanic{c.status(), method})
|
|
| 732 | 732 |
} |
| 733 | 733 |
return c |
| 734 | 734 |
} |
| ... | ... |
@@ -753,7 +762,7 @@ func (runner *suiteRunner) forkTest(method *methodType) *C {
|
| 753 | 753 |
if mt.NumIn() != 1 || mt.In(0) != reflect.TypeOf(c) {
|
| 754 | 754 |
// Rather than a plain panic, provide a more helpful message when |
| 755 | 755 |
// the argument type is incorrect. |
| 756 |
- c.status = panickedSt |
|
| 756 |
+ c.setStatus(panickedSt) |
|
| 757 | 757 |
c.logArgPanic(c.method, "*check.C") |
| 758 | 758 |
return |
| 759 | 759 |
} |
| ... | ... |
@@ -773,7 +782,7 @@ func (runner *suiteRunner) forkTest(method *methodType) *C {
|
| 773 | 773 |
c.StartTimer() |
| 774 | 774 |
c.method.Call([]reflect.Value{reflect.ValueOf(c)})
|
| 775 | 775 |
c.StopTimer() |
| 776 |
- if c.status != succeededSt || c.duration >= c.benchTime || benchN >= 1e9 {
|
|
| 776 |
+ if c.status() != succeededSt || c.duration >= c.benchTime || benchN >= 1e9 {
|
|
| 777 | 777 |
return |
| 778 | 778 |
} |
| 779 | 779 |
perOpN := int(1e9) |
| ... | ... |
@@ -808,7 +817,7 @@ func (runner *suiteRunner) runTest(method *methodType) *C {
|
| 808 | 808 |
func (runner *suiteRunner) skipTests(status funcStatus, methods []*methodType) {
|
| 809 | 809 |
for _, method := range methods {
|
| 810 | 810 |
runner.runFunc(method, testKd, "", nil, func(c *C) {
|
| 811 |
- c.status = status |
|
| 811 |
+ c.setStatus(status) |
|
| 812 | 812 |
}) |
| 813 | 813 |
} |
| 814 | 814 |
} |
| ... | ... |
@@ -825,7 +834,7 @@ func (runner *suiteRunner) checkFixtureArgs() bool {
|
| 825 | 825 |
succeeded = false |
| 826 | 826 |
runner.runFunc(method, fixtureKd, "", nil, func(c *C) {
|
| 827 | 827 |
c.logArgPanic(method, "*check.C") |
| 828 |
- c.status = panickedSt |
|
| 828 |
+ c.setStatus(panickedSt) |
|
| 829 | 829 |
}) |
| 830 | 830 |
} |
| 831 | 831 |
} |
| ... | ... |
@@ -839,7 +848,7 @@ func (runner *suiteRunner) reportCallStarted(c *C) {
|
| 839 | 839 |
|
| 840 | 840 |
func (runner *suiteRunner) reportCallDone(c *C) {
|
| 841 | 841 |
runner.tracker.callDone(c) |
| 842 |
- switch c.status {
|
|
| 842 |
+ switch c.status() {
|
|
| 843 | 843 |
case succeededSt: |
| 844 | 844 |
if c.mustFail {
|
| 845 | 845 |
runner.output.WriteCallSuccess("FAIL EXPECTED", c)
|
| ... | ... |
@@ -917,7 +926,7 @@ func (ow *outputWriter) WriteCallSuccess(label string, c *C) {
|
| 917 | 917 |
if c.reason != "" {
|
| 918 | 918 |
suffix = " (" + c.reason + ")"
|
| 919 | 919 |
} |
| 920 |
- if c.status == succeededSt {
|
|
| 920 |
+ if c.status() == succeededSt {
|
|
| 921 | 921 |
suffix += "\t" + c.timerString() |
| 922 | 922 |
} |
| 923 | 923 |
suffix += "\n" |
| ... | ... |
@@ -16,7 +16,7 @@ func (c *C) TestName() string {
|
| 16 | 16 |
|
| 17 | 17 |
// Failed returns whether the currently running test has already failed. |
| 18 | 18 |
func (c *C) Failed() bool {
|
| 19 |
- return c.status == failedSt |
|
| 19 |
+ return c.status() == failedSt |
|
| 20 | 20 |
} |
| 21 | 21 |
|
| 22 | 22 |
// Fail marks the currently running test as failed. |
| ... | ... |
@@ -25,7 +25,7 @@ func (c *C) Failed() bool {
|
| 25 | 25 |
// what went wrong. The higher level helper functions will fail the test |
| 26 | 26 |
// and do the logging properly. |
| 27 | 27 |
func (c *C) Fail() {
|
| 28 |
- c.status = failedSt |
|
| 28 |
+ c.setStatus(failedSt) |
|
| 29 | 29 |
} |
| 30 | 30 |
|
| 31 | 31 |
// FailNow marks the currently running test as failed and stops running it. |
| ... | ... |
@@ -40,7 +40,7 @@ func (c *C) FailNow() {
|
| 40 | 40 |
// Succeed marks the currently running test as succeeded, undoing any |
| 41 | 41 |
// previous failures. |
| 42 | 42 |
func (c *C) Succeed() {
|
| 43 |
- c.status = succeededSt |
|
| 43 |
+ c.setStatus(succeededSt) |
|
| 44 | 44 |
} |
| 45 | 45 |
|
| 46 | 46 |
// SucceedNow marks the currently running test as succeeded, undoing any |
| ... | ... |
@@ -72,7 +72,7 @@ func (c *C) Skip(reason string) {
|
| 72 | 72 |
panic("Missing reason why the test is being skipped")
|
| 73 | 73 |
} |
| 74 | 74 |
c.reason = reason |
| 75 |
- c.status = skippedSt |
|
| 75 |
+ c.setStatus(skippedSt) |
|
| 76 | 76 |
c.stopNow() |
| 77 | 77 |
} |
| 78 | 78 |
|
| 0 | 4 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,12 @@ |
| 0 |
+FROM golang:1.5 |
|
| 1 |
+ |
|
| 2 |
+RUN go get golang.org/x/tools/cmd/cover |
|
| 3 |
+RUN go get github.com/golang/lint/golint |
|
| 4 |
+RUN go get golang.org/x/tools/cmd/vet |
|
| 5 |
+ |
|
| 6 |
+WORKDIR /go/src/github.com/vdemeester/shakers |
|
| 7 |
+ |
|
| 8 |
+# enable GO15VENDOREXPERIMENT |
|
| 9 |
+ENV GO15VENDOREXPERIMENT 1 |
|
| 10 |
+ |
|
| 11 |
+COPY . /go/src/github.com/vdemeester/shakers |
| 0 | 12 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,40 @@ |
| 0 |
+.PHONY: all |
|
| 1 |
+ |
|
| 2 |
+SHAKERS_MOUNT := $(if $(BIND_DIR),-v "$(CURDIR)/$(BIND_DIR):/go/src/github.com/vdemeester/shakers/$(BIND_DIR)") |
|
| 3 |
+ |
|
| 4 |
+GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null) |
|
| 5 |
+SHAKERS_DEV_IMAGE := shakers-dev$(if $(GIT_BRANCH),:$(GIT_BRANCH)) |
|
| 6 |
+ |
|
| 7 |
+DOCKER_RUN_SHAKERS := docker run $(if $(CIRCLECI),,--rm) -it $(SHAKERS_ENVS) $(SHAKERS_MOUNT) "$(SHAKERS_DEV_IMAGE)" |
|
| 8 |
+ |
|
| 9 |
+print-%: ; @echo $*=$($*) |
|
| 10 |
+ |
|
| 11 |
+default: binary |
|
| 12 |
+ |
|
| 13 |
+binary: build |
|
| 14 |
+ $(DOCKER_RUN_SHAKERS) ./script/make.sh binary |
|
| 15 |
+ |
|
| 16 |
+test-unit: build |
|
| 17 |
+ $(DOCKER_RUN_SHAKERS) ./script/make.sh test-unit |
|
| 18 |
+ |
|
| 19 |
+validate: build |
|
| 20 |
+ $(DOCKER_RUN_SHAKERS) ./script/make.sh validate-gofmt validate-golint validate-govet |
|
| 21 |
+ |
|
| 22 |
+validate-govet: build |
|
| 23 |
+ $(DOCKER_RUN_SHAKERS) ./script/make.sh validate-govet |
|
| 24 |
+ |
|
| 25 |
+validate-golint: build |
|
| 26 |
+ $(DOCKER_RUN_SHAKERS) ./script/make.sh validate-golint |
|
| 27 |
+ |
|
| 28 |
+validate-gofmt: build |
|
| 29 |
+ $(DOCKER_RUN_SHAKERS) ./script/make.sh validate-gofmt |
|
| 30 |
+ |
|
| 31 |
+build: |
|
| 32 |
+ docker build -t "$(SHAKERS_DEV_IMAGE)" . |
|
| 33 |
+ |
|
| 34 |
+shell: build |
|
| 35 |
+ $(DOCKER_RUN_SHAKERS) /bin/bash |
|
| 36 |
+ |
|
| 37 |
+run-dev: |
|
| 38 |
+ go build |
|
| 39 |
+ ./traefik |
| 0 | 40 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,24 @@ |
| 0 |
+# Shakers |
|
| 1 |
+🐹 + 🐙 = 😽 [](https://circleci.com/gh/vdemeester/shakers) |
|
| 2 |
+ |
|
| 3 |
+A collection of `go-check` Checkers to ease the use of it. |
|
| 4 |
+ |
|
| 5 |
+## Building and testing it |
|
| 6 |
+ |
|
| 7 |
+You need either [docker](https://github.com/docker/docker), or `go` |
|
| 8 |
+and `godep` in order to build and test shakers. |
|
| 9 |
+ |
|
| 10 |
+### Using Docker and Makefile |
|
| 11 |
+ |
|
| 12 |
+You need to run the ``test-unit`` target. |
|
| 13 |
+```bash |
|
| 14 |
+$ make test-unit |
|
| 15 |
+docker build -t "shakers-dev:master" . |
|
| 16 |
+# […] |
|
| 17 |
+docker run --rm -it "shakers-dev:master" ./script/make.sh test-unit |
|
| 18 |
+---> Making bundle: test-unit (in .) |
|
| 19 |
++ go test -cover -coverprofile=cover.out . |
|
| 20 |
+ok github.com/vdemeester/shakers 0.015s coverage: 96.0% of statements |
|
| 21 |
+ |
|
| 22 |
+Test success |
|
| 23 |
+``` |
| 0 | 24 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,46 @@ |
| 0 |
+package shakers |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "github.com/go-check/check" |
|
| 4 |
+) |
|
| 5 |
+ |
|
| 6 |
+// True checker verifies the obtained value is true |
|
| 7 |
+// |
|
| 8 |
+// c.Assert(myBool, True) |
|
| 9 |
+// |
|
| 10 |
+var True check.Checker = &boolChecker{
|
|
| 11 |
+ &check.CheckerInfo{
|
|
| 12 |
+ Name: "True", |
|
| 13 |
+ Params: []string{"obtained"},
|
|
| 14 |
+ }, |
|
| 15 |
+ true, |
|
| 16 |
+} |
|
| 17 |
+ |
|
| 18 |
+// False checker verifies the obtained value is false |
|
| 19 |
+// |
|
| 20 |
+// c.Assert(myBool, False) |
|
| 21 |
+// |
|
| 22 |
+var False check.Checker = &boolChecker{
|
|
| 23 |
+ &check.CheckerInfo{
|
|
| 24 |
+ Name: "False", |
|
| 25 |
+ Params: []string{"obtained"},
|
|
| 26 |
+ }, |
|
| 27 |
+ false, |
|
| 28 |
+} |
|
| 29 |
+ |
|
| 30 |
+type boolChecker struct {
|
|
| 31 |
+ *check.CheckerInfo |
|
| 32 |
+ expected bool |
|
| 33 |
+} |
|
| 34 |
+ |
|
| 35 |
+func (checker *boolChecker) Check(params []interface{}, names []string) (bool, string) {
|
|
| 36 |
+ return is(checker.expected, params[0]) |
|
| 37 |
+} |
|
| 38 |
+ |
|
| 39 |
+func is(expected bool, obtained interface{}) (bool, string) {
|
|
| 40 |
+ obtainedBool, ok := obtained.(bool) |
|
| 41 |
+ if !ok {
|
|
| 42 |
+ return false, "obtained value must be a bool." |
|
| 43 |
+ } |
|
| 44 |
+ return obtainedBool == expected, "" |
|
| 45 |
+} |
| 0 | 11 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,310 @@ |
| 0 |
+package shakers |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "reflect" |
|
| 4 |
+ "time" |
|
| 5 |
+ |
|
| 6 |
+ "github.com/go-check/check" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+// As a commodity, we bring all check.Checker variables into the current namespace to avoid having |
|
| 10 |
+// to think about check.X versus checker.X. |
|
| 11 |
+var ( |
|
| 12 |
+ DeepEquals = check.DeepEquals |
|
| 13 |
+ ErrorMatches = check.ErrorMatches |
|
| 14 |
+ FitsTypeOf = check.FitsTypeOf |
|
| 15 |
+ HasLen = check.HasLen |
|
| 16 |
+ Implements = check.Implements |
|
| 17 |
+ IsNil = check.IsNil |
|
| 18 |
+ Matches = check.Matches |
|
| 19 |
+ Not = check.Not |
|
| 20 |
+ NotNil = check.NotNil |
|
| 21 |
+ PanicMatches = check.PanicMatches |
|
| 22 |
+ Panics = check.Panics |
|
| 23 |
+) |
|
| 24 |
+ |
|
| 25 |
+// Equaler is an interface implemented if the type has a Equal method. |
|
| 26 |
+// This is used to compare struct using shakers.Equals. |
|
| 27 |
+type Equaler interface {
|
|
| 28 |
+ Equal(Equaler) bool |
|
| 29 |
+} |
|
| 30 |
+ |
|
| 31 |
+// Equals checker verifies the obtained value is equal to the specified one. |
|
| 32 |
+// It's is smart in a wait that it supports several *types* (built-in, Equaler, |
|
| 33 |
+// time.Time) |
|
| 34 |
+// |
|
| 35 |
+// c.Assert(myStruct, Equals, aStruct, check.Commentf("bouuuhh"))
|
|
| 36 |
+// c.Assert(myTime, Equals, aTime, check.Commentf("bouuuhh"))
|
|
| 37 |
+// |
|
| 38 |
+var Equals check.Checker = &equalChecker{
|
|
| 39 |
+ &check.CheckerInfo{
|
|
| 40 |
+ Name: "Equals", |
|
| 41 |
+ Params: []string{"obtained", "expected"},
|
|
| 42 |
+ }, |
|
| 43 |
+} |
|
| 44 |
+ |
|
| 45 |
+type equalChecker struct {
|
|
| 46 |
+ *check.CheckerInfo |
|
| 47 |
+} |
|
| 48 |
+ |
|
| 49 |
+func (checker *equalChecker) Check(params []interface{}, names []string) (bool, string) {
|
|
| 50 |
+ return isEqual(params[0], params[1]) |
|
| 51 |
+} |
|
| 52 |
+ |
|
| 53 |
+func isEqual(obtained, expected interface{}) (bool, string) {
|
|
| 54 |
+ switch obtained.(type) {
|
|
| 55 |
+ case time.Time: |
|
| 56 |
+ return timeEquals(obtained, expected) |
|
| 57 |
+ case Equaler: |
|
| 58 |
+ return equalerEquals(obtained, expected) |
|
| 59 |
+ default: |
|
| 60 |
+ if reflect.TypeOf(obtained) != reflect.TypeOf(expected) {
|
|
| 61 |
+ return false, "obtained value and expected value have not the same type." |
|
| 62 |
+ } |
|
| 63 |
+ return obtained == expected, "" |
|
| 64 |
+ } |
|
| 65 |
+} |
|
| 66 |
+ |
|
| 67 |
+func equalerEquals(obtained, expected interface{}) (bool, string) {
|
|
| 68 |
+ expectedEqualer, ok := expected.(Equaler) |
|
| 69 |
+ if !ok {
|
|
| 70 |
+ return false, "expected value must be an Equaler - implementing Equal(Equaler)." |
|
| 71 |
+ } |
|
| 72 |
+ obtainedEqualer, ok := obtained.(Equaler) |
|
| 73 |
+ if !ok {
|
|
| 74 |
+ return false, "obtained value must be an Equaler - implementing Equal(Equaler)." |
|
| 75 |
+ } |
|
| 76 |
+ return obtainedEqualer.Equal(expectedEqualer), "" |
|
| 77 |
+} |
|
| 78 |
+ |
|
| 79 |
+// GreaterThan checker verifies the obtained value is greater than the specified one. |
|
| 80 |
+// It's is smart in a wait that it supports several *types* (built-in, time.Time) |
|
| 81 |
+// |
|
| 82 |
+// c.Assert(myTime, GreaterThan, aTime, check.Commentf("bouuuhh"))
|
|
| 83 |
+// c.Assert(myInt, GreaterThan, 2, check.Commentf("bouuuhh"))
|
|
| 84 |
+// |
|
| 85 |
+var GreaterThan check.Checker = &greaterThanChecker{
|
|
| 86 |
+ &check.CheckerInfo{
|
|
| 87 |
+ Name: "GreaterThan", |
|
| 88 |
+ Params: []string{"obtained", "expected"},
|
|
| 89 |
+ }, |
|
| 90 |
+} |
|
| 91 |
+ |
|
| 92 |
+type greaterThanChecker struct {
|
|
| 93 |
+ *check.CheckerInfo |
|
| 94 |
+} |
|
| 95 |
+ |
|
| 96 |
+func (checker *greaterThanChecker) Check(params []interface{}, names []string) (bool, string) {
|
|
| 97 |
+ return greaterThan(params[0], params[1]) |
|
| 98 |
+} |
|
| 99 |
+ |
|
| 100 |
+func greaterThan(obtained, expected interface{}) (bool, string) {
|
|
| 101 |
+ if _, ok := obtained.(time.Time); ok {
|
|
| 102 |
+ return isAfter(obtained, expected) |
|
| 103 |
+ } |
|
| 104 |
+ if reflect.TypeOf(obtained) != reflect.TypeOf(expected) {
|
|
| 105 |
+ return false, "obtained value and expected value have not the same type." |
|
| 106 |
+ } |
|
| 107 |
+ switch v := obtained.(type) {
|
|
| 108 |
+ case float32: |
|
| 109 |
+ return v > expected.(float32), "" |
|
| 110 |
+ case float64: |
|
| 111 |
+ return v > expected.(float64), "" |
|
| 112 |
+ case int: |
|
| 113 |
+ return v > expected.(int), "" |
|
| 114 |
+ case int8: |
|
| 115 |
+ return v > expected.(int8), "" |
|
| 116 |
+ case int16: |
|
| 117 |
+ return v > expected.(int16), "" |
|
| 118 |
+ case int32: |
|
| 119 |
+ return v > expected.(int32), "" |
|
| 120 |
+ case int64: |
|
| 121 |
+ return v > expected.(int64), "" |
|
| 122 |
+ case uint: |
|
| 123 |
+ return v > expected.(uint), "" |
|
| 124 |
+ case uint8: |
|
| 125 |
+ return v > expected.(uint8), "" |
|
| 126 |
+ case uint16: |
|
| 127 |
+ return v > expected.(uint16), "" |
|
| 128 |
+ case uint32: |
|
| 129 |
+ return v > expected.(uint32), "" |
|
| 130 |
+ case uint64: |
|
| 131 |
+ return v > expected.(uint64), "" |
|
| 132 |
+ default: |
|
| 133 |
+ return false, "obtained value type not supported." |
|
| 134 |
+ } |
|
| 135 |
+} |
|
| 136 |
+ |
|
| 137 |
+// GreaterOrEqualThan checker verifies the obtained value is greater or equal than the specified one. |
|
| 138 |
+// It's is smart in a wait that it supports several *types* (built-in, time.Time) |
|
| 139 |
+// |
|
| 140 |
+// c.Assert(myTime, GreaterOrEqualThan, aTime, check.Commentf("bouuuhh"))
|
|
| 141 |
+// c.Assert(myInt, GreaterOrEqualThan, 2, check.Commentf("bouuuhh"))
|
|
| 142 |
+// |
|
| 143 |
+var GreaterOrEqualThan check.Checker = &greaterOrEqualThanChecker{
|
|
| 144 |
+ &check.CheckerInfo{
|
|
| 145 |
+ Name: "GreaterOrEqualThan", |
|
| 146 |
+ Params: []string{"obtained", "expected"},
|
|
| 147 |
+ }, |
|
| 148 |
+} |
|
| 149 |
+ |
|
| 150 |
+type greaterOrEqualThanChecker struct {
|
|
| 151 |
+ *check.CheckerInfo |
|
| 152 |
+} |
|
| 153 |
+ |
|
| 154 |
+func (checker *greaterOrEqualThanChecker) Check(params []interface{}, names []string) (bool, string) {
|
|
| 155 |
+ return greaterOrEqualThan(params[0], params[1]) |
|
| 156 |
+} |
|
| 157 |
+ |
|
| 158 |
+func greaterOrEqualThan(obtained, expected interface{}) (bool, string) {
|
|
| 159 |
+ if _, ok := obtained.(time.Time); ok {
|
|
| 160 |
+ return isAfter(obtained, expected) |
|
| 161 |
+ } |
|
| 162 |
+ if reflect.TypeOf(obtained) != reflect.TypeOf(expected) {
|
|
| 163 |
+ return false, "obtained value and expected value have not the same type." |
|
| 164 |
+ } |
|
| 165 |
+ switch v := obtained.(type) {
|
|
| 166 |
+ case float32: |
|
| 167 |
+ return v >= expected.(float32), "" |
|
| 168 |
+ case float64: |
|
| 169 |
+ return v >= expected.(float64), "" |
|
| 170 |
+ case int: |
|
| 171 |
+ return v >= expected.(int), "" |
|
| 172 |
+ case int8: |
|
| 173 |
+ return v >= expected.(int8), "" |
|
| 174 |
+ case int16: |
|
| 175 |
+ return v >= expected.(int16), "" |
|
| 176 |
+ case int32: |
|
| 177 |
+ return v >= expected.(int32), "" |
|
| 178 |
+ case int64: |
|
| 179 |
+ return v >= expected.(int64), "" |
|
| 180 |
+ case uint: |
|
| 181 |
+ return v >= expected.(uint), "" |
|
| 182 |
+ case uint8: |
|
| 183 |
+ return v >= expected.(uint8), "" |
|
| 184 |
+ case uint16: |
|
| 185 |
+ return v >= expected.(uint16), "" |
|
| 186 |
+ case uint32: |
|
| 187 |
+ return v >= expected.(uint32), "" |
|
| 188 |
+ case uint64: |
|
| 189 |
+ return v >= expected.(uint64), "" |
|
| 190 |
+ default: |
|
| 191 |
+ return false, "obtained value type not supported." |
|
| 192 |
+ } |
|
| 193 |
+} |
|
| 194 |
+ |
|
| 195 |
+// LessThan checker verifies the obtained value is less than the specified one. |
|
| 196 |
+// It's is smart in a wait that it supports several *types* (built-in, time.Time) |
|
| 197 |
+// |
|
| 198 |
+// c.Assert(myTime, LessThan, aTime, check.Commentf("bouuuhh"))
|
|
| 199 |
+// c.Assert(myInt, LessThan, 2, check.Commentf("bouuuhh"))
|
|
| 200 |
+// |
|
| 201 |
+var LessThan check.Checker = &lessThanChecker{
|
|
| 202 |
+ &check.CheckerInfo{
|
|
| 203 |
+ Name: "LessThan", |
|
| 204 |
+ Params: []string{"obtained", "expected"},
|
|
| 205 |
+ }, |
|
| 206 |
+} |
|
| 207 |
+ |
|
| 208 |
+type lessThanChecker struct {
|
|
| 209 |
+ *check.CheckerInfo |
|
| 210 |
+} |
|
| 211 |
+ |
|
| 212 |
+func (checker *lessThanChecker) Check(params []interface{}, names []string) (bool, string) {
|
|
| 213 |
+ return lessThan(params[0], params[1]) |
|
| 214 |
+} |
|
| 215 |
+ |
|
| 216 |
+func lessThan(obtained, expected interface{}) (bool, string) {
|
|
| 217 |
+ if _, ok := obtained.(time.Time); ok {
|
|
| 218 |
+ return isBefore(obtained, expected) |
|
| 219 |
+ } |
|
| 220 |
+ if reflect.TypeOf(obtained) != reflect.TypeOf(expected) {
|
|
| 221 |
+ return false, "obtained value and expected value have not the same type." |
|
| 222 |
+ } |
|
| 223 |
+ switch v := obtained.(type) {
|
|
| 224 |
+ case float32: |
|
| 225 |
+ return v < expected.(float32), "" |
|
| 226 |
+ case float64: |
|
| 227 |
+ return v < expected.(float64), "" |
|
| 228 |
+ case int: |
|
| 229 |
+ return v < expected.(int), "" |
|
| 230 |
+ case int8: |
|
| 231 |
+ return v < expected.(int8), "" |
|
| 232 |
+ case int16: |
|
| 233 |
+ return v < expected.(int16), "" |
|
| 234 |
+ case int32: |
|
| 235 |
+ return v < expected.(int32), "" |
|
| 236 |
+ case int64: |
|
| 237 |
+ return v < expected.(int64), "" |
|
| 238 |
+ case uint: |
|
| 239 |
+ return v < expected.(uint), "" |
|
| 240 |
+ case uint8: |
|
| 241 |
+ return v < expected.(uint8), "" |
|
| 242 |
+ case uint16: |
|
| 243 |
+ return v < expected.(uint16), "" |
|
| 244 |
+ case uint32: |
|
| 245 |
+ return v < expected.(uint32), "" |
|
| 246 |
+ case uint64: |
|
| 247 |
+ return v < expected.(uint64), "" |
|
| 248 |
+ default: |
|
| 249 |
+ return false, "obtained value type not supported." |
|
| 250 |
+ } |
|
| 251 |
+} |
|
| 252 |
+ |
|
| 253 |
+// LessOrEqualThan checker verifies the obtained value is less or equal than the specified one. |
|
| 254 |
+// It's is smart in a wait that it supports several *types* (built-in, time.Time) |
|
| 255 |
+// |
|
| 256 |
+// c.Assert(myTime, LessThan, aTime, check.Commentf("bouuuhh"))
|
|
| 257 |
+// c.Assert(myInt, LessThan, 2, check.Commentf("bouuuhh"))
|
|
| 258 |
+// |
|
| 259 |
+var LessOrEqualThan check.Checker = &lessOrEqualThanChecker{
|
|
| 260 |
+ &check.CheckerInfo{
|
|
| 261 |
+ Name: "LessOrEqualThan", |
|
| 262 |
+ Params: []string{"obtained", "expected"},
|
|
| 263 |
+ }, |
|
| 264 |
+} |
|
| 265 |
+ |
|
| 266 |
+type lessOrEqualThanChecker struct {
|
|
| 267 |
+ *check.CheckerInfo |
|
| 268 |
+} |
|
| 269 |
+ |
|
| 270 |
+func (checker *lessOrEqualThanChecker) Check(params []interface{}, names []string) (bool, string) {
|
|
| 271 |
+ return lessOrEqualThan(params[0], params[1]) |
|
| 272 |
+} |
|
| 273 |
+ |
|
| 274 |
+func lessOrEqualThan(obtained, expected interface{}) (bool, string) {
|
|
| 275 |
+ if _, ok := obtained.(time.Time); ok {
|
|
| 276 |
+ return isBefore(obtained, expected) |
|
| 277 |
+ } |
|
| 278 |
+ if reflect.TypeOf(obtained) != reflect.TypeOf(expected) {
|
|
| 279 |
+ return false, "obtained value and expected value have not the same type." |
|
| 280 |
+ } |
|
| 281 |
+ switch v := obtained.(type) {
|
|
| 282 |
+ case float32: |
|
| 283 |
+ return v <= expected.(float32), "" |
|
| 284 |
+ case float64: |
|
| 285 |
+ return v <= expected.(float64), "" |
|
| 286 |
+ case int: |
|
| 287 |
+ return v <= expected.(int), "" |
|
| 288 |
+ case int8: |
|
| 289 |
+ return v <= expected.(int8), "" |
|
| 290 |
+ case int16: |
|
| 291 |
+ return v <= expected.(int16), "" |
|
| 292 |
+ case int32: |
|
| 293 |
+ return v <= expected.(int32), "" |
|
| 294 |
+ case int64: |
|
| 295 |
+ return v <= expected.(int64), "" |
|
| 296 |
+ case uint: |
|
| 297 |
+ return v <= expected.(uint), "" |
|
| 298 |
+ case uint8: |
|
| 299 |
+ return v <= expected.(uint8), "" |
|
| 300 |
+ case uint16: |
|
| 301 |
+ return v <= expected.(uint16), "" |
|
| 302 |
+ case uint32: |
|
| 303 |
+ return v <= expected.(uint32), "" |
|
| 304 |
+ case uint64: |
|
| 305 |
+ return v <= expected.(uint64), "" |
|
| 306 |
+ default: |
|
| 307 |
+ return false, "obtained value type not supported." |
|
| 308 |
+ } |
|
| 309 |
+} |
| 0 | 4 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,168 @@ |
| 0 |
+// Package shakers provide some checker implementation the go-check.Checker interface. |
|
| 1 |
+package shakers |
|
| 2 |
+ |
|
| 3 |
+import ( |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "strings" |
|
| 6 |
+ |
|
| 7 |
+ "github.com/go-check/check" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+// Contains checker verifies that obtained value contains a substring. |
|
| 11 |
+var Contains check.Checker = &substringChecker{
|
|
| 12 |
+ &check.CheckerInfo{
|
|
| 13 |
+ Name: "Contains", |
|
| 14 |
+ Params: []string{"obtained", "substring"},
|
|
| 15 |
+ }, |
|
| 16 |
+ strings.Contains, |
|
| 17 |
+} |
|
| 18 |
+ |
|
| 19 |
+// ContainsAny checker verifies that any Unicode code points in chars |
|
| 20 |
+// are in the obtained string. |
|
| 21 |
+var ContainsAny check.Checker = &substringChecker{
|
|
| 22 |
+ &check.CheckerInfo{
|
|
| 23 |
+ Name: "ContainsAny", |
|
| 24 |
+ Params: []string{"obtained", "chars"},
|
|
| 25 |
+ }, |
|
| 26 |
+ strings.ContainsAny, |
|
| 27 |
+} |
|
| 28 |
+ |
|
| 29 |
+// HasPrefix checker verifies that obtained value has the specified substring as prefix |
|
| 30 |
+var HasPrefix check.Checker = &substringChecker{
|
|
| 31 |
+ &check.CheckerInfo{
|
|
| 32 |
+ Name: "HasPrefix", |
|
| 33 |
+ Params: []string{"obtained", "prefix"},
|
|
| 34 |
+ }, |
|
| 35 |
+ strings.HasPrefix, |
|
| 36 |
+} |
|
| 37 |
+ |
|
| 38 |
+// HasSuffix checker verifies that obtained value has the specified substring as prefix |
|
| 39 |
+var HasSuffix check.Checker = &substringChecker{
|
|
| 40 |
+ &check.CheckerInfo{
|
|
| 41 |
+ Name: "HasSuffix", |
|
| 42 |
+ Params: []string{"obtained", "suffix"},
|
|
| 43 |
+ }, |
|
| 44 |
+ strings.HasSuffix, |
|
| 45 |
+} |
|
| 46 |
+ |
|
| 47 |
+// EqualFold checker verifies that obtained value is, interpreted as UTF-8 strings, are equal under Unicode case-folding. |
|
| 48 |
+var EqualFold check.Checker = &substringChecker{
|
|
| 49 |
+ &check.CheckerInfo{
|
|
| 50 |
+ Name: "EqualFold", |
|
| 51 |
+ Params: []string{"obtained", "expected"},
|
|
| 52 |
+ }, |
|
| 53 |
+ strings.EqualFold, |
|
| 54 |
+} |
|
| 55 |
+ |
|
| 56 |
+type substringChecker struct {
|
|
| 57 |
+ *check.CheckerInfo |
|
| 58 |
+ substringFunction func(string, string) bool |
|
| 59 |
+} |
|
| 60 |
+ |
|
| 61 |
+func (checker *substringChecker) Check(params []interface{}, names []string) (bool, string) {
|
|
| 62 |
+ obtained := params[0] |
|
| 63 |
+ substring := params[1] |
|
| 64 |
+ substringStr, ok := substring.(string) |
|
| 65 |
+ if !ok {
|
|
| 66 |
+ return false, fmt.Sprintf("%s value must be a string.", names[1])
|
|
| 67 |
+ } |
|
| 68 |
+ obtainedString, obtainedIsStr := obtained.(string) |
|
| 69 |
+ if !obtainedIsStr {
|
|
| 70 |
+ if obtainedWithStringer, obtainedHasStringer := obtained.(fmt.Stringer); obtainedHasStringer {
|
|
| 71 |
+ obtainedString, obtainedIsStr = obtainedWithStringer.String(), true |
|
| 72 |
+ } |
|
| 73 |
+ } |
|
| 74 |
+ if obtainedIsStr {
|
|
| 75 |
+ return checker.substringFunction(obtainedString, substringStr), "" |
|
| 76 |
+ } |
|
| 77 |
+ return false, "obtained value is not a string and has no .String()." |
|
| 78 |
+} |
|
| 79 |
+ |
|
| 80 |
+// IndexAny checker verifies that the index of the first instance of any Unicode code point from chars in the obtained value is equal to expected |
|
| 81 |
+var IndexAny check.Checker = &substringCountChecker{
|
|
| 82 |
+ &check.CheckerInfo{
|
|
| 83 |
+ Name: "IndexAny", |
|
| 84 |
+ Params: []string{"obtained", "chars", "expected"},
|
|
| 85 |
+ }, |
|
| 86 |
+ strings.IndexAny, |
|
| 87 |
+} |
|
| 88 |
+ |
|
| 89 |
+// Index checker verifies that the index of the first instance of sep in the obtained value is equal to expected |
|
| 90 |
+var Index check.Checker = &substringCountChecker{
|
|
| 91 |
+ &check.CheckerInfo{
|
|
| 92 |
+ Name: "Index", |
|
| 93 |
+ Params: []string{"obtained", "sep", "expected"},
|
|
| 94 |
+ }, |
|
| 95 |
+ strings.Index, |
|
| 96 |
+} |
|
| 97 |
+ |
|
| 98 |
+// Count checker verifies that obtained value has the specified number of non-overlapping instances of sep |
|
| 99 |
+var Count check.Checker = &substringCountChecker{
|
|
| 100 |
+ &check.CheckerInfo{
|
|
| 101 |
+ Name: "Count", |
|
| 102 |
+ Params: []string{"obtained", "sep", "expected"},
|
|
| 103 |
+ }, |
|
| 104 |
+ strings.Count, |
|
| 105 |
+} |
|
| 106 |
+ |
|
| 107 |
+type substringCountChecker struct {
|
|
| 108 |
+ *check.CheckerInfo |
|
| 109 |
+ substringFunction func(string, string) int |
|
| 110 |
+} |
|
| 111 |
+ |
|
| 112 |
+func (checker *substringCountChecker) Check(params []interface{}, names []string) (bool, string) {
|
|
| 113 |
+ obtained := params[0] |
|
| 114 |
+ substring := params[1] |
|
| 115 |
+ expected := params[2] |
|
| 116 |
+ substringStr, ok := substring.(string) |
|
| 117 |
+ if !ok {
|
|
| 118 |
+ return false, fmt.Sprintf("%s value must be a string.", names[1])
|
|
| 119 |
+ } |
|
| 120 |
+ obtainedString, obtainedIsStr := obtained.(string) |
|
| 121 |
+ if !obtainedIsStr {
|
|
| 122 |
+ if obtainedWithStringer, obtainedHasStringer := obtained.(fmt.Stringer); obtainedHasStringer {
|
|
| 123 |
+ obtainedString, obtainedIsStr = obtainedWithStringer.String(), true |
|
| 124 |
+ } |
|
| 125 |
+ } |
|
| 126 |
+ if obtainedIsStr {
|
|
| 127 |
+ return checker.substringFunction(obtainedString, substringStr) == expected, "" |
|
| 128 |
+ } |
|
| 129 |
+ return false, "obtained value is not a string and has no .String()." |
|
| 130 |
+} |
|
| 131 |
+ |
|
| 132 |
+// IsLower checker verifies that the obtained value is in lower case |
|
| 133 |
+var IsLower check.Checker = &stringTransformChecker{
|
|
| 134 |
+ &check.CheckerInfo{
|
|
| 135 |
+ Name: "IsLower", |
|
| 136 |
+ Params: []string{"obtained"},
|
|
| 137 |
+ }, |
|
| 138 |
+ strings.ToLower, |
|
| 139 |
+} |
|
| 140 |
+ |
|
| 141 |
+// IsUpper checker verifies that the obtained value is in lower case |
|
| 142 |
+var IsUpper check.Checker = &stringTransformChecker{
|
|
| 143 |
+ &check.CheckerInfo{
|
|
| 144 |
+ Name: "IsUpper", |
|
| 145 |
+ Params: []string{"obtained"},
|
|
| 146 |
+ }, |
|
| 147 |
+ strings.ToUpper, |
|
| 148 |
+} |
|
| 149 |
+ |
|
| 150 |
+type stringTransformChecker struct {
|
|
| 151 |
+ *check.CheckerInfo |
|
| 152 |
+ stringFunction func(string) string |
|
| 153 |
+} |
|
| 154 |
+ |
|
| 155 |
+func (checker *stringTransformChecker) Check(params []interface{}, names []string) (bool, string) {
|
|
| 156 |
+ obtained := params[0] |
|
| 157 |
+ obtainedString, obtainedIsStr := obtained.(string) |
|
| 158 |
+ if !obtainedIsStr {
|
|
| 159 |
+ if obtainedWithStringer, obtainedHasStringer := obtained.(fmt.Stringer); obtainedHasStringer {
|
|
| 160 |
+ obtainedString, obtainedIsStr = obtainedWithStringer.String(), true |
|
| 161 |
+ } |
|
| 162 |
+ } |
|
| 163 |
+ if obtainedIsStr {
|
|
| 164 |
+ return checker.stringFunction(obtainedString) == obtainedString, "" |
|
| 165 |
+ } |
|
| 166 |
+ return false, "obtained value is not a string and has no .String()." |
|
| 167 |
+} |
| 0 | 168 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,234 @@ |
| 0 |
+package shakers |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "time" |
|
| 5 |
+ |
|
| 6 |
+ "github.com/go-check/check" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+// Default format when parsing (in addition to RFC and default time formats..) |
|
| 10 |
+const shortForm = "2006-01-02" |
|
| 11 |
+ |
|
| 12 |
+// IsBefore checker verifies the specified value is before the specified time. |
|
| 13 |
+// It is exclusive. |
|
| 14 |
+// |
|
| 15 |
+// c.Assert(myTime, IsBefore, theTime, check.Commentf("bouuuhhh"))
|
|
| 16 |
+// |
|
| 17 |
+var IsBefore check.Checker = &isBeforeChecker{
|
|
| 18 |
+ &check.CheckerInfo{
|
|
| 19 |
+ Name: "IsBefore", |
|
| 20 |
+ Params: []string{"obtained", "expected"},
|
|
| 21 |
+ }, |
|
| 22 |
+} |
|
| 23 |
+ |
|
| 24 |
+type isBeforeChecker struct {
|
|
| 25 |
+ *check.CheckerInfo |
|
| 26 |
+} |
|
| 27 |
+ |
|
| 28 |
+func (checker *isBeforeChecker) Check(params []interface{}, names []string) (bool, string) {
|
|
| 29 |
+ return isBefore(params[0], params[1]) |
|
| 30 |
+} |
|
| 31 |
+ |
|
| 32 |
+func isBefore(value, t interface{}) (bool, string) {
|
|
| 33 |
+ tTime, ok := parseTime(t) |
|
| 34 |
+ if !ok {
|
|
| 35 |
+ return false, "expected must be a Time struct, or parseable." |
|
| 36 |
+ } |
|
| 37 |
+ valueTime, valueIsTime := parseTime(value) |
|
| 38 |
+ if valueIsTime {
|
|
| 39 |
+ return valueTime.Before(tTime), "" |
|
| 40 |
+ } |
|
| 41 |
+ return false, "obtained value is not a time.Time struct or parseable as a time." |
|
| 42 |
+} |
|
| 43 |
+ |
|
| 44 |
+// IsAfter checker verifies the specified value is before the specified time. |
|
| 45 |
+// It is exclusive. |
|
| 46 |
+// |
|
| 47 |
+// c.Assert(myTime, IsAfter, theTime, check.Commentf("bouuuhhh"))
|
|
| 48 |
+// |
|
| 49 |
+var IsAfter check.Checker = &isAfterChecker{
|
|
| 50 |
+ &check.CheckerInfo{
|
|
| 51 |
+ Name: "IsAfter", |
|
| 52 |
+ Params: []string{"obtained", "expected"},
|
|
| 53 |
+ }, |
|
| 54 |
+} |
|
| 55 |
+ |
|
| 56 |
+type isAfterChecker struct {
|
|
| 57 |
+ *check.CheckerInfo |
|
| 58 |
+} |
|
| 59 |
+ |
|
| 60 |
+func (checker *isAfterChecker) Check(params []interface{}, names []string) (bool, string) {
|
|
| 61 |
+ return isAfter(params[0], params[1]) |
|
| 62 |
+} |
|
| 63 |
+ |
|
| 64 |
+func isAfter(value, t interface{}) (bool, string) {
|
|
| 65 |
+ tTime, ok := parseTime(t) |
|
| 66 |
+ if !ok {
|
|
| 67 |
+ return false, "expected must be a Time struct, or parseable." |
|
| 68 |
+ } |
|
| 69 |
+ valueTime, valueIsTime := parseTime(value) |
|
| 70 |
+ if valueIsTime {
|
|
| 71 |
+ return valueTime.After(tTime), "" |
|
| 72 |
+ } |
|
| 73 |
+ return false, "obtained value is not a time.Time struct or parseable as a time." |
|
| 74 |
+} |
|
| 75 |
+ |
|
| 76 |
+// IsBetween checker verifies the specified time is between the specified start |
|
| 77 |
+// and end. It's exclusive so if the specified time is at the tip of the interval. |
|
| 78 |
+// |
|
| 79 |
+// c.Assert(myTime, IsBetween, startTime, endTime, check.Commentf("bouuuhhh"))
|
|
| 80 |
+// |
|
| 81 |
+var IsBetween check.Checker = &isBetweenChecker{
|
|
| 82 |
+ &check.CheckerInfo{
|
|
| 83 |
+ Name: "IsBetween", |
|
| 84 |
+ Params: []string{"obtained", "start", "end"},
|
|
| 85 |
+ }, |
|
| 86 |
+} |
|
| 87 |
+ |
|
| 88 |
+type isBetweenChecker struct {
|
|
| 89 |
+ *check.CheckerInfo |
|
| 90 |
+} |
|
| 91 |
+ |
|
| 92 |
+func (checker *isBetweenChecker) Check(params []interface{}, names []string) (bool, string) {
|
|
| 93 |
+ return isBetween(params[0], params[1], params[2]) |
|
| 94 |
+} |
|
| 95 |
+ |
|
| 96 |
+func isBetween(value, start, end interface{}) (bool, string) {
|
|
| 97 |
+ startTime, ok := parseTime(start) |
|
| 98 |
+ if !ok {
|
|
| 99 |
+ return false, "start must be a Time struct, or parseable." |
|
| 100 |
+ } |
|
| 101 |
+ endTime, ok := parseTime(end) |
|
| 102 |
+ if !ok {
|
|
| 103 |
+ return false, "end must be a Time struct, or parseable." |
|
| 104 |
+ } |
|
| 105 |
+ valueTime, valueIsTime := parseTime(value) |
|
| 106 |
+ if valueIsTime {
|
|
| 107 |
+ return valueTime.After(startTime) && valueTime.Before(endTime), "" |
|
| 108 |
+ } |
|
| 109 |
+ return false, "obtained value is not a time.Time struct or parseable as a time." |
|
| 110 |
+} |
|
| 111 |
+ |
|
| 112 |
+// TimeEquals checker verifies the specified time is the equal to the expected |
|
| 113 |
+// time. |
|
| 114 |
+// |
|
| 115 |
+// c.Assert(myTime, TimeEquals, expected, check.Commentf("bouhhh"))
|
|
| 116 |
+// |
|
| 117 |
+// It's possible to ignore some part of the time (like hours, minutes, etc..) using |
|
| 118 |
+// the TimeIgnore checker with it. |
|
| 119 |
+// |
|
| 120 |
+// c.Assert(myTime, TimeIgnore(TimeEquals, time.Hour), expected, check.Commentf("... bouh.."))
|
|
| 121 |
+// |
|
| 122 |
+var TimeEquals check.Checker = &timeEqualsChecker{
|
|
| 123 |
+ &check.CheckerInfo{
|
|
| 124 |
+ Name: "TimeEquals", |
|
| 125 |
+ Params: []string{"obtained", "expected"},
|
|
| 126 |
+ }, |
|
| 127 |
+} |
|
| 128 |
+ |
|
| 129 |
+type timeEqualsChecker struct {
|
|
| 130 |
+ *check.CheckerInfo |
|
| 131 |
+} |
|
| 132 |
+ |
|
| 133 |
+func (checker *timeEqualsChecker) Check(params []interface{}, names []string) (bool, string) {
|
|
| 134 |
+ return timeEquals(params[0], params[1]) |
|
| 135 |
+} |
|
| 136 |
+ |
|
| 137 |
+func timeEquals(obtained, expected interface{}) (bool, string) {
|
|
| 138 |
+ expectedTime, ok := parseTime(expected) |
|
| 139 |
+ if !ok {
|
|
| 140 |
+ return false, "expected must be a Time struct, or parseable." |
|
| 141 |
+ } |
|
| 142 |
+ valueTime, valueIsTime := parseTime(obtained) |
|
| 143 |
+ if valueIsTime {
|
|
| 144 |
+ return valueTime.Equal(expectedTime), "" |
|
| 145 |
+ } |
|
| 146 |
+ return false, "obtained value is not a time.Time struct or parseable as a time." |
|
| 147 |
+} |
|
| 148 |
+ |
|
| 149 |
+// TimeIgnore checker will ignore some part of the time on the encapsulated checker. |
|
| 150 |
+// |
|
| 151 |
+// c.Assert(myTime, TimeIgnore(IsBetween, time.Second), start, end) |
|
| 152 |
+// |
|
| 153 |
+// FIXME use interface{} for ignore (to enable "Month", ..
|
|
| 154 |
+func TimeIgnore(checker check.Checker, ignore time.Duration) check.Checker {
|
|
| 155 |
+ return &timeIgnoreChecker{
|
|
| 156 |
+ sub: checker, |
|
| 157 |
+ ignore: ignore, |
|
| 158 |
+ } |
|
| 159 |
+} |
|
| 160 |
+ |
|
| 161 |
+type timeIgnoreChecker struct {
|
|
| 162 |
+ sub check.Checker |
|
| 163 |
+ ignore time.Duration |
|
| 164 |
+} |
|
| 165 |
+ |
|
| 166 |
+func (checker *timeIgnoreChecker) Info() *check.CheckerInfo {
|
|
| 167 |
+ info := *checker.sub.Info() |
|
| 168 |
+ info.Name = fmt.Sprintf("TimeIgnore(%s, %v)", info.Name, checker.ignore)
|
|
| 169 |
+ return &info |
|
| 170 |
+} |
|
| 171 |
+ |
|
| 172 |
+func (checker *timeIgnoreChecker) Check(params []interface{}, names []string) (bool, string) {
|
|
| 173 |
+ // Naive implementation : all params are supposed to be date |
|
| 174 |
+ mParams := make([]interface{}, len(params))
|
|
| 175 |
+ for index, param := range params {
|
|
| 176 |
+ paramTime, ok := parseTime(param) |
|
| 177 |
+ if !ok {
|
|
| 178 |
+ return false, fmt.Sprintf("%s must be a Time struct, or parseable.", names[index])
|
|
| 179 |
+ } |
|
| 180 |
+ year := paramTime.Year() |
|
| 181 |
+ month := paramTime.Month() |
|
| 182 |
+ day := paramTime.Day() |
|
| 183 |
+ hour := paramTime.Hour() |
|
| 184 |
+ min := paramTime.Minute() |
|
| 185 |
+ sec := paramTime.Second() |
|
| 186 |
+ nsec := paramTime.Nanosecond() |
|
| 187 |
+ location := paramTime.Location() |
|
| 188 |
+ switch checker.ignore {
|
|
| 189 |
+ case time.Hour: |
|
| 190 |
+ hour = 0 |
|
| 191 |
+ fallthrough |
|
| 192 |
+ case time.Minute: |
|
| 193 |
+ min = 0 |
|
| 194 |
+ fallthrough |
|
| 195 |
+ case time.Second: |
|
| 196 |
+ sec = 0 |
|
| 197 |
+ fallthrough |
|
| 198 |
+ case time.Millisecond: |
|
| 199 |
+ fallthrough |
|
| 200 |
+ case time.Microsecond: |
|
| 201 |
+ fallthrough |
|
| 202 |
+ case time.Nanosecond: |
|
| 203 |
+ nsec = 0 |
|
| 204 |
+ } |
|
| 205 |
+ mParams[index] = time.Date(year, month, day, hour, min, sec, nsec, location) |
|
| 206 |
+ } |
|
| 207 |
+ return checker.sub.Check(mParams, names) |
|
| 208 |
+} |
|
| 209 |
+ |
|
| 210 |
+func parseTime(datetime interface{}) (time.Time, bool) {
|
|
| 211 |
+ switch datetime.(type) {
|
|
| 212 |
+ case time.Time: |
|
| 213 |
+ return datetime.(time.Time), true |
|
| 214 |
+ case string: |
|
| 215 |
+ return parseTimeAsString(datetime.(string)) |
|
| 216 |
+ default: |
|
| 217 |
+ if datetimeWithStr, ok := datetime.(fmt.Stringer); ok {
|
|
| 218 |
+ return parseTimeAsString(datetimeWithStr.String()) |
|
| 219 |
+ } |
|
| 220 |
+ return time.Time{}, false
|
|
| 221 |
+ } |
|
| 222 |
+} |
|
| 223 |
+ |
|
| 224 |
+func parseTimeAsString(timeAsStr string) (time.Time, bool) {
|
|
| 225 |
+ forms := []string{shortForm, time.RFC3339, time.RFC3339Nano, time.RFC822, time.RFC822Z}
|
|
| 226 |
+ for _, form := range forms {
|
|
| 227 |
+ datetime, err := time.Parse(form, timeAsStr) |
|
| 228 |
+ if err == nil {
|
|
| 229 |
+ return datetime, true |
|
| 230 |
+ } |
|
| 231 |
+ } |
|
| 232 |
+ return time.Time{}, false
|
|
| 233 |
+} |