Signed-off-by: Alexander Morozov <lk4d4@docker.com>
| ... | ... |
@@ -57,6 +57,8 @@ clone git github.com/Sirupsen/logrus v0.7.2 |
| 57 | 57 |
|
| 58 | 58 |
clone git github.com/go-fsnotify/fsnotify v1.0.4 |
| 59 | 59 |
|
| 60 |
+clone git github.com/go-check/check 64131543e7896d5bcc6bd5a76287eb75ea96c673 |
|
| 61 |
+ |
|
| 60 | 62 |
# get Go tip's archive/tar, for xattr support and improved performance |
| 61 | 63 |
# TODO after Go 1.4 drops, bump our minimum supported version and drop this vendored dep |
| 62 | 64 |
if [ "$1" = '--go' ]; then |
| 0 | 4 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,25 @@ |
| 0 |
+Gocheck - A rich testing framework for Go |
|
| 1 |
+ |
|
| 2 |
+Copyright (c) 2010-2013 Gustavo Niemeyer <gustavo@niemeyer.net> |
|
| 3 |
+ |
|
| 4 |
+All rights reserved. |
|
| 5 |
+ |
|
| 6 |
+Redistribution and use in source and binary forms, with or without |
|
| 7 |
+modification, are permitted provided that the following conditions are met: |
|
| 8 |
+ |
|
| 9 |
+1. Redistributions of source code must retain the above copyright notice, this |
|
| 10 |
+ list of conditions and the following disclaimer. |
|
| 11 |
+2. Redistributions in binary form must reproduce the above copyright notice, |
|
| 12 |
+ this list of conditions and the following disclaimer in the documentation |
|
| 13 |
+ and/or other materials provided with the distribution. |
|
| 14 |
+ |
|
| 15 |
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
|
| 16 |
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
| 17 |
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
| 18 |
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR |
|
| 19 |
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
|
| 20 |
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|
| 21 |
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
|
| 22 |
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
| 23 |
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|
| 24 |
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 0 | 25 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,20 @@ |
| 0 |
+Instructions |
|
| 1 |
+============ |
|
| 2 |
+ |
|
| 3 |
+Install the package with: |
|
| 4 |
+ |
|
| 5 |
+ go get gopkg.in/check.v1 |
|
| 6 |
+ |
|
| 7 |
+Import it with: |
|
| 8 |
+ |
|
| 9 |
+ import "gopkg.in/check.v1" |
|
| 10 |
+ |
|
| 11 |
+and use _check_ as the package name inside the code. |
|
| 12 |
+ |
|
| 13 |
+For more details, visit the project page: |
|
| 14 |
+ |
|
| 15 |
+* http://labix.org/gocheck |
|
| 16 |
+ |
|
| 17 |
+and the API documentation: |
|
| 18 |
+ |
|
| 19 |
+* https://gopkg.in/check.v1 |
| 0 | 2 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,163 @@ |
| 0 |
+// Copyright 2009 The Go Authors. All rights reserved. |
|
| 1 |
+// Use of this source code is governed by a BSD-style |
|
| 2 |
+// license that can be found in the LICENSE file. |
|
| 3 |
+ |
|
| 4 |
+package check |
|
| 5 |
+ |
|
| 6 |
+import ( |
|
| 7 |
+ "fmt" |
|
| 8 |
+ "runtime" |
|
| 9 |
+ "time" |
|
| 10 |
+) |
|
| 11 |
+ |
|
| 12 |
+var memStats runtime.MemStats |
|
| 13 |
+ |
|
| 14 |
+// testingB is a type passed to Benchmark functions to manage benchmark |
|
| 15 |
+// timing and to specify the number of iterations to run. |
|
| 16 |
+type timer struct {
|
|
| 17 |
+ start time.Time // Time test or benchmark started |
|
| 18 |
+ duration time.Duration |
|
| 19 |
+ N int |
|
| 20 |
+ bytes int64 |
|
| 21 |
+ timerOn bool |
|
| 22 |
+ benchTime time.Duration |
|
| 23 |
+ // The initial states of memStats.Mallocs and memStats.TotalAlloc. |
|
| 24 |
+ startAllocs uint64 |
|
| 25 |
+ startBytes uint64 |
|
| 26 |
+ // The net total of this test after being run. |
|
| 27 |
+ netAllocs uint64 |
|
| 28 |
+ netBytes uint64 |
|
| 29 |
+} |
|
| 30 |
+ |
|
| 31 |
+// StartTimer starts timing a test. This function is called automatically |
|
| 32 |
+// before a benchmark starts, but it can also used to resume timing after |
|
| 33 |
+// a call to StopTimer. |
|
| 34 |
+func (c *C) StartTimer() {
|
|
| 35 |
+ if !c.timerOn {
|
|
| 36 |
+ c.start = time.Now() |
|
| 37 |
+ c.timerOn = true |
|
| 38 |
+ |
|
| 39 |
+ runtime.ReadMemStats(&memStats) |
|
| 40 |
+ c.startAllocs = memStats.Mallocs |
|
| 41 |
+ c.startBytes = memStats.TotalAlloc |
|
| 42 |
+ } |
|
| 43 |
+} |
|
| 44 |
+ |
|
| 45 |
+// StopTimer stops timing a test. This can be used to pause the timer |
|
| 46 |
+// while performing complex initialization that you don't |
|
| 47 |
+// want to measure. |
|
| 48 |
+func (c *C) StopTimer() {
|
|
| 49 |
+ if c.timerOn {
|
|
| 50 |
+ c.duration += time.Now().Sub(c.start) |
|
| 51 |
+ c.timerOn = false |
|
| 52 |
+ runtime.ReadMemStats(&memStats) |
|
| 53 |
+ c.netAllocs += memStats.Mallocs - c.startAllocs |
|
| 54 |
+ c.netBytes += memStats.TotalAlloc - c.startBytes |
|
| 55 |
+ } |
|
| 56 |
+} |
|
| 57 |
+ |
|
| 58 |
+// ResetTimer sets the elapsed benchmark time to zero. |
|
| 59 |
+// It does not affect whether the timer is running. |
|
| 60 |
+func (c *C) ResetTimer() {
|
|
| 61 |
+ if c.timerOn {
|
|
| 62 |
+ c.start = time.Now() |
|
| 63 |
+ runtime.ReadMemStats(&memStats) |
|
| 64 |
+ c.startAllocs = memStats.Mallocs |
|
| 65 |
+ c.startBytes = memStats.TotalAlloc |
|
| 66 |
+ } |
|
| 67 |
+ c.duration = 0 |
|
| 68 |
+ c.netAllocs = 0 |
|
| 69 |
+ c.netBytes = 0 |
|
| 70 |
+} |
|
| 71 |
+ |
|
| 72 |
+// SetBytes informs the number of bytes that the benchmark processes |
|
| 73 |
+// on each iteration. If this is called in a benchmark it will also |
|
| 74 |
+// report MB/s. |
|
| 75 |
+func (c *C) SetBytes(n int64) {
|
|
| 76 |
+ c.bytes = n |
|
| 77 |
+} |
|
| 78 |
+ |
|
| 79 |
+func (c *C) nsPerOp() int64 {
|
|
| 80 |
+ if c.N <= 0 {
|
|
| 81 |
+ return 0 |
|
| 82 |
+ } |
|
| 83 |
+ return c.duration.Nanoseconds() / int64(c.N) |
|
| 84 |
+} |
|
| 85 |
+ |
|
| 86 |
+func (c *C) mbPerSec() float64 {
|
|
| 87 |
+ if c.bytes <= 0 || c.duration <= 0 || c.N <= 0 {
|
|
| 88 |
+ return 0 |
|
| 89 |
+ } |
|
| 90 |
+ return (float64(c.bytes) * float64(c.N) / 1e6) / c.duration.Seconds() |
|
| 91 |
+} |
|
| 92 |
+ |
|
| 93 |
+func (c *C) timerString() string {
|
|
| 94 |
+ if c.N <= 0 {
|
|
| 95 |
+ return fmt.Sprintf("%3.3fs", float64(c.duration.Nanoseconds())/1e9)
|
|
| 96 |
+ } |
|
| 97 |
+ mbs := c.mbPerSec() |
|
| 98 |
+ mb := "" |
|
| 99 |
+ if mbs != 0 {
|
|
| 100 |
+ mb = fmt.Sprintf("\t%7.2f MB/s", mbs)
|
|
| 101 |
+ } |
|
| 102 |
+ nsop := c.nsPerOp() |
|
| 103 |
+ ns := fmt.Sprintf("%10d ns/op", nsop)
|
|
| 104 |
+ if c.N > 0 && nsop < 100 {
|
|
| 105 |
+ // The format specifiers here make sure that |
|
| 106 |
+ // the ones digits line up for all three possible formats. |
|
| 107 |
+ if nsop < 10 {
|
|
| 108 |
+ ns = fmt.Sprintf("%13.2f ns/op", float64(c.duration.Nanoseconds())/float64(c.N))
|
|
| 109 |
+ } else {
|
|
| 110 |
+ ns = fmt.Sprintf("%12.1f ns/op", float64(c.duration.Nanoseconds())/float64(c.N))
|
|
| 111 |
+ } |
|
| 112 |
+ } |
|
| 113 |
+ memStats := "" |
|
| 114 |
+ if c.benchMem {
|
|
| 115 |
+ allocedBytes := fmt.Sprintf("%8d B/op", int64(c.netBytes)/int64(c.N))
|
|
| 116 |
+ allocs := fmt.Sprintf("%8d allocs/op", int64(c.netAllocs)/int64(c.N))
|
|
| 117 |
+ memStats = fmt.Sprintf("\t%s\t%s", allocedBytes, allocs)
|
|
| 118 |
+ } |
|
| 119 |
+ return fmt.Sprintf("%8d\t%s%s%s", c.N, ns, mb, memStats)
|
|
| 120 |
+} |
|
| 121 |
+ |
|
| 122 |
+func min(x, y int) int {
|
|
| 123 |
+ if x > y {
|
|
| 124 |
+ return y |
|
| 125 |
+ } |
|
| 126 |
+ return x |
|
| 127 |
+} |
|
| 128 |
+ |
|
| 129 |
+func max(x, y int) int {
|
|
| 130 |
+ if x < y {
|
|
| 131 |
+ return y |
|
| 132 |
+ } |
|
| 133 |
+ return x |
|
| 134 |
+} |
|
| 135 |
+ |
|
| 136 |
+// roundDown10 rounds a number down to the nearest power of 10. |
|
| 137 |
+func roundDown10(n int) int {
|
|
| 138 |
+ var tens = 0 |
|
| 139 |
+ // tens = floor(log_10(n)) |
|
| 140 |
+ for n > 10 {
|
|
| 141 |
+ n = n / 10 |
|
| 142 |
+ tens++ |
|
| 143 |
+ } |
|
| 144 |
+ // result = 10^tens |
|
| 145 |
+ result := 1 |
|
| 146 |
+ for i := 0; i < tens; i++ {
|
|
| 147 |
+ result *= 10 |
|
| 148 |
+ } |
|
| 149 |
+ return result |
|
| 150 |
+} |
|
| 151 |
+ |
|
| 152 |
+// roundUp rounds x up to a number of the form [1eX, 2eX, 5eX]. |
|
| 153 |
+func roundUp(n int) int {
|
|
| 154 |
+ base := roundDown10(n) |
|
| 155 |
+ if n < (2 * base) {
|
|
| 156 |
+ return 2 * base |
|
| 157 |
+ } |
|
| 158 |
+ if n < (5 * base) {
|
|
| 159 |
+ return 5 * base |
|
| 160 |
+ } |
|
| 161 |
+ return 10 * base |
|
| 162 |
+} |
| 0 | 163 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,91 @@ |
| 0 |
+// These tests verify the test running logic. |
|
| 1 |
+ |
|
| 2 |
+package check_test |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "time" |
|
| 6 |
+ . "gopkg.in/check.v1" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+var benchmarkS = Suite(&BenchmarkS{})
|
|
| 10 |
+ |
|
| 11 |
+type BenchmarkS struct{}
|
|
| 12 |
+ |
|
| 13 |
+func (s *BenchmarkS) TestCountSuite(c *C) {
|
|
| 14 |
+ suitesRun += 1 |
|
| 15 |
+} |
|
| 16 |
+ |
|
| 17 |
+func (s *BenchmarkS) TestBasicTestTiming(c *C) {
|
|
| 18 |
+ helper := FixtureHelper{sleepOn: "Test1", sleep: 1000000 * time.Nanosecond}
|
|
| 19 |
+ output := String{}
|
|
| 20 |
+ runConf := RunConf{Output: &output, Verbose: true}
|
|
| 21 |
+ Run(&helper, &runConf) |
|
| 22 |
+ |
|
| 23 |
+ expected := "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Test1\t0\\.001s\n" + |
|
| 24 |
+ "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Test2\t0\\.000s\n" |
|
| 25 |
+ c.Assert(output.value, Matches, expected) |
|
| 26 |
+} |
|
| 27 |
+ |
|
| 28 |
+func (s *BenchmarkS) TestStreamTestTiming(c *C) {
|
|
| 29 |
+ helper := FixtureHelper{sleepOn: "SetUpSuite", sleep: 1000000 * time.Nanosecond}
|
|
| 30 |
+ output := String{}
|
|
| 31 |
+ runConf := RunConf{Output: &output, Stream: true}
|
|
| 32 |
+ Run(&helper, &runConf) |
|
| 33 |
+ |
|
| 34 |
+ expected := "(?s).*\nPASS: check_test\\.go:[0-9]+: FixtureHelper\\.SetUpSuite\t *0\\.001s\n.*" |
|
| 35 |
+ c.Assert(output.value, Matches, expected) |
|
| 36 |
+} |
|
| 37 |
+ |
|
| 38 |
+func (s *BenchmarkS) TestBenchmark(c *C) {
|
|
| 39 |
+ helper := FixtureHelper{sleep: 100000}
|
|
| 40 |
+ output := String{}
|
|
| 41 |
+ runConf := RunConf{
|
|
| 42 |
+ Output: &output, |
|
| 43 |
+ Benchmark: true, |
|
| 44 |
+ BenchmarkTime: 10000000, |
|
| 45 |
+ Filter: "Benchmark1", |
|
| 46 |
+ } |
|
| 47 |
+ Run(&helper, &runConf) |
|
| 48 |
+ c.Check(helper.calls[0], Equals, "SetUpSuite") |
|
| 49 |
+ c.Check(helper.calls[1], Equals, "SetUpTest") |
|
| 50 |
+ c.Check(helper.calls[2], Equals, "Benchmark1") |
|
| 51 |
+ c.Check(helper.calls[3], Equals, "TearDownTest") |
|
| 52 |
+ c.Check(helper.calls[4], Equals, "SetUpTest") |
|
| 53 |
+ c.Check(helper.calls[5], Equals, "Benchmark1") |
|
| 54 |
+ c.Check(helper.calls[6], Equals, "TearDownTest") |
|
| 55 |
+ // ... and more. |
|
| 56 |
+ |
|
| 57 |
+ expected := "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Benchmark1\t *100\t *[12][0-9]{5} ns/op\n"
|
|
| 58 |
+ c.Assert(output.value, Matches, expected) |
|
| 59 |
+} |
|
| 60 |
+ |
|
| 61 |
+func (s *BenchmarkS) TestBenchmarkBytes(c *C) {
|
|
| 62 |
+ helper := FixtureHelper{sleep: 100000}
|
|
| 63 |
+ output := String{}
|
|
| 64 |
+ runConf := RunConf{
|
|
| 65 |
+ Output: &output, |
|
| 66 |
+ Benchmark: true, |
|
| 67 |
+ BenchmarkTime: 10000000, |
|
| 68 |
+ Filter: "Benchmark2", |
|
| 69 |
+ } |
|
| 70 |
+ Run(&helper, &runConf) |
|
| 71 |
+ |
|
| 72 |
+ expected := "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Benchmark2\t *100\t *[12][0-9]{5} ns/op\t *[4-9]\\.[0-9]{2} MB/s\n"
|
|
| 73 |
+ c.Assert(output.value, Matches, expected) |
|
| 74 |
+} |
|
| 75 |
+ |
|
| 76 |
+func (s *BenchmarkS) TestBenchmarkMem(c *C) {
|
|
| 77 |
+ helper := FixtureHelper{sleep: 100000}
|
|
| 78 |
+ output := String{}
|
|
| 79 |
+ runConf := RunConf{
|
|
| 80 |
+ Output: &output, |
|
| 81 |
+ Benchmark: true, |
|
| 82 |
+ BenchmarkMem: true, |
|
| 83 |
+ BenchmarkTime: 10000000, |
|
| 84 |
+ Filter: "Benchmark3", |
|
| 85 |
+ } |
|
| 86 |
+ Run(&helper, &runConf) |
|
| 87 |
+ |
|
| 88 |
+ expected := "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Benchmark3\t *100\t *[12][0-9]{5} ns/op\t *[0-9]+ B/op\t *[1-9] allocs/op\n"
|
|
| 89 |
+ c.Assert(output.value, Matches, expected) |
|
| 90 |
+} |
| 0 | 91 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,82 @@ |
| 0 |
+// These initial tests are for bootstrapping. They verify that we can |
|
| 1 |
+// basically use the testing infrastructure itself to check if the test |
|
| 2 |
+// system is working. |
|
| 3 |
+// |
|
| 4 |
+// These tests use will break down the test runner badly in case of |
|
| 5 |
+// errors because if they simply fail, we can't be sure the developer |
|
| 6 |
+// will ever see anything (because failing means the failing system |
|
| 7 |
+// somehow isn't working! :-) |
|
| 8 |
+// |
|
| 9 |
+// Do not assume *any* internal functionality works as expected besides |
|
| 10 |
+// what's actually tested here. |
|
| 11 |
+ |
|
| 12 |
+package check_test |
|
| 13 |
+ |
|
| 14 |
+import ( |
|
| 15 |
+ "fmt" |
|
| 16 |
+ "gopkg.in/check.v1" |
|
| 17 |
+ "strings" |
|
| 18 |
+) |
|
| 19 |
+ |
|
| 20 |
+type BootstrapS struct{}
|
|
| 21 |
+ |
|
| 22 |
+var boostrapS = check.Suite(&BootstrapS{})
|
|
| 23 |
+ |
|
| 24 |
+func (s *BootstrapS) TestCountSuite(c *check.C) {
|
|
| 25 |
+ suitesRun += 1 |
|
| 26 |
+} |
|
| 27 |
+ |
|
| 28 |
+func (s *BootstrapS) TestFailedAndFail(c *check.C) {
|
|
| 29 |
+ if c.Failed() {
|
|
| 30 |
+ critical("c.Failed() must be false first!")
|
|
| 31 |
+ } |
|
| 32 |
+ c.Fail() |
|
| 33 |
+ if !c.Failed() {
|
|
| 34 |
+ critical("c.Fail() didn't put the test in a failed state!")
|
|
| 35 |
+ } |
|
| 36 |
+ c.Succeed() |
|
| 37 |
+} |
|
| 38 |
+ |
|
| 39 |
+func (s *BootstrapS) TestFailedAndSucceed(c *check.C) {
|
|
| 40 |
+ c.Fail() |
|
| 41 |
+ c.Succeed() |
|
| 42 |
+ if c.Failed() {
|
|
| 43 |
+ critical("c.Succeed() didn't put the test back in a non-failed state")
|
|
| 44 |
+ } |
|
| 45 |
+} |
|
| 46 |
+ |
|
| 47 |
+func (s *BootstrapS) TestLogAndGetTestLog(c *check.C) {
|
|
| 48 |
+ c.Log("Hello there!")
|
|
| 49 |
+ log := c.GetTestLog() |
|
| 50 |
+ if log != "Hello there!\n" {
|
|
| 51 |
+ critical(fmt.Sprintf("Log() or GetTestLog() is not working! Got: %#v", log))
|
|
| 52 |
+ } |
|
| 53 |
+} |
|
| 54 |
+ |
|
| 55 |
+func (s *BootstrapS) TestLogfAndGetTestLog(c *check.C) {
|
|
| 56 |
+ c.Logf("Hello %v", "there!")
|
|
| 57 |
+ log := c.GetTestLog() |
|
| 58 |
+ if log != "Hello there!\n" {
|
|
| 59 |
+ critical(fmt.Sprintf("Logf() or GetTestLog() is not working! Got: %#v", log))
|
|
| 60 |
+ } |
|
| 61 |
+} |
|
| 62 |
+ |
|
| 63 |
+func (s *BootstrapS) TestRunShowsErrors(c *check.C) {
|
|
| 64 |
+ output := String{}
|
|
| 65 |
+ check.Run(&FailHelper{}, &check.RunConf{Output: &output})
|
|
| 66 |
+ if strings.Index(output.value, "Expected failure!") == -1 {
|
|
| 67 |
+ critical(fmt.Sprintf("RunWithWriter() output did not contain the "+
|
|
| 68 |
+ "expected failure! Got: %#v", |
|
| 69 |
+ output.value)) |
|
| 70 |
+ } |
|
| 71 |
+} |
|
| 72 |
+ |
|
| 73 |
+func (s *BootstrapS) TestRunDoesntShowSuccesses(c *check.C) {
|
|
| 74 |
+ output := String{}
|
|
| 75 |
+ check.Run(&SuccessHelper{}, &check.RunConf{Output: &output})
|
|
| 76 |
+ if strings.Index(output.value, "Expected success!") != -1 {
|
|
| 77 |
+ critical(fmt.Sprintf("RunWithWriter() output contained a successful "+
|
|
| 78 |
+ "test! Got: %#v", |
|
| 79 |
+ output.value)) |
|
| 80 |
+ } |
|
| 81 |
+} |
| 0 | 82 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,945 @@ |
| 0 |
+// Package check is a rich testing extension for Go's testing package. |
|
| 1 |
+// |
|
| 2 |
+// For details about the project, see: |
|
| 3 |
+// |
|
| 4 |
+// http://labix.org/gocheck |
|
| 5 |
+// |
|
| 6 |
+package check |
|
| 7 |
+ |
|
| 8 |
+import ( |
|
| 9 |
+ "bytes" |
|
| 10 |
+ "errors" |
|
| 11 |
+ "fmt" |
|
| 12 |
+ "io" |
|
| 13 |
+ "math/rand" |
|
| 14 |
+ "os" |
|
| 15 |
+ "path" |
|
| 16 |
+ "path/filepath" |
|
| 17 |
+ "reflect" |
|
| 18 |
+ "regexp" |
|
| 19 |
+ "runtime" |
|
| 20 |
+ "strconv" |
|
| 21 |
+ "strings" |
|
| 22 |
+ "sync" |
|
| 23 |
+ "time" |
|
| 24 |
+) |
|
| 25 |
+ |
|
| 26 |
+// ----------------------------------------------------------------------- |
|
| 27 |
+// Internal type which deals with suite method calling. |
|
| 28 |
+ |
|
| 29 |
+const ( |
|
| 30 |
+ fixtureKd = iota |
|
| 31 |
+ testKd |
|
| 32 |
+) |
|
| 33 |
+ |
|
| 34 |
+type funcKind int |
|
| 35 |
+ |
|
| 36 |
+const ( |
|
| 37 |
+ succeededSt = iota |
|
| 38 |
+ failedSt |
|
| 39 |
+ skippedSt |
|
| 40 |
+ panickedSt |
|
| 41 |
+ fixturePanickedSt |
|
| 42 |
+ missedSt |
|
| 43 |
+) |
|
| 44 |
+ |
|
| 45 |
+type funcStatus int |
|
| 46 |
+ |
|
| 47 |
+// A method value can't reach its own Method structure. |
|
| 48 |
+type methodType struct {
|
|
| 49 |
+ reflect.Value |
|
| 50 |
+ Info reflect.Method |
|
| 51 |
+} |
|
| 52 |
+ |
|
| 53 |
+func newMethod(receiver reflect.Value, i int) *methodType {
|
|
| 54 |
+ return &methodType{receiver.Method(i), receiver.Type().Method(i)}
|
|
| 55 |
+} |
|
| 56 |
+ |
|
| 57 |
+func (method *methodType) PC() uintptr {
|
|
| 58 |
+ return method.Info.Func.Pointer() |
|
| 59 |
+} |
|
| 60 |
+ |
|
| 61 |
+func (method *methodType) suiteName() string {
|
|
| 62 |
+ t := method.Info.Type.In(0) |
|
| 63 |
+ if t.Kind() == reflect.Ptr {
|
|
| 64 |
+ t = t.Elem() |
|
| 65 |
+ } |
|
| 66 |
+ return t.Name() |
|
| 67 |
+} |
|
| 68 |
+ |
|
| 69 |
+func (method *methodType) String() string {
|
|
| 70 |
+ return method.suiteName() + "." + method.Info.Name |
|
| 71 |
+} |
|
| 72 |
+ |
|
| 73 |
+func (method *methodType) matches(re *regexp.Regexp) bool {
|
|
| 74 |
+ return (re.MatchString(method.Info.Name) || |
|
| 75 |
+ re.MatchString(method.suiteName()) || |
|
| 76 |
+ re.MatchString(method.String())) |
|
| 77 |
+} |
|
| 78 |
+ |
|
| 79 |
+type C struct {
|
|
| 80 |
+ method *methodType |
|
| 81 |
+ kind funcKind |
|
| 82 |
+ testName string |
|
| 83 |
+ status funcStatus |
|
| 84 |
+ logb *logger |
|
| 85 |
+ logw io.Writer |
|
| 86 |
+ done chan *C |
|
| 87 |
+ reason string |
|
| 88 |
+ mustFail bool |
|
| 89 |
+ tempDir *tempDir |
|
| 90 |
+ benchMem bool |
|
| 91 |
+ startTime time.Time |
|
| 92 |
+ timer |
|
| 93 |
+} |
|
| 94 |
+ |
|
| 95 |
+func (c *C) stopNow() {
|
|
| 96 |
+ runtime.Goexit() |
|
| 97 |
+} |
|
| 98 |
+ |
|
| 99 |
+// logger is a concurrency safe byte.Buffer |
|
| 100 |
+type logger struct {
|
|
| 101 |
+ sync.Mutex |
|
| 102 |
+ writer bytes.Buffer |
|
| 103 |
+} |
|
| 104 |
+ |
|
| 105 |
+func (l *logger) Write(buf []byte) (int, error) {
|
|
| 106 |
+ l.Lock() |
|
| 107 |
+ defer l.Unlock() |
|
| 108 |
+ return l.writer.Write(buf) |
|
| 109 |
+} |
|
| 110 |
+ |
|
| 111 |
+func (l *logger) WriteTo(w io.Writer) (int64, error) {
|
|
| 112 |
+ l.Lock() |
|
| 113 |
+ defer l.Unlock() |
|
| 114 |
+ return l.writer.WriteTo(w) |
|
| 115 |
+} |
|
| 116 |
+ |
|
| 117 |
+func (l *logger) String() string {
|
|
| 118 |
+ l.Lock() |
|
| 119 |
+ defer l.Unlock() |
|
| 120 |
+ return l.writer.String() |
|
| 121 |
+} |
|
| 122 |
+ |
|
| 123 |
+// ----------------------------------------------------------------------- |
|
| 124 |
+// Handling of temporary files and directories. |
|
| 125 |
+ |
|
| 126 |
+type tempDir struct {
|
|
| 127 |
+ sync.Mutex |
|
| 128 |
+ path string |
|
| 129 |
+ counter int |
|
| 130 |
+} |
|
| 131 |
+ |
|
| 132 |
+func (td *tempDir) newPath() string {
|
|
| 133 |
+ td.Lock() |
|
| 134 |
+ defer td.Unlock() |
|
| 135 |
+ if td.path == "" {
|
|
| 136 |
+ var err error |
|
| 137 |
+ for i := 0; i != 100; i++ {
|
|
| 138 |
+ path := fmt.Sprintf("%s%ccheck-%d", os.TempDir(), os.PathSeparator, rand.Int())
|
|
| 139 |
+ if err = os.Mkdir(path, 0700); err == nil {
|
|
| 140 |
+ td.path = path |
|
| 141 |
+ break |
|
| 142 |
+ } |
|
| 143 |
+ } |
|
| 144 |
+ if td.path == "" {
|
|
| 145 |
+ panic("Couldn't create temporary directory: " + err.Error())
|
|
| 146 |
+ } |
|
| 147 |
+ } |
|
| 148 |
+ result := filepath.Join(td.path, strconv.Itoa(td.counter)) |
|
| 149 |
+ td.counter += 1 |
|
| 150 |
+ return result |
|
| 151 |
+} |
|
| 152 |
+ |
|
| 153 |
+func (td *tempDir) removeAll() {
|
|
| 154 |
+ td.Lock() |
|
| 155 |
+ defer td.Unlock() |
|
| 156 |
+ if td.path != "" {
|
|
| 157 |
+ err := os.RemoveAll(td.path) |
|
| 158 |
+ if err != nil {
|
|
| 159 |
+ fmt.Fprintf(os.Stderr, "WARNING: Error cleaning up temporaries: "+err.Error()) |
|
| 160 |
+ } |
|
| 161 |
+ } |
|
| 162 |
+} |
|
| 163 |
+ |
|
| 164 |
+// Create a new temporary directory which is automatically removed after |
|
| 165 |
+// the suite finishes running. |
|
| 166 |
+func (c *C) MkDir() string {
|
|
| 167 |
+ path := c.tempDir.newPath() |
|
| 168 |
+ if err := os.Mkdir(path, 0700); err != nil {
|
|
| 169 |
+ panic(fmt.Sprintf("Couldn't create temporary directory %s: %s", path, err.Error()))
|
|
| 170 |
+ } |
|
| 171 |
+ return path |
|
| 172 |
+} |
|
| 173 |
+ |
|
| 174 |
+// ----------------------------------------------------------------------- |
|
| 175 |
+// Low-level logging functions. |
|
| 176 |
+ |
|
| 177 |
+func (c *C) log(args ...interface{}) {
|
|
| 178 |
+ c.writeLog([]byte(fmt.Sprint(args...) + "\n")) |
|
| 179 |
+} |
|
| 180 |
+ |
|
| 181 |
+func (c *C) logf(format string, args ...interface{}) {
|
|
| 182 |
+ c.writeLog([]byte(fmt.Sprintf(format+"\n", args...))) |
|
| 183 |
+} |
|
| 184 |
+ |
|
| 185 |
+func (c *C) logNewLine() {
|
|
| 186 |
+ c.writeLog([]byte{'\n'})
|
|
| 187 |
+} |
|
| 188 |
+ |
|
| 189 |
+func (c *C) writeLog(buf []byte) {
|
|
| 190 |
+ c.logb.Write(buf) |
|
| 191 |
+ if c.logw != nil {
|
|
| 192 |
+ c.logw.Write(buf) |
|
| 193 |
+ } |
|
| 194 |
+} |
|
| 195 |
+ |
|
| 196 |
+func hasStringOrError(x interface{}) (ok bool) {
|
|
| 197 |
+ _, ok = x.(fmt.Stringer) |
|
| 198 |
+ if ok {
|
|
| 199 |
+ return |
|
| 200 |
+ } |
|
| 201 |
+ _, ok = x.(error) |
|
| 202 |
+ return |
|
| 203 |
+} |
|
| 204 |
+ |
|
| 205 |
+func (c *C) logValue(label string, value interface{}) {
|
|
| 206 |
+ if label == "" {
|
|
| 207 |
+ if hasStringOrError(value) {
|
|
| 208 |
+ c.logf("... %#v (%q)", value, value)
|
|
| 209 |
+ } else {
|
|
| 210 |
+ c.logf("... %#v", value)
|
|
| 211 |
+ } |
|
| 212 |
+ } else if value == nil {
|
|
| 213 |
+ c.logf("... %s = nil", label)
|
|
| 214 |
+ } else {
|
|
| 215 |
+ if hasStringOrError(value) {
|
|
| 216 |
+ fv := fmt.Sprintf("%#v", value)
|
|
| 217 |
+ qv := fmt.Sprintf("%q", value)
|
|
| 218 |
+ if fv != qv {
|
|
| 219 |
+ c.logf("... %s %s = %s (%s)", label, reflect.TypeOf(value), fv, qv)
|
|
| 220 |
+ return |
|
| 221 |
+ } |
|
| 222 |
+ } |
|
| 223 |
+ if s, ok := value.(string); ok && isMultiLine(s) {
|
|
| 224 |
+ c.logf(`... %s %s = "" +`, label, reflect.TypeOf(value)) |
|
| 225 |
+ c.logMultiLine(s) |
|
| 226 |
+ } else {
|
|
| 227 |
+ c.logf("... %s %s = %#v", label, reflect.TypeOf(value), value)
|
|
| 228 |
+ } |
|
| 229 |
+ } |
|
| 230 |
+} |
|
| 231 |
+ |
|
| 232 |
+func (c *C) logMultiLine(s string) {
|
|
| 233 |
+ b := make([]byte, 0, len(s)*2) |
|
| 234 |
+ i := 0 |
|
| 235 |
+ n := len(s) |
|
| 236 |
+ for i < n {
|
|
| 237 |
+ j := i + 1 |
|
| 238 |
+ for j < n && s[j-1] != '\n' {
|
|
| 239 |
+ j++ |
|
| 240 |
+ } |
|
| 241 |
+ b = append(b, "... "...) |
|
| 242 |
+ b = strconv.AppendQuote(b, s[i:j]) |
|
| 243 |
+ if j < n {
|
|
| 244 |
+ b = append(b, " +"...) |
|
| 245 |
+ } |
|
| 246 |
+ b = append(b, '\n') |
|
| 247 |
+ i = j |
|
| 248 |
+ } |
|
| 249 |
+ c.writeLog(b) |
|
| 250 |
+} |
|
| 251 |
+ |
|
| 252 |
+func isMultiLine(s string) bool {
|
|
| 253 |
+ for i := 0; i+1 < len(s); i++ {
|
|
| 254 |
+ if s[i] == '\n' {
|
|
| 255 |
+ return true |
|
| 256 |
+ } |
|
| 257 |
+ } |
|
| 258 |
+ return false |
|
| 259 |
+} |
|
| 260 |
+ |
|
| 261 |
+func (c *C) logString(issue string) {
|
|
| 262 |
+ c.log("... ", issue)
|
|
| 263 |
+} |
|
| 264 |
+ |
|
| 265 |
+func (c *C) logCaller(skip int) {
|
|
| 266 |
+ // This is a bit heavier than it ought to be. |
|
| 267 |
+ skip += 1 // Our own frame. |
|
| 268 |
+ pc, callerFile, callerLine, ok := runtime.Caller(skip) |
|
| 269 |
+ if !ok {
|
|
| 270 |
+ return |
|
| 271 |
+ } |
|
| 272 |
+ var testFile string |
|
| 273 |
+ var testLine int |
|
| 274 |
+ testFunc := runtime.FuncForPC(c.method.PC()) |
|
| 275 |
+ if runtime.FuncForPC(pc) != testFunc {
|
|
| 276 |
+ for {
|
|
| 277 |
+ skip += 1 |
|
| 278 |
+ if pc, file, line, ok := runtime.Caller(skip); ok {
|
|
| 279 |
+ // Note that the test line may be different on |
|
| 280 |
+ // distinct calls for the same test. Showing |
|
| 281 |
+ // the "internal" line is helpful when debugging. |
|
| 282 |
+ if runtime.FuncForPC(pc) == testFunc {
|
|
| 283 |
+ testFile, testLine = file, line |
|
| 284 |
+ break |
|
| 285 |
+ } |
|
| 286 |
+ } else {
|
|
| 287 |
+ break |
|
| 288 |
+ } |
|
| 289 |
+ } |
|
| 290 |
+ } |
|
| 291 |
+ if testFile != "" && (testFile != callerFile || testLine != callerLine) {
|
|
| 292 |
+ c.logCode(testFile, testLine) |
|
| 293 |
+ } |
|
| 294 |
+ c.logCode(callerFile, callerLine) |
|
| 295 |
+} |
|
| 296 |
+ |
|
| 297 |
+func (c *C) logCode(path string, line int) {
|
|
| 298 |
+ c.logf("%s:%d:", nicePath(path), line)
|
|
| 299 |
+ code, err := printLine(path, line) |
|
| 300 |
+ if code == "" {
|
|
| 301 |
+ code = "..." // XXX Open the file and take the raw line. |
|
| 302 |
+ if err != nil {
|
|
| 303 |
+ code += err.Error() |
|
| 304 |
+ } |
|
| 305 |
+ } |
|
| 306 |
+ c.log(indent(code, " ")) |
|
| 307 |
+} |
|
| 308 |
+ |
|
| 309 |
+var valueGo = filepath.Join("reflect", "value.go")
|
|
| 310 |
+var asmGo = filepath.Join("runtime", "asm_")
|
|
| 311 |
+ |
|
| 312 |
+func (c *C) logPanic(skip int, value interface{}) {
|
|
| 313 |
+ skip++ // Our own frame. |
|
| 314 |
+ initialSkip := skip |
|
| 315 |
+ for ; ; skip++ {
|
|
| 316 |
+ if pc, file, line, ok := runtime.Caller(skip); ok {
|
|
| 317 |
+ if skip == initialSkip {
|
|
| 318 |
+ c.logf("... Panic: %s (PC=0x%X)\n", value, pc)
|
|
| 319 |
+ } |
|
| 320 |
+ name := niceFuncName(pc) |
|
| 321 |
+ path := nicePath(file) |
|
| 322 |
+ if strings.Contains(path, "/gopkg.in/check.v") {
|
|
| 323 |
+ continue |
|
| 324 |
+ } |
|
| 325 |
+ if name == "Value.call" && strings.HasSuffix(path, valueGo) {
|
|
| 326 |
+ continue |
|
| 327 |
+ } |
|
| 328 |
+ if name == "call16" && strings.Contains(path, asmGo) {
|
|
| 329 |
+ continue |
|
| 330 |
+ } |
|
| 331 |
+ c.logf("%s:%d\n in %s", nicePath(file), line, name)
|
|
| 332 |
+ } else {
|
|
| 333 |
+ break |
|
| 334 |
+ } |
|
| 335 |
+ } |
|
| 336 |
+} |
|
| 337 |
+ |
|
| 338 |
+func (c *C) logSoftPanic(issue string) {
|
|
| 339 |
+ c.log("... Panic: ", issue)
|
|
| 340 |
+} |
|
| 341 |
+ |
|
| 342 |
+func (c *C) logArgPanic(method *methodType, expectedType string) {
|
|
| 343 |
+ c.logf("... Panic: %s argument should be %s",
|
|
| 344 |
+ niceFuncName(method.PC()), expectedType) |
|
| 345 |
+} |
|
| 346 |
+ |
|
| 347 |
+// ----------------------------------------------------------------------- |
|
| 348 |
+// Some simple formatting helpers. |
|
| 349 |
+ |
|
| 350 |
+var initWD, initWDErr = os.Getwd() |
|
| 351 |
+ |
|
| 352 |
+func init() {
|
|
| 353 |
+ if initWDErr == nil {
|
|
| 354 |
+ initWD = strings.Replace(initWD, "\\", "/", -1) + "/" |
|
| 355 |
+ } |
|
| 356 |
+} |
|
| 357 |
+ |
|
| 358 |
+func nicePath(path string) string {
|
|
| 359 |
+ if initWDErr == nil {
|
|
| 360 |
+ if strings.HasPrefix(path, initWD) {
|
|
| 361 |
+ return path[len(initWD):] |
|
| 362 |
+ } |
|
| 363 |
+ } |
|
| 364 |
+ return path |
|
| 365 |
+} |
|
| 366 |
+ |
|
| 367 |
+func niceFuncPath(pc uintptr) string {
|
|
| 368 |
+ function := runtime.FuncForPC(pc) |
|
| 369 |
+ if function != nil {
|
|
| 370 |
+ filename, line := function.FileLine(pc) |
|
| 371 |
+ return fmt.Sprintf("%s:%d", nicePath(filename), line)
|
|
| 372 |
+ } |
|
| 373 |
+ return "<unknown path>" |
|
| 374 |
+} |
|
| 375 |
+ |
|
| 376 |
+func niceFuncName(pc uintptr) string {
|
|
| 377 |
+ function := runtime.FuncForPC(pc) |
|
| 378 |
+ if function != nil {
|
|
| 379 |
+ name := path.Base(function.Name()) |
|
| 380 |
+ if i := strings.Index(name, "."); i > 0 {
|
|
| 381 |
+ name = name[i+1:] |
|
| 382 |
+ } |
|
| 383 |
+ if strings.HasPrefix(name, "(*") {
|
|
| 384 |
+ if i := strings.Index(name, ")"); i > 0 {
|
|
| 385 |
+ name = name[2:i] + name[i+1:] |
|
| 386 |
+ } |
|
| 387 |
+ } |
|
| 388 |
+ if i := strings.LastIndex(name, ".*"); i != -1 {
|
|
| 389 |
+ name = name[:i] + "." + name[i+2:] |
|
| 390 |
+ } |
|
| 391 |
+ if i := strings.LastIndex(name, "·"); i != -1 {
|
|
| 392 |
+ name = name[:i] + "." + name[i+2:] |
|
| 393 |
+ } |
|
| 394 |
+ return name |
|
| 395 |
+ } |
|
| 396 |
+ return "<unknown function>" |
|
| 397 |
+} |
|
| 398 |
+ |
|
| 399 |
+// ----------------------------------------------------------------------- |
|
| 400 |
+// Result tracker to aggregate call results. |
|
| 401 |
+ |
|
| 402 |
+type Result struct {
|
|
| 403 |
+ Succeeded int |
|
| 404 |
+ Failed int |
|
| 405 |
+ Skipped int |
|
| 406 |
+ Panicked int |
|
| 407 |
+ FixturePanicked int |
|
| 408 |
+ ExpectedFailures int |
|
| 409 |
+ Missed int // Not even tried to run, related to a panic in the fixture. |
|
| 410 |
+ RunError error // Houston, we've got a problem. |
|
| 411 |
+ WorkDir string // If KeepWorkDir is true |
|
| 412 |
+} |
|
| 413 |
+ |
|
| 414 |
+type resultTracker struct {
|
|
| 415 |
+ result Result |
|
| 416 |
+ _lastWasProblem bool |
|
| 417 |
+ _waiting int |
|
| 418 |
+ _missed int |
|
| 419 |
+ _expectChan chan *C |
|
| 420 |
+ _doneChan chan *C |
|
| 421 |
+ _stopChan chan bool |
|
| 422 |
+} |
|
| 423 |
+ |
|
| 424 |
+func newResultTracker() *resultTracker {
|
|
| 425 |
+ return &resultTracker{_expectChan: make(chan *C), // Synchronous
|
|
| 426 |
+ _doneChan: make(chan *C, 32), // Asynchronous |
|
| 427 |
+ _stopChan: make(chan bool)} // Synchronous |
|
| 428 |
+} |
|
| 429 |
+ |
|
| 430 |
+func (tracker *resultTracker) start() {
|
|
| 431 |
+ go tracker._loopRoutine() |
|
| 432 |
+} |
|
| 433 |
+ |
|
| 434 |
+func (tracker *resultTracker) waitAndStop() {
|
|
| 435 |
+ <-tracker._stopChan |
|
| 436 |
+} |
|
| 437 |
+ |
|
| 438 |
+func (tracker *resultTracker) expectCall(c *C) {
|
|
| 439 |
+ tracker._expectChan <- c |
|
| 440 |
+} |
|
| 441 |
+ |
|
| 442 |
+func (tracker *resultTracker) callDone(c *C) {
|
|
| 443 |
+ tracker._doneChan <- c |
|
| 444 |
+} |
|
| 445 |
+ |
|
| 446 |
+func (tracker *resultTracker) _loopRoutine() {
|
|
| 447 |
+ for {
|
|
| 448 |
+ var c *C |
|
| 449 |
+ if tracker._waiting > 0 {
|
|
| 450 |
+ // Calls still running. Can't stop. |
|
| 451 |
+ select {
|
|
| 452 |
+ // XXX Reindent this (not now to make diff clear) |
|
| 453 |
+ case c = <-tracker._expectChan: |
|
| 454 |
+ tracker._waiting += 1 |
|
| 455 |
+ case c = <-tracker._doneChan: |
|
| 456 |
+ tracker._waiting -= 1 |
|
| 457 |
+ switch c.status {
|
|
| 458 |
+ case succeededSt: |
|
| 459 |
+ if c.kind == testKd {
|
|
| 460 |
+ if c.mustFail {
|
|
| 461 |
+ tracker.result.ExpectedFailures++ |
|
| 462 |
+ } else {
|
|
| 463 |
+ tracker.result.Succeeded++ |
|
| 464 |
+ } |
|
| 465 |
+ } |
|
| 466 |
+ case failedSt: |
|
| 467 |
+ tracker.result.Failed++ |
|
| 468 |
+ case panickedSt: |
|
| 469 |
+ if c.kind == fixtureKd {
|
|
| 470 |
+ tracker.result.FixturePanicked++ |
|
| 471 |
+ } else {
|
|
| 472 |
+ tracker.result.Panicked++ |
|
| 473 |
+ } |
|
| 474 |
+ case fixturePanickedSt: |
|
| 475 |
+ // Track it as missed, since the panic |
|
| 476 |
+ // was on the fixture, not on the test. |
|
| 477 |
+ tracker.result.Missed++ |
|
| 478 |
+ case missedSt: |
|
| 479 |
+ tracker.result.Missed++ |
|
| 480 |
+ case skippedSt: |
|
| 481 |
+ if c.kind == testKd {
|
|
| 482 |
+ tracker.result.Skipped++ |
|
| 483 |
+ } |
|
| 484 |
+ } |
|
| 485 |
+ } |
|
| 486 |
+ } else {
|
|
| 487 |
+ // No calls. Can stop, but no done calls here. |
|
| 488 |
+ select {
|
|
| 489 |
+ case tracker._stopChan <- true: |
|
| 490 |
+ return |
|
| 491 |
+ case c = <-tracker._expectChan: |
|
| 492 |
+ tracker._waiting += 1 |
|
| 493 |
+ case c = <-tracker._doneChan: |
|
| 494 |
+ panic("Tracker got an unexpected done call.")
|
|
| 495 |
+ } |
|
| 496 |
+ } |
|
| 497 |
+ } |
|
| 498 |
+} |
|
| 499 |
+ |
|
| 500 |
+// ----------------------------------------------------------------------- |
|
| 501 |
+// The underlying suite runner. |
|
| 502 |
+ |
|
| 503 |
+type suiteRunner struct {
|
|
| 504 |
+ suite interface{}
|
|
| 505 |
+ setUpSuite, tearDownSuite *methodType |
|
| 506 |
+ setUpTest, tearDownTest *methodType |
|
| 507 |
+ tests []*methodType |
|
| 508 |
+ tracker *resultTracker |
|
| 509 |
+ tempDir *tempDir |
|
| 510 |
+ keepDir bool |
|
| 511 |
+ output *outputWriter |
|
| 512 |
+ reportedProblemLast bool |
|
| 513 |
+ benchTime time.Duration |
|
| 514 |
+ benchMem bool |
|
| 515 |
+} |
|
| 516 |
+ |
|
| 517 |
+type RunConf struct {
|
|
| 518 |
+ Output io.Writer |
|
| 519 |
+ Stream bool |
|
| 520 |
+ Verbose bool |
|
| 521 |
+ Filter string |
|
| 522 |
+ Benchmark bool |
|
| 523 |
+ BenchmarkTime time.Duration // Defaults to 1 second |
|
| 524 |
+ BenchmarkMem bool |
|
| 525 |
+ KeepWorkDir bool |
|
| 526 |
+} |
|
| 527 |
+ |
|
| 528 |
+// Create a new suiteRunner able to run all methods in the given suite. |
|
| 529 |
+func newSuiteRunner(suite interface{}, runConf *RunConf) *suiteRunner {
|
|
| 530 |
+ var conf RunConf |
|
| 531 |
+ if runConf != nil {
|
|
| 532 |
+ conf = *runConf |
|
| 533 |
+ } |
|
| 534 |
+ if conf.Output == nil {
|
|
| 535 |
+ conf.Output = os.Stdout |
|
| 536 |
+ } |
|
| 537 |
+ if conf.Benchmark {
|
|
| 538 |
+ conf.Verbose = true |
|
| 539 |
+ } |
|
| 540 |
+ |
|
| 541 |
+ suiteType := reflect.TypeOf(suite) |
|
| 542 |
+ suiteNumMethods := suiteType.NumMethod() |
|
| 543 |
+ suiteValue := reflect.ValueOf(suite) |
|
| 544 |
+ |
|
| 545 |
+ runner := &suiteRunner{
|
|
| 546 |
+ suite: suite, |
|
| 547 |
+ output: newOutputWriter(conf.Output, conf.Stream, conf.Verbose), |
|
| 548 |
+ tracker: newResultTracker(), |
|
| 549 |
+ benchTime: conf.BenchmarkTime, |
|
| 550 |
+ benchMem: conf.BenchmarkMem, |
|
| 551 |
+ tempDir: &tempDir{},
|
|
| 552 |
+ keepDir: conf.KeepWorkDir, |
|
| 553 |
+ tests: make([]*methodType, 0, suiteNumMethods), |
|
| 554 |
+ } |
|
| 555 |
+ if runner.benchTime == 0 {
|
|
| 556 |
+ runner.benchTime = 1 * time.Second |
|
| 557 |
+ } |
|
| 558 |
+ |
|
| 559 |
+ var filterRegexp *regexp.Regexp |
|
| 560 |
+ if conf.Filter != "" {
|
|
| 561 |
+ if regexp, err := regexp.Compile(conf.Filter); err != nil {
|
|
| 562 |
+ msg := "Bad filter expression: " + err.Error() |
|
| 563 |
+ runner.tracker.result.RunError = errors.New(msg) |
|
| 564 |
+ return runner |
|
| 565 |
+ } else {
|
|
| 566 |
+ filterRegexp = regexp |
|
| 567 |
+ } |
|
| 568 |
+ } |
|
| 569 |
+ |
|
| 570 |
+ for i := 0; i != suiteNumMethods; i++ {
|
|
| 571 |
+ method := newMethod(suiteValue, i) |
|
| 572 |
+ switch method.Info.Name {
|
|
| 573 |
+ case "SetUpSuite": |
|
| 574 |
+ runner.setUpSuite = method |
|
| 575 |
+ case "TearDownSuite": |
|
| 576 |
+ runner.tearDownSuite = method |
|
| 577 |
+ case "SetUpTest": |
|
| 578 |
+ runner.setUpTest = method |
|
| 579 |
+ case "TearDownTest": |
|
| 580 |
+ runner.tearDownTest = method |
|
| 581 |
+ default: |
|
| 582 |
+ prefix := "Test" |
|
| 583 |
+ if conf.Benchmark {
|
|
| 584 |
+ prefix = "Benchmark" |
|
| 585 |
+ } |
|
| 586 |
+ if !strings.HasPrefix(method.Info.Name, prefix) {
|
|
| 587 |
+ continue |
|
| 588 |
+ } |
|
| 589 |
+ if filterRegexp == nil || method.matches(filterRegexp) {
|
|
| 590 |
+ runner.tests = append(runner.tests, method) |
|
| 591 |
+ } |
|
| 592 |
+ } |
|
| 593 |
+ } |
|
| 594 |
+ return runner |
|
| 595 |
+} |
|
| 596 |
+ |
|
| 597 |
+// Run all methods in the given suite. |
|
| 598 |
+func (runner *suiteRunner) run() *Result {
|
|
| 599 |
+ if runner.tracker.result.RunError == nil && len(runner.tests) > 0 {
|
|
| 600 |
+ runner.tracker.start() |
|
| 601 |
+ if runner.checkFixtureArgs() {
|
|
| 602 |
+ c := runner.runFixture(runner.setUpSuite, "", nil) |
|
| 603 |
+ if c == nil || c.status == succeededSt {
|
|
| 604 |
+ for i := 0; i != len(runner.tests); i++ {
|
|
| 605 |
+ c := runner.runTest(runner.tests[i]) |
|
| 606 |
+ if c.status == fixturePanickedSt {
|
|
| 607 |
+ runner.skipTests(missedSt, runner.tests[i+1:]) |
|
| 608 |
+ break |
|
| 609 |
+ } |
|
| 610 |
+ } |
|
| 611 |
+ } else if c != nil && c.status == skippedSt {
|
|
| 612 |
+ runner.skipTests(skippedSt, runner.tests) |
|
| 613 |
+ } else {
|
|
| 614 |
+ runner.skipTests(missedSt, runner.tests) |
|
| 615 |
+ } |
|
| 616 |
+ runner.runFixture(runner.tearDownSuite, "", nil) |
|
| 617 |
+ } else {
|
|
| 618 |
+ runner.skipTests(missedSt, runner.tests) |
|
| 619 |
+ } |
|
| 620 |
+ runner.tracker.waitAndStop() |
|
| 621 |
+ if runner.keepDir {
|
|
| 622 |
+ runner.tracker.result.WorkDir = runner.tempDir.path |
|
| 623 |
+ } else {
|
|
| 624 |
+ runner.tempDir.removeAll() |
|
| 625 |
+ } |
|
| 626 |
+ } |
|
| 627 |
+ return &runner.tracker.result |
|
| 628 |
+} |
|
| 629 |
+ |
|
| 630 |
+// Create a call object with the given suite method, and fork a |
|
| 631 |
+// goroutine with the provided dispatcher for running it. |
|
| 632 |
+func (runner *suiteRunner) forkCall(method *methodType, kind funcKind, testName string, logb *logger, dispatcher func(c *C)) *C {
|
|
| 633 |
+ var logw io.Writer |
|
| 634 |
+ if runner.output.Stream {
|
|
| 635 |
+ logw = runner.output |
|
| 636 |
+ } |
|
| 637 |
+ if logb == nil {
|
|
| 638 |
+ logb = new(logger) |
|
| 639 |
+ } |
|
| 640 |
+ c := &C{
|
|
| 641 |
+ method: method, |
|
| 642 |
+ kind: kind, |
|
| 643 |
+ testName: testName, |
|
| 644 |
+ logb: logb, |
|
| 645 |
+ logw: logw, |
|
| 646 |
+ tempDir: runner.tempDir, |
|
| 647 |
+ done: make(chan *C, 1), |
|
| 648 |
+ timer: timer{benchTime: runner.benchTime},
|
|
| 649 |
+ startTime: time.Now(), |
|
| 650 |
+ benchMem: runner.benchMem, |
|
| 651 |
+ } |
|
| 652 |
+ runner.tracker.expectCall(c) |
|
| 653 |
+ go (func() {
|
|
| 654 |
+ runner.reportCallStarted(c) |
|
| 655 |
+ defer runner.callDone(c) |
|
| 656 |
+ dispatcher(c) |
|
| 657 |
+ })() |
|
| 658 |
+ return c |
|
| 659 |
+} |
|
| 660 |
+ |
|
| 661 |
+// Same as forkCall(), but wait for call to finish before returning. |
|
| 662 |
+func (runner *suiteRunner) runFunc(method *methodType, kind funcKind, testName string, logb *logger, dispatcher func(c *C)) *C {
|
|
| 663 |
+ c := runner.forkCall(method, kind, testName, logb, dispatcher) |
|
| 664 |
+ <-c.done |
|
| 665 |
+ return c |
|
| 666 |
+} |
|
| 667 |
+ |
|
| 668 |
+// Handle a finished call. If there were any panics, update the call status |
|
| 669 |
+// accordingly. Then, mark the call as done and report to the tracker. |
|
| 670 |
+func (runner *suiteRunner) callDone(c *C) {
|
|
| 671 |
+ value := recover() |
|
| 672 |
+ if value != nil {
|
|
| 673 |
+ switch v := value.(type) {
|
|
| 674 |
+ case *fixturePanic: |
|
| 675 |
+ if v.status == skippedSt {
|
|
| 676 |
+ c.status = skippedSt |
|
| 677 |
+ } else {
|
|
| 678 |
+ c.logSoftPanic("Fixture has panicked (see related PANIC)")
|
|
| 679 |
+ c.status = fixturePanickedSt |
|
| 680 |
+ } |
|
| 681 |
+ default: |
|
| 682 |
+ c.logPanic(1, value) |
|
| 683 |
+ c.status = panickedSt |
|
| 684 |
+ } |
|
| 685 |
+ } |
|
| 686 |
+ if c.mustFail {
|
|
| 687 |
+ switch c.status {
|
|
| 688 |
+ case failedSt: |
|
| 689 |
+ c.status = succeededSt |
|
| 690 |
+ case succeededSt: |
|
| 691 |
+ c.status = failedSt |
|
| 692 |
+ c.logString("Error: Test succeeded, but was expected to fail")
|
|
| 693 |
+ c.logString("Reason: " + c.reason)
|
|
| 694 |
+ } |
|
| 695 |
+ } |
|
| 696 |
+ |
|
| 697 |
+ runner.reportCallDone(c) |
|
| 698 |
+ c.done <- c |
|
| 699 |
+} |
|
| 700 |
+ |
|
| 701 |
+// Runs a fixture call synchronously. The fixture will still be run in a |
|
| 702 |
+// goroutine like all suite methods, but this method will not return |
|
| 703 |
+// while the fixture goroutine is not done, because the fixture must be |
|
| 704 |
+// run in a desired order. |
|
| 705 |
+func (runner *suiteRunner) runFixture(method *methodType, testName string, logb *logger) *C {
|
|
| 706 |
+ if method != nil {
|
|
| 707 |
+ c := runner.runFunc(method, fixtureKd, testName, logb, func(c *C) {
|
|
| 708 |
+ c.ResetTimer() |
|
| 709 |
+ c.StartTimer() |
|
| 710 |
+ defer c.StopTimer() |
|
| 711 |
+ c.method.Call([]reflect.Value{reflect.ValueOf(c)})
|
|
| 712 |
+ }) |
|
| 713 |
+ return c |
|
| 714 |
+ } |
|
| 715 |
+ return nil |
|
| 716 |
+} |
|
| 717 |
+ |
|
| 718 |
+// Run the fixture method with runFixture(), but panic with a fixturePanic{}
|
|
| 719 |
+// in case the fixture method panics. This makes it easier to track the |
|
| 720 |
+// fixture panic together with other call panics within forkTest(). |
|
| 721 |
+func (runner *suiteRunner) runFixtureWithPanic(method *methodType, testName string, logb *logger, skipped *bool) *C {
|
|
| 722 |
+ if skipped != nil && *skipped {
|
|
| 723 |
+ return nil |
|
| 724 |
+ } |
|
| 725 |
+ c := runner.runFixture(method, testName, logb) |
|
| 726 |
+ if c != nil && c.status != succeededSt {
|
|
| 727 |
+ if skipped != nil {
|
|
| 728 |
+ *skipped = c.status == skippedSt |
|
| 729 |
+ } |
|
| 730 |
+ panic(&fixturePanic{c.status, method})
|
|
| 731 |
+ } |
|
| 732 |
+ return c |
|
| 733 |
+} |
|
| 734 |
+ |
|
| 735 |
+type fixturePanic struct {
|
|
| 736 |
+ status funcStatus |
|
| 737 |
+ method *methodType |
|
| 738 |
+} |
|
| 739 |
+ |
|
| 740 |
+// Run the suite test method, together with the test-specific fixture, |
|
| 741 |
+// asynchronously. |
|
| 742 |
+func (runner *suiteRunner) forkTest(method *methodType) *C {
|
|
| 743 |
+ testName := method.String() |
|
| 744 |
+ return runner.forkCall(method, testKd, testName, nil, func(c *C) {
|
|
| 745 |
+ var skipped bool |
|
| 746 |
+ defer runner.runFixtureWithPanic(runner.tearDownTest, testName, nil, &skipped) |
|
| 747 |
+ defer c.StopTimer() |
|
| 748 |
+ benchN := 1 |
|
| 749 |
+ for {
|
|
| 750 |
+ runner.runFixtureWithPanic(runner.setUpTest, testName, c.logb, &skipped) |
|
| 751 |
+ mt := c.method.Type() |
|
| 752 |
+ if mt.NumIn() != 1 || mt.In(0) != reflect.TypeOf(c) {
|
|
| 753 |
+ // Rather than a plain panic, provide a more helpful message when |
|
| 754 |
+ // the argument type is incorrect. |
|
| 755 |
+ c.status = panickedSt |
|
| 756 |
+ c.logArgPanic(c.method, "*check.C") |
|
| 757 |
+ return |
|
| 758 |
+ } |
|
| 759 |
+ if strings.HasPrefix(c.method.Info.Name, "Test") {
|
|
| 760 |
+ c.ResetTimer() |
|
| 761 |
+ c.StartTimer() |
|
| 762 |
+ c.method.Call([]reflect.Value{reflect.ValueOf(c)})
|
|
| 763 |
+ return |
|
| 764 |
+ } |
|
| 765 |
+ if !strings.HasPrefix(c.method.Info.Name, "Benchmark") {
|
|
| 766 |
+ panic("unexpected method prefix: " + c.method.Info.Name)
|
|
| 767 |
+ } |
|
| 768 |
+ |
|
| 769 |
+ runtime.GC() |
|
| 770 |
+ c.N = benchN |
|
| 771 |
+ c.ResetTimer() |
|
| 772 |
+ c.StartTimer() |
|
| 773 |
+ c.method.Call([]reflect.Value{reflect.ValueOf(c)})
|
|
| 774 |
+ c.StopTimer() |
|
| 775 |
+ if c.status != succeededSt || c.duration >= c.benchTime || benchN >= 1e9 {
|
|
| 776 |
+ return |
|
| 777 |
+ } |
|
| 778 |
+ perOpN := int(1e9) |
|
| 779 |
+ if c.nsPerOp() != 0 {
|
|
| 780 |
+ perOpN = int(c.benchTime.Nanoseconds() / c.nsPerOp()) |
|
| 781 |
+ } |
|
| 782 |
+ |
|
| 783 |
+ // Logic taken from the stock testing package: |
|
| 784 |
+ // - Run more iterations than we think we'll need for a second (1.5x). |
|
| 785 |
+ // - Don't grow too fast in case we had timing errors previously. |
|
| 786 |
+ // - Be sure to run at least one more than last time. |
|
| 787 |
+ benchN = max(min(perOpN+perOpN/2, 100*benchN), benchN+1) |
|
| 788 |
+ benchN = roundUp(benchN) |
|
| 789 |
+ |
|
| 790 |
+ skipped = true // Don't run the deferred one if this panics. |
|
| 791 |
+ runner.runFixtureWithPanic(runner.tearDownTest, testName, nil, nil) |
|
| 792 |
+ skipped = false |
|
| 793 |
+ } |
|
| 794 |
+ }) |
|
| 795 |
+} |
|
| 796 |
+ |
|
| 797 |
+// Same as forkTest(), but wait for the test to finish before returning. |
|
| 798 |
+func (runner *suiteRunner) runTest(method *methodType) *C {
|
|
| 799 |
+ c := runner.forkTest(method) |
|
| 800 |
+ <-c.done |
|
| 801 |
+ return c |
|
| 802 |
+} |
|
| 803 |
+ |
|
| 804 |
+// Helper to mark tests as skipped or missed. A bit heavy for what |
|
| 805 |
+// it does, but it enables homogeneous handling of tracking, including |
|
| 806 |
+// nice verbose output. |
|
| 807 |
+func (runner *suiteRunner) skipTests(status funcStatus, methods []*methodType) {
|
|
| 808 |
+ for _, method := range methods {
|
|
| 809 |
+ runner.runFunc(method, testKd, "", nil, func(c *C) {
|
|
| 810 |
+ c.status = status |
|
| 811 |
+ }) |
|
| 812 |
+ } |
|
| 813 |
+} |
|
| 814 |
+ |
|
| 815 |
+// Verify if the fixture arguments are *check.C. In case of errors, |
|
| 816 |
+// log the error as a panic in the fixture method call, and return false. |
|
| 817 |
+func (runner *suiteRunner) checkFixtureArgs() bool {
|
|
| 818 |
+ succeeded := true |
|
| 819 |
+ argType := reflect.TypeOf(&C{})
|
|
| 820 |
+ for _, method := range []*methodType{runner.setUpSuite, runner.tearDownSuite, runner.setUpTest, runner.tearDownTest} {
|
|
| 821 |
+ if method != nil {
|
|
| 822 |
+ mt := method.Type() |
|
| 823 |
+ if mt.NumIn() != 1 || mt.In(0) != argType {
|
|
| 824 |
+ succeeded = false |
|
| 825 |
+ runner.runFunc(method, fixtureKd, "", nil, func(c *C) {
|
|
| 826 |
+ c.logArgPanic(method, "*check.C") |
|
| 827 |
+ c.status = panickedSt |
|
| 828 |
+ }) |
|
| 829 |
+ } |
|
| 830 |
+ } |
|
| 831 |
+ } |
|
| 832 |
+ return succeeded |
|
| 833 |
+} |
|
| 834 |
+ |
|
| 835 |
+func (runner *suiteRunner) reportCallStarted(c *C) {
|
|
| 836 |
+ runner.output.WriteCallStarted("START", c)
|
|
| 837 |
+} |
|
| 838 |
+ |
|
| 839 |
+func (runner *suiteRunner) reportCallDone(c *C) {
|
|
| 840 |
+ runner.tracker.callDone(c) |
|
| 841 |
+ switch c.status {
|
|
| 842 |
+ case succeededSt: |
|
| 843 |
+ if c.mustFail {
|
|
| 844 |
+ runner.output.WriteCallSuccess("FAIL EXPECTED", c)
|
|
| 845 |
+ } else {
|
|
| 846 |
+ runner.output.WriteCallSuccess("PASS", c)
|
|
| 847 |
+ } |
|
| 848 |
+ case skippedSt: |
|
| 849 |
+ runner.output.WriteCallSuccess("SKIP", c)
|
|
| 850 |
+ case failedSt: |
|
| 851 |
+ runner.output.WriteCallProblem("FAIL", c)
|
|
| 852 |
+ case panickedSt: |
|
| 853 |
+ runner.output.WriteCallProblem("PANIC", c)
|
|
| 854 |
+ case fixturePanickedSt: |
|
| 855 |
+ // That's a testKd call reporting that its fixture |
|
| 856 |
+ // has panicked. The fixture call which caused the |
|
| 857 |
+ // panic itself was tracked above. We'll report to |
|
| 858 |
+ // aid debugging. |
|
| 859 |
+ runner.output.WriteCallProblem("PANIC", c)
|
|
| 860 |
+ case missedSt: |
|
| 861 |
+ runner.output.WriteCallSuccess("MISS", c)
|
|
| 862 |
+ } |
|
| 863 |
+} |
|
| 864 |
+ |
|
| 865 |
+// ----------------------------------------------------------------------- |
|
| 866 |
+// Output writer manages atomic output writing according to settings. |
|
| 867 |
+ |
|
| 868 |
+type outputWriter struct {
|
|
| 869 |
+ m sync.Mutex |
|
| 870 |
+ writer io.Writer |
|
| 871 |
+ wroteCallProblemLast bool |
|
| 872 |
+ Stream bool |
|
| 873 |
+ Verbose bool |
|
| 874 |
+} |
|
| 875 |
+ |
|
| 876 |
+func newOutputWriter(writer io.Writer, stream, verbose bool) *outputWriter {
|
|
| 877 |
+ return &outputWriter{writer: writer, Stream: stream, Verbose: verbose}
|
|
| 878 |
+} |
|
| 879 |
+ |
|
| 880 |
+func (ow *outputWriter) Write(content []byte) (n int, err error) {
|
|
| 881 |
+ ow.m.Lock() |
|
| 882 |
+ n, err = ow.writer.Write(content) |
|
| 883 |
+ ow.m.Unlock() |
|
| 884 |
+ return |
|
| 885 |
+} |
|
| 886 |
+ |
|
| 887 |
+func (ow *outputWriter) WriteCallStarted(label string, c *C) {
|
|
| 888 |
+ if ow.Stream {
|
|
| 889 |
+ header := renderCallHeader(label, c, "", "\n") |
|
| 890 |
+ ow.m.Lock() |
|
| 891 |
+ ow.writer.Write([]byte(header)) |
|
| 892 |
+ ow.m.Unlock() |
|
| 893 |
+ } |
|
| 894 |
+} |
|
| 895 |
+ |
|
| 896 |
+func (ow *outputWriter) WriteCallProblem(label string, c *C) {
|
|
| 897 |
+ var prefix string |
|
| 898 |
+ if !ow.Stream {
|
|
| 899 |
+ prefix = "\n-----------------------------------" + |
|
| 900 |
+ "-----------------------------------\n" |
|
| 901 |
+ } |
|
| 902 |
+ header := renderCallHeader(label, c, prefix, "\n\n") |
|
| 903 |
+ ow.m.Lock() |
|
| 904 |
+ ow.wroteCallProblemLast = true |
|
| 905 |
+ ow.writer.Write([]byte(header)) |
|
| 906 |
+ if !ow.Stream {
|
|
| 907 |
+ c.logb.WriteTo(ow.writer) |
|
| 908 |
+ } |
|
| 909 |
+ ow.m.Unlock() |
|
| 910 |
+} |
|
| 911 |
+ |
|
| 912 |
+func (ow *outputWriter) WriteCallSuccess(label string, c *C) {
|
|
| 913 |
+ if ow.Stream || (ow.Verbose && c.kind == testKd) {
|
|
| 914 |
+ // TODO Use a buffer here. |
|
| 915 |
+ var suffix string |
|
| 916 |
+ if c.reason != "" {
|
|
| 917 |
+ suffix = " (" + c.reason + ")"
|
|
| 918 |
+ } |
|
| 919 |
+ if c.status == succeededSt {
|
|
| 920 |
+ suffix += "\t" + c.timerString() |
|
| 921 |
+ } |
|
| 922 |
+ suffix += "\n" |
|
| 923 |
+ if ow.Stream {
|
|
| 924 |
+ suffix += "\n" |
|
| 925 |
+ } |
|
| 926 |
+ header := renderCallHeader(label, c, "", suffix) |
|
| 927 |
+ ow.m.Lock() |
|
| 928 |
+ // Resist temptation of using line as prefix above due to race. |
|
| 929 |
+ if !ow.Stream && ow.wroteCallProblemLast {
|
|
| 930 |
+ header = "\n-----------------------------------" + |
|
| 931 |
+ "-----------------------------------\n" + |
|
| 932 |
+ header |
|
| 933 |
+ } |
|
| 934 |
+ ow.wroteCallProblemLast = false |
|
| 935 |
+ ow.writer.Write([]byte(header)) |
|
| 936 |
+ ow.m.Unlock() |
|
| 937 |
+ } |
|
| 938 |
+} |
|
| 939 |
+ |
|
| 940 |
+func renderCallHeader(label string, c *C, prefix, suffix string) string {
|
|
| 941 |
+ pc := c.method.PC() |
|
| 942 |
+ return fmt.Sprintf("%s%s: %s: %s%s", prefix, label, niceFuncPath(pc),
|
|
| 943 |
+ niceFuncName(pc), suffix) |
|
| 944 |
+} |
| 0 | 945 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,207 @@ |
| 0 |
+// This file contains just a few generic helpers which are used by the |
|
| 1 |
+// other test files. |
|
| 2 |
+ |
|
| 3 |
+package check_test |
|
| 4 |
+ |
|
| 5 |
+import ( |
|
| 6 |
+ "flag" |
|
| 7 |
+ "fmt" |
|
| 8 |
+ "os" |
|
| 9 |
+ "regexp" |
|
| 10 |
+ "runtime" |
|
| 11 |
+ "testing" |
|
| 12 |
+ "time" |
|
| 13 |
+ |
|
| 14 |
+ "gopkg.in/check.v1" |
|
| 15 |
+) |
|
| 16 |
+ |
|
| 17 |
+// We count the number of suites run at least to get a vague hint that the |
|
| 18 |
+// test suite is behaving as it should. Otherwise a bug introduced at the |
|
| 19 |
+// very core of the system could go unperceived. |
|
| 20 |
+const suitesRunExpected = 8 |
|
| 21 |
+ |
|
| 22 |
+var suitesRun int = 0 |
|
| 23 |
+ |
|
| 24 |
+func Test(t *testing.T) {
|
|
| 25 |
+ check.TestingT(t) |
|
| 26 |
+ if suitesRun != suitesRunExpected && flag.Lookup("check.f").Value.String() == "" {
|
|
| 27 |
+ critical(fmt.Sprintf("Expected %d suites to run rather than %d",
|
|
| 28 |
+ suitesRunExpected, suitesRun)) |
|
| 29 |
+ } |
|
| 30 |
+} |
|
| 31 |
+ |
|
| 32 |
+// ----------------------------------------------------------------------- |
|
| 33 |
+// Helper functions. |
|
| 34 |
+ |
|
| 35 |
+// Break down badly. This is used in test cases which can't yet assume |
|
| 36 |
+// that the fundamental bits are working. |
|
| 37 |
+func critical(error string) {
|
|
| 38 |
+ fmt.Fprintln(os.Stderr, "CRITICAL: "+error) |
|
| 39 |
+ os.Exit(1) |
|
| 40 |
+} |
|
| 41 |
+ |
|
| 42 |
+// Return the file line where it's called. |
|
| 43 |
+func getMyLine() int {
|
|
| 44 |
+ if _, _, line, ok := runtime.Caller(1); ok {
|
|
| 45 |
+ return line |
|
| 46 |
+ } |
|
| 47 |
+ return -1 |
|
| 48 |
+} |
|
| 49 |
+ |
|
| 50 |
+// ----------------------------------------------------------------------- |
|
| 51 |
+// Helper type implementing a basic io.Writer for testing output. |
|
| 52 |
+ |
|
| 53 |
+// Type implementing the io.Writer interface for analyzing output. |
|
| 54 |
+type String struct {
|
|
| 55 |
+ value string |
|
| 56 |
+} |
|
| 57 |
+ |
|
| 58 |
+// The only function required by the io.Writer interface. Will append |
|
| 59 |
+// written data to the String.value string. |
|
| 60 |
+func (s *String) Write(p []byte) (n int, err error) {
|
|
| 61 |
+ s.value += string(p) |
|
| 62 |
+ return len(p), nil |
|
| 63 |
+} |
|
| 64 |
+ |
|
| 65 |
+// Trivial wrapper to test errors happening on a different file |
|
| 66 |
+// than the test itself. |
|
| 67 |
+func checkEqualWrapper(c *check.C, obtained, expected interface{}) (result bool, line int) {
|
|
| 68 |
+ return c.Check(obtained, check.Equals, expected), getMyLine() |
|
| 69 |
+} |
|
| 70 |
+ |
|
| 71 |
+// ----------------------------------------------------------------------- |
|
| 72 |
+// Helper suite for testing basic fail behavior. |
|
| 73 |
+ |
|
| 74 |
+type FailHelper struct {
|
|
| 75 |
+ testLine int |
|
| 76 |
+} |
|
| 77 |
+ |
|
| 78 |
+func (s *FailHelper) TestLogAndFail(c *check.C) {
|
|
| 79 |
+ s.testLine = getMyLine() - 1 |
|
| 80 |
+ c.Log("Expected failure!")
|
|
| 81 |
+ c.Fail() |
|
| 82 |
+} |
|
| 83 |
+ |
|
| 84 |
+// ----------------------------------------------------------------------- |
|
| 85 |
+// Helper suite for testing basic success behavior. |
|
| 86 |
+ |
|
| 87 |
+type SuccessHelper struct{}
|
|
| 88 |
+ |
|
| 89 |
+func (s *SuccessHelper) TestLogAndSucceed(c *check.C) {
|
|
| 90 |
+ c.Log("Expected success!")
|
|
| 91 |
+} |
|
| 92 |
+ |
|
| 93 |
+// ----------------------------------------------------------------------- |
|
| 94 |
+// Helper suite for testing ordering and behavior of fixture. |
|
| 95 |
+ |
|
| 96 |
+type FixtureHelper struct {
|
|
| 97 |
+ calls []string |
|
| 98 |
+ panicOn string |
|
| 99 |
+ skip bool |
|
| 100 |
+ skipOnN int |
|
| 101 |
+ sleepOn string |
|
| 102 |
+ sleep time.Duration |
|
| 103 |
+ bytes int64 |
|
| 104 |
+} |
|
| 105 |
+ |
|
| 106 |
+func (s *FixtureHelper) trace(name string, c *check.C) {
|
|
| 107 |
+ s.calls = append(s.calls, name) |
|
| 108 |
+ if name == s.panicOn {
|
|
| 109 |
+ panic(name) |
|
| 110 |
+ } |
|
| 111 |
+ if s.sleep > 0 && s.sleepOn == name {
|
|
| 112 |
+ time.Sleep(s.sleep) |
|
| 113 |
+ } |
|
| 114 |
+ if s.skip && s.skipOnN == len(s.calls)-1 {
|
|
| 115 |
+ c.Skip("skipOnN == n")
|
|
| 116 |
+ } |
|
| 117 |
+} |
|
| 118 |
+ |
|
| 119 |
+func (s *FixtureHelper) SetUpSuite(c *check.C) {
|
|
| 120 |
+ s.trace("SetUpSuite", c)
|
|
| 121 |
+} |
|
| 122 |
+ |
|
| 123 |
+func (s *FixtureHelper) TearDownSuite(c *check.C) {
|
|
| 124 |
+ s.trace("TearDownSuite", c)
|
|
| 125 |
+} |
|
| 126 |
+ |
|
| 127 |
+func (s *FixtureHelper) SetUpTest(c *check.C) {
|
|
| 128 |
+ s.trace("SetUpTest", c)
|
|
| 129 |
+} |
|
| 130 |
+ |
|
| 131 |
+func (s *FixtureHelper) TearDownTest(c *check.C) {
|
|
| 132 |
+ s.trace("TearDownTest", c)
|
|
| 133 |
+} |
|
| 134 |
+ |
|
| 135 |
+func (s *FixtureHelper) Test1(c *check.C) {
|
|
| 136 |
+ s.trace("Test1", c)
|
|
| 137 |
+} |
|
| 138 |
+ |
|
| 139 |
+func (s *FixtureHelper) Test2(c *check.C) {
|
|
| 140 |
+ s.trace("Test2", c)
|
|
| 141 |
+} |
|
| 142 |
+ |
|
| 143 |
+func (s *FixtureHelper) Benchmark1(c *check.C) {
|
|
| 144 |
+ s.trace("Benchmark1", c)
|
|
| 145 |
+ for i := 0; i < c.N; i++ {
|
|
| 146 |
+ time.Sleep(s.sleep) |
|
| 147 |
+ } |
|
| 148 |
+} |
|
| 149 |
+ |
|
| 150 |
+func (s *FixtureHelper) Benchmark2(c *check.C) {
|
|
| 151 |
+ s.trace("Benchmark2", c)
|
|
| 152 |
+ c.SetBytes(1024) |
|
| 153 |
+ for i := 0; i < c.N; i++ {
|
|
| 154 |
+ time.Sleep(s.sleep) |
|
| 155 |
+ } |
|
| 156 |
+} |
|
| 157 |
+ |
|
| 158 |
+func (s *FixtureHelper) Benchmark3(c *check.C) {
|
|
| 159 |
+ var x []int64 |
|
| 160 |
+ s.trace("Benchmark3", c)
|
|
| 161 |
+ for i := 0; i < c.N; i++ {
|
|
| 162 |
+ time.Sleep(s.sleep) |
|
| 163 |
+ x = make([]int64, 5) |
|
| 164 |
+ _ = x |
|
| 165 |
+ } |
|
| 166 |
+} |
|
| 167 |
+ |
|
| 168 |
+// ----------------------------------------------------------------------- |
|
| 169 |
+// Helper which checks the state of the test and ensures that it matches |
|
| 170 |
+// the given expectations. Depends on c.Errorf() working, so shouldn't |
|
| 171 |
+// be used to test this one function. |
|
| 172 |
+ |
|
| 173 |
+type expectedState struct {
|
|
| 174 |
+ name string |
|
| 175 |
+ result interface{}
|
|
| 176 |
+ failed bool |
|
| 177 |
+ log string |
|
| 178 |
+} |
|
| 179 |
+ |
|
| 180 |
+// Verify the state of the test. Note that since this also verifies if |
|
| 181 |
+// the test is supposed to be in a failed state, no other checks should |
|
| 182 |
+// be done in addition to what is being tested. |
|
| 183 |
+func checkState(c *check.C, result interface{}, expected *expectedState) {
|
|
| 184 |
+ failed := c.Failed() |
|
| 185 |
+ c.Succeed() |
|
| 186 |
+ log := c.GetTestLog() |
|
| 187 |
+ matched, matchError := regexp.MatchString("^"+expected.log+"$", log)
|
|
| 188 |
+ if matchError != nil {
|
|
| 189 |
+ c.Errorf("Error in matching expression used in testing %s",
|
|
| 190 |
+ expected.name) |
|
| 191 |
+ } else if !matched {
|
|
| 192 |
+ c.Errorf("%s logged:\n----------\n%s----------\n\nExpected:\n----------\n%s\n----------",
|
|
| 193 |
+ expected.name, log, expected.log) |
|
| 194 |
+ } |
|
| 195 |
+ if result != expected.result {
|
|
| 196 |
+ c.Errorf("%s returned %#v rather than %#v",
|
|
| 197 |
+ expected.name, result, expected.result) |
|
| 198 |
+ } |
|
| 199 |
+ if failed != expected.failed {
|
|
| 200 |
+ if failed {
|
|
| 201 |
+ c.Errorf("%s has failed when it shouldn't", expected.name)
|
|
| 202 |
+ } else {
|
|
| 203 |
+ c.Errorf("%s has not failed when it should", expected.name)
|
|
| 204 |
+ } |
|
| 205 |
+ } |
|
| 206 |
+} |
| 0 | 207 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,458 @@ |
| 0 |
+package check |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "reflect" |
|
| 5 |
+ "regexp" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+// ----------------------------------------------------------------------- |
|
| 9 |
+// CommentInterface and Commentf helper, to attach extra information to checks. |
|
| 10 |
+ |
|
| 11 |
+type comment struct {
|
|
| 12 |
+ format string |
|
| 13 |
+ args []interface{}
|
|
| 14 |
+} |
|
| 15 |
+ |
|
| 16 |
+// Commentf returns an infomational value to use with Assert or Check calls. |
|
| 17 |
+// If the checker test fails, the provided arguments will be passed to |
|
| 18 |
+// fmt.Sprintf, and will be presented next to the logged failure. |
|
| 19 |
+// |
|
| 20 |
+// For example: |
|
| 21 |
+// |
|
| 22 |
+// c.Assert(v, Equals, 42, Commentf("Iteration #%d failed.", i))
|
|
| 23 |
+// |
|
| 24 |
+// Note that if the comment is constant, a better option is to |
|
| 25 |
+// simply use a normal comment right above or next to the line, as |
|
| 26 |
+// it will also get printed with any errors: |
|
| 27 |
+// |
|
| 28 |
+// c.Assert(l, Equals, 8192) // Ensure buffer size is correct (bug #123) |
|
| 29 |
+// |
|
| 30 |
+func Commentf(format string, args ...interface{}) CommentInterface {
|
|
| 31 |
+ return &comment{format, args}
|
|
| 32 |
+} |
|
| 33 |
+ |
|
| 34 |
+// CommentInterface must be implemented by types that attach extra |
|
| 35 |
+// information to failed checks. See the Commentf function for details. |
|
| 36 |
+type CommentInterface interface {
|
|
| 37 |
+ CheckCommentString() string |
|
| 38 |
+} |
|
| 39 |
+ |
|
| 40 |
+func (c *comment) CheckCommentString() string {
|
|
| 41 |
+ return fmt.Sprintf(c.format, c.args...) |
|
| 42 |
+} |
|
| 43 |
+ |
|
| 44 |
+// ----------------------------------------------------------------------- |
|
| 45 |
+// The Checker interface. |
|
| 46 |
+ |
|
| 47 |
+// The Checker interface must be provided by checkers used with |
|
| 48 |
+// the Assert and Check verification methods. |
|
| 49 |
+type Checker interface {
|
|
| 50 |
+ Info() *CheckerInfo |
|
| 51 |
+ Check(params []interface{}, names []string) (result bool, error string)
|
|
| 52 |
+} |
|
| 53 |
+ |
|
| 54 |
+// See the Checker interface. |
|
| 55 |
+type CheckerInfo struct {
|
|
| 56 |
+ Name string |
|
| 57 |
+ Params []string |
|
| 58 |
+} |
|
| 59 |
+ |
|
| 60 |
+func (info *CheckerInfo) Info() *CheckerInfo {
|
|
| 61 |
+ return info |
|
| 62 |
+} |
|
| 63 |
+ |
|
| 64 |
+// ----------------------------------------------------------------------- |
|
| 65 |
+// Not checker logic inverter. |
|
| 66 |
+ |
|
| 67 |
+// The Not checker inverts the logic of the provided checker. The |
|
| 68 |
+// resulting checker will succeed where the original one failed, and |
|
| 69 |
+// vice-versa. |
|
| 70 |
+// |
|
| 71 |
+// For example: |
|
| 72 |
+// |
|
| 73 |
+// c.Assert(a, Not(Equals), b) |
|
| 74 |
+// |
|
| 75 |
+func Not(checker Checker) Checker {
|
|
| 76 |
+ return ¬Checker{checker}
|
|
| 77 |
+} |
|
| 78 |
+ |
|
| 79 |
+type notChecker struct {
|
|
| 80 |
+ sub Checker |
|
| 81 |
+} |
|
| 82 |
+ |
|
| 83 |
+func (checker *notChecker) Info() *CheckerInfo {
|
|
| 84 |
+ info := *checker.sub.Info() |
|
| 85 |
+ info.Name = "Not(" + info.Name + ")"
|
|
| 86 |
+ return &info |
|
| 87 |
+} |
|
| 88 |
+ |
|
| 89 |
+func (checker *notChecker) Check(params []interface{}, names []string) (result bool, error string) {
|
|
| 90 |
+ result, error = checker.sub.Check(params, names) |
|
| 91 |
+ result = !result |
|
| 92 |
+ return |
|
| 93 |
+} |
|
| 94 |
+ |
|
| 95 |
+// ----------------------------------------------------------------------- |
|
| 96 |
+// IsNil checker. |
|
| 97 |
+ |
|
| 98 |
+type isNilChecker struct {
|
|
| 99 |
+ *CheckerInfo |
|
| 100 |
+} |
|
| 101 |
+ |
|
| 102 |
+// The IsNil checker tests whether the obtained value is nil. |
|
| 103 |
+// |
|
| 104 |
+// For example: |
|
| 105 |
+// |
|
| 106 |
+// c.Assert(err, IsNil) |
|
| 107 |
+// |
|
| 108 |
+var IsNil Checker = &isNilChecker{
|
|
| 109 |
+ &CheckerInfo{Name: "IsNil", Params: []string{"value"}},
|
|
| 110 |
+} |
|
| 111 |
+ |
|
| 112 |
+func (checker *isNilChecker) Check(params []interface{}, names []string) (result bool, error string) {
|
|
| 113 |
+ return isNil(params[0]), "" |
|
| 114 |
+} |
|
| 115 |
+ |
|
| 116 |
+func isNil(obtained interface{}) (result bool) {
|
|
| 117 |
+ if obtained == nil {
|
|
| 118 |
+ result = true |
|
| 119 |
+ } else {
|
|
| 120 |
+ switch v := reflect.ValueOf(obtained); v.Kind() {
|
|
| 121 |
+ case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: |
|
| 122 |
+ return v.IsNil() |
|
| 123 |
+ } |
|
| 124 |
+ } |
|
| 125 |
+ return |
|
| 126 |
+} |
|
| 127 |
+ |
|
| 128 |
+// ----------------------------------------------------------------------- |
|
| 129 |
+// NotNil checker. Alias for Not(IsNil), since it's so common. |
|
| 130 |
+ |
|
| 131 |
+type notNilChecker struct {
|
|
| 132 |
+ *CheckerInfo |
|
| 133 |
+} |
|
| 134 |
+ |
|
| 135 |
+// The NotNil checker verifies that the obtained value is not nil. |
|
| 136 |
+// |
|
| 137 |
+// For example: |
|
| 138 |
+// |
|
| 139 |
+// c.Assert(iface, NotNil) |
|
| 140 |
+// |
|
| 141 |
+// This is an alias for Not(IsNil), made available since it's a |
|
| 142 |
+// fairly common check. |
|
| 143 |
+// |
|
| 144 |
+var NotNil Checker = ¬NilChecker{
|
|
| 145 |
+ &CheckerInfo{Name: "NotNil", Params: []string{"value"}},
|
|
| 146 |
+} |
|
| 147 |
+ |
|
| 148 |
+func (checker *notNilChecker) Check(params []interface{}, names []string) (result bool, error string) {
|
|
| 149 |
+ return !isNil(params[0]), "" |
|
| 150 |
+} |
|
| 151 |
+ |
|
| 152 |
+// ----------------------------------------------------------------------- |
|
| 153 |
+// Equals checker. |
|
| 154 |
+ |
|
| 155 |
+type equalsChecker struct {
|
|
| 156 |
+ *CheckerInfo |
|
| 157 |
+} |
|
| 158 |
+ |
|
| 159 |
+// The Equals checker verifies that the obtained value is equal to |
|
| 160 |
+// the expected value, according to usual Go semantics for ==. |
|
| 161 |
+// |
|
| 162 |
+// For example: |
|
| 163 |
+// |
|
| 164 |
+// c.Assert(value, Equals, 42) |
|
| 165 |
+// |
|
| 166 |
+var Equals Checker = &equalsChecker{
|
|
| 167 |
+ &CheckerInfo{Name: "Equals", Params: []string{"obtained", "expected"}},
|
|
| 168 |
+} |
|
| 169 |
+ |
|
| 170 |
+func (checker *equalsChecker) Check(params []interface{}, names []string) (result bool, error string) {
|
|
| 171 |
+ defer func() {
|
|
| 172 |
+ if v := recover(); v != nil {
|
|
| 173 |
+ result = false |
|
| 174 |
+ error = fmt.Sprint(v) |
|
| 175 |
+ } |
|
| 176 |
+ }() |
|
| 177 |
+ return params[0] == params[1], "" |
|
| 178 |
+} |
|
| 179 |
+ |
|
| 180 |
+// ----------------------------------------------------------------------- |
|
| 181 |
+// DeepEquals checker. |
|
| 182 |
+ |
|
| 183 |
+type deepEqualsChecker struct {
|
|
| 184 |
+ *CheckerInfo |
|
| 185 |
+} |
|
| 186 |
+ |
|
| 187 |
+// The DeepEquals checker verifies that the obtained value is deep-equal to |
|
| 188 |
+// the expected value. The check will work correctly even when facing |
|
| 189 |
+// slices, interfaces, and values of different types (which always fail |
|
| 190 |
+// the test). |
|
| 191 |
+// |
|
| 192 |
+// For example: |
|
| 193 |
+// |
|
| 194 |
+// c.Assert(value, DeepEquals, 42) |
|
| 195 |
+// c.Assert(array, DeepEquals, []string{"hi", "there"})
|
|
| 196 |
+// |
|
| 197 |
+var DeepEquals Checker = &deepEqualsChecker{
|
|
| 198 |
+ &CheckerInfo{Name: "DeepEquals", Params: []string{"obtained", "expected"}},
|
|
| 199 |
+} |
|
| 200 |
+ |
|
| 201 |
+func (checker *deepEqualsChecker) Check(params []interface{}, names []string) (result bool, error string) {
|
|
| 202 |
+ return reflect.DeepEqual(params[0], params[1]), "" |
|
| 203 |
+} |
|
| 204 |
+ |
|
| 205 |
+// ----------------------------------------------------------------------- |
|
| 206 |
+// HasLen checker. |
|
| 207 |
+ |
|
| 208 |
+type hasLenChecker struct {
|
|
| 209 |
+ *CheckerInfo |
|
| 210 |
+} |
|
| 211 |
+ |
|
| 212 |
+// The HasLen checker verifies that the obtained value has the |
|
| 213 |
+// provided length. In many cases this is superior to using Equals |
|
| 214 |
+// in conjuction with the len function because in case the check |
|
| 215 |
+// fails the value itself will be printed, instead of its length, |
|
| 216 |
+// providing more details for figuring the problem. |
|
| 217 |
+// |
|
| 218 |
+// For example: |
|
| 219 |
+// |
|
| 220 |
+// c.Assert(list, HasLen, 5) |
|
| 221 |
+// |
|
| 222 |
+var HasLen Checker = &hasLenChecker{
|
|
| 223 |
+ &CheckerInfo{Name: "HasLen", Params: []string{"obtained", "n"}},
|
|
| 224 |
+} |
|
| 225 |
+ |
|
| 226 |
+func (checker *hasLenChecker) Check(params []interface{}, names []string) (result bool, error string) {
|
|
| 227 |
+ n, ok := params[1].(int) |
|
| 228 |
+ if !ok {
|
|
| 229 |
+ return false, "n must be an int" |
|
| 230 |
+ } |
|
| 231 |
+ value := reflect.ValueOf(params[0]) |
|
| 232 |
+ switch value.Kind() {
|
|
| 233 |
+ case reflect.Map, reflect.Array, reflect.Slice, reflect.Chan, reflect.String: |
|
| 234 |
+ default: |
|
| 235 |
+ return false, "obtained value type has no length" |
|
| 236 |
+ } |
|
| 237 |
+ return value.Len() == n, "" |
|
| 238 |
+} |
|
| 239 |
+ |
|
| 240 |
+// ----------------------------------------------------------------------- |
|
| 241 |
+// ErrorMatches checker. |
|
| 242 |
+ |
|
| 243 |
+type errorMatchesChecker struct {
|
|
| 244 |
+ *CheckerInfo |
|
| 245 |
+} |
|
| 246 |
+ |
|
| 247 |
+// The ErrorMatches checker verifies that the error value |
|
| 248 |
+// is non nil and matches the regular expression provided. |
|
| 249 |
+// |
|
| 250 |
+// For example: |
|
| 251 |
+// |
|
| 252 |
+// c.Assert(err, ErrorMatches, "perm.*denied") |
|
| 253 |
+// |
|
| 254 |
+var ErrorMatches Checker = errorMatchesChecker{
|
|
| 255 |
+ &CheckerInfo{Name: "ErrorMatches", Params: []string{"value", "regex"}},
|
|
| 256 |
+} |
|
| 257 |
+ |
|
| 258 |
+func (checker errorMatchesChecker) Check(params []interface{}, names []string) (result bool, errStr string) {
|
|
| 259 |
+ if params[0] == nil {
|
|
| 260 |
+ return false, "Error value is nil" |
|
| 261 |
+ } |
|
| 262 |
+ err, ok := params[0].(error) |
|
| 263 |
+ if !ok {
|
|
| 264 |
+ return false, "Value is not an error" |
|
| 265 |
+ } |
|
| 266 |
+ params[0] = err.Error() |
|
| 267 |
+ names[0] = "error" |
|
| 268 |
+ return matches(params[0], params[1]) |
|
| 269 |
+} |
|
| 270 |
+ |
|
| 271 |
+// ----------------------------------------------------------------------- |
|
| 272 |
+// Matches checker. |
|
| 273 |
+ |
|
| 274 |
+type matchesChecker struct {
|
|
| 275 |
+ *CheckerInfo |
|
| 276 |
+} |
|
| 277 |
+ |
|
| 278 |
+// The Matches checker verifies that the string provided as the obtained |
|
| 279 |
+// value (or the string resulting from obtained.String()) matches the |
|
| 280 |
+// regular expression provided. |
|
| 281 |
+// |
|
| 282 |
+// For example: |
|
| 283 |
+// |
|
| 284 |
+// c.Assert(err, Matches, "perm.*denied") |
|
| 285 |
+// |
|
| 286 |
+var Matches Checker = &matchesChecker{
|
|
| 287 |
+ &CheckerInfo{Name: "Matches", Params: []string{"value", "regex"}},
|
|
| 288 |
+} |
|
| 289 |
+ |
|
| 290 |
+func (checker *matchesChecker) Check(params []interface{}, names []string) (result bool, error string) {
|
|
| 291 |
+ return matches(params[0], params[1]) |
|
| 292 |
+} |
|
| 293 |
+ |
|
| 294 |
+func matches(value, regex interface{}) (result bool, error string) {
|
|
| 295 |
+ reStr, ok := regex.(string) |
|
| 296 |
+ if !ok {
|
|
| 297 |
+ return false, "Regex must be a string" |
|
| 298 |
+ } |
|
| 299 |
+ valueStr, valueIsStr := value.(string) |
|
| 300 |
+ if !valueIsStr {
|
|
| 301 |
+ if valueWithStr, valueHasStr := value.(fmt.Stringer); valueHasStr {
|
|
| 302 |
+ valueStr, valueIsStr = valueWithStr.String(), true |
|
| 303 |
+ } |
|
| 304 |
+ } |
|
| 305 |
+ if valueIsStr {
|
|
| 306 |
+ matches, err := regexp.MatchString("^"+reStr+"$", valueStr)
|
|
| 307 |
+ if err != nil {
|
|
| 308 |
+ return false, "Can't compile regex: " + err.Error() |
|
| 309 |
+ } |
|
| 310 |
+ return matches, "" |
|
| 311 |
+ } |
|
| 312 |
+ return false, "Obtained value is not a string and has no .String()" |
|
| 313 |
+} |
|
| 314 |
+ |
|
| 315 |
+// ----------------------------------------------------------------------- |
|
| 316 |
+// Panics checker. |
|
| 317 |
+ |
|
| 318 |
+type panicsChecker struct {
|
|
| 319 |
+ *CheckerInfo |
|
| 320 |
+} |
|
| 321 |
+ |
|
| 322 |
+// The Panics checker verifies that calling the provided zero-argument |
|
| 323 |
+// function will cause a panic which is deep-equal to the provided value. |
|
| 324 |
+// |
|
| 325 |
+// For example: |
|
| 326 |
+// |
|
| 327 |
+// c.Assert(func() { f(1, 2) }, Panics, &SomeErrorType{"BOOM"}).
|
|
| 328 |
+// |
|
| 329 |
+// |
|
| 330 |
+var Panics Checker = &panicsChecker{
|
|
| 331 |
+ &CheckerInfo{Name: "Panics", Params: []string{"function", "expected"}},
|
|
| 332 |
+} |
|
| 333 |
+ |
|
| 334 |
+func (checker *panicsChecker) Check(params []interface{}, names []string) (result bool, error string) {
|
|
| 335 |
+ f := reflect.ValueOf(params[0]) |
|
| 336 |
+ if f.Kind() != reflect.Func || f.Type().NumIn() != 0 {
|
|
| 337 |
+ return false, "Function must take zero arguments" |
|
| 338 |
+ } |
|
| 339 |
+ defer func() {
|
|
| 340 |
+ // If the function has not panicked, then don't do the check. |
|
| 341 |
+ if error != "" {
|
|
| 342 |
+ return |
|
| 343 |
+ } |
|
| 344 |
+ params[0] = recover() |
|
| 345 |
+ names[0] = "panic" |
|
| 346 |
+ result = reflect.DeepEqual(params[0], params[1]) |
|
| 347 |
+ }() |
|
| 348 |
+ f.Call(nil) |
|
| 349 |
+ return false, "Function has not panicked" |
|
| 350 |
+} |
|
| 351 |
+ |
|
| 352 |
+type panicMatchesChecker struct {
|
|
| 353 |
+ *CheckerInfo |
|
| 354 |
+} |
|
| 355 |
+ |
|
| 356 |
+// The PanicMatches checker verifies that calling the provided zero-argument |
|
| 357 |
+// function will cause a panic with an error value matching |
|
| 358 |
+// the regular expression provided. |
|
| 359 |
+// |
|
| 360 |
+// For example: |
|
| 361 |
+// |
|
| 362 |
+// c.Assert(func() { f(1, 2) }, PanicMatches, `open.*: no such file or directory`).
|
|
| 363 |
+// |
|
| 364 |
+// |
|
| 365 |
+var PanicMatches Checker = &panicMatchesChecker{
|
|
| 366 |
+ &CheckerInfo{Name: "PanicMatches", Params: []string{"function", "expected"}},
|
|
| 367 |
+} |
|
| 368 |
+ |
|
| 369 |
+func (checker *panicMatchesChecker) Check(params []interface{}, names []string) (result bool, errmsg string) {
|
|
| 370 |
+ f := reflect.ValueOf(params[0]) |
|
| 371 |
+ if f.Kind() != reflect.Func || f.Type().NumIn() != 0 {
|
|
| 372 |
+ return false, "Function must take zero arguments" |
|
| 373 |
+ } |
|
| 374 |
+ defer func() {
|
|
| 375 |
+ // If the function has not panicked, then don't do the check. |
|
| 376 |
+ if errmsg != "" {
|
|
| 377 |
+ return |
|
| 378 |
+ } |
|
| 379 |
+ obtained := recover() |
|
| 380 |
+ names[0] = "panic" |
|
| 381 |
+ if e, ok := obtained.(error); ok {
|
|
| 382 |
+ params[0] = e.Error() |
|
| 383 |
+ } else if _, ok := obtained.(string); ok {
|
|
| 384 |
+ params[0] = obtained |
|
| 385 |
+ } else {
|
|
| 386 |
+ errmsg = "Panic value is not a string or an error" |
|
| 387 |
+ return |
|
| 388 |
+ } |
|
| 389 |
+ result, errmsg = matches(params[0], params[1]) |
|
| 390 |
+ }() |
|
| 391 |
+ f.Call(nil) |
|
| 392 |
+ return false, "Function has not panicked" |
|
| 393 |
+} |
|
| 394 |
+ |
|
| 395 |
+// ----------------------------------------------------------------------- |
|
| 396 |
+// FitsTypeOf checker. |
|
| 397 |
+ |
|
| 398 |
+type fitsTypeChecker struct {
|
|
| 399 |
+ *CheckerInfo |
|
| 400 |
+} |
|
| 401 |
+ |
|
| 402 |
+// The FitsTypeOf checker verifies that the obtained value is |
|
| 403 |
+// assignable to a variable with the same type as the provided |
|
| 404 |
+// sample value. |
|
| 405 |
+// |
|
| 406 |
+// For example: |
|
| 407 |
+// |
|
| 408 |
+// c.Assert(value, FitsTypeOf, int64(0)) |
|
| 409 |
+// c.Assert(value, FitsTypeOf, os.Error(nil)) |
|
| 410 |
+// |
|
| 411 |
+var FitsTypeOf Checker = &fitsTypeChecker{
|
|
| 412 |
+ &CheckerInfo{Name: "FitsTypeOf", Params: []string{"obtained", "sample"}},
|
|
| 413 |
+} |
|
| 414 |
+ |
|
| 415 |
+func (checker *fitsTypeChecker) Check(params []interface{}, names []string) (result bool, error string) {
|
|
| 416 |
+ obtained := reflect.ValueOf(params[0]) |
|
| 417 |
+ sample := reflect.ValueOf(params[1]) |
|
| 418 |
+ if !obtained.IsValid() {
|
|
| 419 |
+ return false, "" |
|
| 420 |
+ } |
|
| 421 |
+ if !sample.IsValid() {
|
|
| 422 |
+ return false, "Invalid sample value" |
|
| 423 |
+ } |
|
| 424 |
+ return obtained.Type().AssignableTo(sample.Type()), "" |
|
| 425 |
+} |
|
| 426 |
+ |
|
| 427 |
+// ----------------------------------------------------------------------- |
|
| 428 |
+// Implements checker. |
|
| 429 |
+ |
|
| 430 |
+type implementsChecker struct {
|
|
| 431 |
+ *CheckerInfo |
|
| 432 |
+} |
|
| 433 |
+ |
|
| 434 |
+// The Implements checker verifies that the obtained value |
|
| 435 |
+// implements the interface specified via a pointer to an interface |
|
| 436 |
+// variable. |
|
| 437 |
+// |
|
| 438 |
+// For example: |
|
| 439 |
+// |
|
| 440 |
+// var e os.Error |
|
| 441 |
+// c.Assert(err, Implements, &e) |
|
| 442 |
+// |
|
| 443 |
+var Implements Checker = &implementsChecker{
|
|
| 444 |
+ &CheckerInfo{Name: "Implements", Params: []string{"obtained", "ifaceptr"}},
|
|
| 445 |
+} |
|
| 446 |
+ |
|
| 447 |
+func (checker *implementsChecker) Check(params []interface{}, names []string) (result bool, error string) {
|
|
| 448 |
+ obtained := reflect.ValueOf(params[0]) |
|
| 449 |
+ ifaceptr := reflect.ValueOf(params[1]) |
|
| 450 |
+ if !obtained.IsValid() {
|
|
| 451 |
+ return false, "" |
|
| 452 |
+ } |
|
| 453 |
+ if !ifaceptr.IsValid() || ifaceptr.Kind() != reflect.Ptr || ifaceptr.Elem().Kind() != reflect.Interface {
|
|
| 454 |
+ return false, "ifaceptr should be a pointer to an interface variable" |
|
| 455 |
+ } |
|
| 456 |
+ return obtained.Type().Implements(ifaceptr.Elem().Type()), "" |
|
| 457 |
+} |
| 0 | 458 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,272 @@ |
| 0 |
+package check_test |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "errors" |
|
| 4 |
+ "gopkg.in/check.v1" |
|
| 5 |
+ "reflect" |
|
| 6 |
+ "runtime" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+type CheckersS struct{}
|
|
| 10 |
+ |
|
| 11 |
+var _ = check.Suite(&CheckersS{})
|
|
| 12 |
+ |
|
| 13 |
+func testInfo(c *check.C, checker check.Checker, name string, paramNames []string) {
|
|
| 14 |
+ info := checker.Info() |
|
| 15 |
+ if info.Name != name {
|
|
| 16 |
+ c.Fatalf("Got name %s, expected %s", info.Name, name)
|
|
| 17 |
+ } |
|
| 18 |
+ if !reflect.DeepEqual(info.Params, paramNames) {
|
|
| 19 |
+ c.Fatalf("Got param names %#v, expected %#v", info.Params, paramNames)
|
|
| 20 |
+ } |
|
| 21 |
+} |
|
| 22 |
+ |
|
| 23 |
+func testCheck(c *check.C, checker check.Checker, result bool, error string, params ...interface{}) ([]interface{}, []string) {
|
|
| 24 |
+ info := checker.Info() |
|
| 25 |
+ if len(params) != len(info.Params) {
|
|
| 26 |
+ c.Fatalf("unexpected param count in test; expected %d got %d", len(info.Params), len(params))
|
|
| 27 |
+ } |
|
| 28 |
+ names := append([]string{}, info.Params...)
|
|
| 29 |
+ result_, error_ := checker.Check(params, names) |
|
| 30 |
+ if result_ != result || error_ != error {
|
|
| 31 |
+ c.Fatalf("%s.Check(%#v) returned (%#v, %#v) rather than (%#v, %#v)",
|
|
| 32 |
+ info.Name, params, result_, error_, result, error) |
|
| 33 |
+ } |
|
| 34 |
+ return params, names |
|
| 35 |
+} |
|
| 36 |
+ |
|
| 37 |
+func (s *CheckersS) TestComment(c *check.C) {
|
|
| 38 |
+ bug := check.Commentf("a %d bc", 42)
|
|
| 39 |
+ comment := bug.CheckCommentString() |
|
| 40 |
+ if comment != "a 42 bc" {
|
|
| 41 |
+ c.Fatalf("Commentf returned %#v", comment)
|
|
| 42 |
+ } |
|
| 43 |
+} |
|
| 44 |
+ |
|
| 45 |
+func (s *CheckersS) TestIsNil(c *check.C) {
|
|
| 46 |
+ testInfo(c, check.IsNil, "IsNil", []string{"value"})
|
|
| 47 |
+ |
|
| 48 |
+ testCheck(c, check.IsNil, true, "", nil) |
|
| 49 |
+ testCheck(c, check.IsNil, false, "", "a") |
|
| 50 |
+ |
|
| 51 |
+ testCheck(c, check.IsNil, true, "", (chan int)(nil)) |
|
| 52 |
+ testCheck(c, check.IsNil, false, "", make(chan int)) |
|
| 53 |
+ testCheck(c, check.IsNil, true, "", (error)(nil)) |
|
| 54 |
+ testCheck(c, check.IsNil, false, "", errors.New(""))
|
|
| 55 |
+ testCheck(c, check.IsNil, true, "", ([]int)(nil)) |
|
| 56 |
+ testCheck(c, check.IsNil, false, "", make([]int, 1)) |
|
| 57 |
+ testCheck(c, check.IsNil, false, "", int(0)) |
|
| 58 |
+} |
|
| 59 |
+ |
|
| 60 |
+func (s *CheckersS) TestNotNil(c *check.C) {
|
|
| 61 |
+ testInfo(c, check.NotNil, "NotNil", []string{"value"})
|
|
| 62 |
+ |
|
| 63 |
+ testCheck(c, check.NotNil, false, "", nil) |
|
| 64 |
+ testCheck(c, check.NotNil, true, "", "a") |
|
| 65 |
+ |
|
| 66 |
+ testCheck(c, check.NotNil, false, "", (chan int)(nil)) |
|
| 67 |
+ testCheck(c, check.NotNil, true, "", make(chan int)) |
|
| 68 |
+ testCheck(c, check.NotNil, false, "", (error)(nil)) |
|
| 69 |
+ testCheck(c, check.NotNil, true, "", errors.New(""))
|
|
| 70 |
+ testCheck(c, check.NotNil, false, "", ([]int)(nil)) |
|
| 71 |
+ testCheck(c, check.NotNil, true, "", make([]int, 1)) |
|
| 72 |
+} |
|
| 73 |
+ |
|
| 74 |
+func (s *CheckersS) TestNot(c *check.C) {
|
|
| 75 |
+ testInfo(c, check.Not(check.IsNil), "Not(IsNil)", []string{"value"})
|
|
| 76 |
+ |
|
| 77 |
+ testCheck(c, check.Not(check.IsNil), false, "", nil) |
|
| 78 |
+ testCheck(c, check.Not(check.IsNil), true, "", "a") |
|
| 79 |
+} |
|
| 80 |
+ |
|
| 81 |
+type simpleStruct struct {
|
|
| 82 |
+ i int |
|
| 83 |
+} |
|
| 84 |
+ |
|
| 85 |
+func (s *CheckersS) TestEquals(c *check.C) {
|
|
| 86 |
+ testInfo(c, check.Equals, "Equals", []string{"obtained", "expected"})
|
|
| 87 |
+ |
|
| 88 |
+ // The simplest. |
|
| 89 |
+ testCheck(c, check.Equals, true, "", 42, 42) |
|
| 90 |
+ testCheck(c, check.Equals, false, "", 42, 43) |
|
| 91 |
+ |
|
| 92 |
+ // Different native types. |
|
| 93 |
+ testCheck(c, check.Equals, false, "", int32(42), int64(42)) |
|
| 94 |
+ |
|
| 95 |
+ // With nil. |
|
| 96 |
+ testCheck(c, check.Equals, false, "", 42, nil) |
|
| 97 |
+ |
|
| 98 |
+ // Slices |
|
| 99 |
+ testCheck(c, check.Equals, false, "runtime error: comparing uncomparable type []uint8", []byte{1, 2}, []byte{1, 2})
|
|
| 100 |
+ |
|
| 101 |
+ // Struct values |
|
| 102 |
+ testCheck(c, check.Equals, true, "", simpleStruct{1}, simpleStruct{1})
|
|
| 103 |
+ testCheck(c, check.Equals, false, "", simpleStruct{1}, simpleStruct{2})
|
|
| 104 |
+ |
|
| 105 |
+ // Struct pointers |
|
| 106 |
+ testCheck(c, check.Equals, false, "", &simpleStruct{1}, &simpleStruct{1})
|
|
| 107 |
+ testCheck(c, check.Equals, false, "", &simpleStruct{1}, &simpleStruct{2})
|
|
| 108 |
+} |
|
| 109 |
+ |
|
| 110 |
+func (s *CheckersS) TestDeepEquals(c *check.C) {
|
|
| 111 |
+ testInfo(c, check.DeepEquals, "DeepEquals", []string{"obtained", "expected"})
|
|
| 112 |
+ |
|
| 113 |
+ // The simplest. |
|
| 114 |
+ testCheck(c, check.DeepEquals, true, "", 42, 42) |
|
| 115 |
+ testCheck(c, check.DeepEquals, false, "", 42, 43) |
|
| 116 |
+ |
|
| 117 |
+ // Different native types. |
|
| 118 |
+ testCheck(c, check.DeepEquals, false, "", int32(42), int64(42)) |
|
| 119 |
+ |
|
| 120 |
+ // With nil. |
|
| 121 |
+ testCheck(c, check.DeepEquals, false, "", 42, nil) |
|
| 122 |
+ |
|
| 123 |
+ // Slices |
|
| 124 |
+ testCheck(c, check.DeepEquals, true, "", []byte{1, 2}, []byte{1, 2})
|
|
| 125 |
+ testCheck(c, check.DeepEquals, false, "", []byte{1, 2}, []byte{1, 3})
|
|
| 126 |
+ |
|
| 127 |
+ // Struct values |
|
| 128 |
+ testCheck(c, check.DeepEquals, true, "", simpleStruct{1}, simpleStruct{1})
|
|
| 129 |
+ testCheck(c, check.DeepEquals, false, "", simpleStruct{1}, simpleStruct{2})
|
|
| 130 |
+ |
|
| 131 |
+ // Struct pointers |
|
| 132 |
+ testCheck(c, check.DeepEquals, true, "", &simpleStruct{1}, &simpleStruct{1})
|
|
| 133 |
+ testCheck(c, check.DeepEquals, false, "", &simpleStruct{1}, &simpleStruct{2})
|
|
| 134 |
+} |
|
| 135 |
+ |
|
| 136 |
+func (s *CheckersS) TestHasLen(c *check.C) {
|
|
| 137 |
+ testInfo(c, check.HasLen, "HasLen", []string{"obtained", "n"})
|
|
| 138 |
+ |
|
| 139 |
+ testCheck(c, check.HasLen, true, "", "abcd", 4) |
|
| 140 |
+ testCheck(c, check.HasLen, true, "", []int{1, 2}, 2)
|
|
| 141 |
+ testCheck(c, check.HasLen, false, "", []int{1, 2}, 3)
|
|
| 142 |
+ |
|
| 143 |
+ testCheck(c, check.HasLen, false, "n must be an int", []int{1, 2}, "2")
|
|
| 144 |
+ testCheck(c, check.HasLen, false, "obtained value type has no length", nil, 2) |
|
| 145 |
+} |
|
| 146 |
+ |
|
| 147 |
+func (s *CheckersS) TestErrorMatches(c *check.C) {
|
|
| 148 |
+ testInfo(c, check.ErrorMatches, "ErrorMatches", []string{"value", "regex"})
|
|
| 149 |
+ |
|
| 150 |
+ testCheck(c, check.ErrorMatches, false, "Error value is nil", nil, "some error") |
|
| 151 |
+ testCheck(c, check.ErrorMatches, false, "Value is not an error", 1, "some error") |
|
| 152 |
+ testCheck(c, check.ErrorMatches, true, "", errors.New("some error"), "some error")
|
|
| 153 |
+ testCheck(c, check.ErrorMatches, true, "", errors.New("some error"), "so.*or")
|
|
| 154 |
+ |
|
| 155 |
+ // Verify params mutation |
|
| 156 |
+ params, names := testCheck(c, check.ErrorMatches, false, "", errors.New("some error"), "other error")
|
|
| 157 |
+ c.Assert(params[0], check.Equals, "some error") |
|
| 158 |
+ c.Assert(names[0], check.Equals, "error") |
|
| 159 |
+} |
|
| 160 |
+ |
|
| 161 |
+func (s *CheckersS) TestMatches(c *check.C) {
|
|
| 162 |
+ testInfo(c, check.Matches, "Matches", []string{"value", "regex"})
|
|
| 163 |
+ |
|
| 164 |
+ // Simple matching |
|
| 165 |
+ testCheck(c, check.Matches, true, "", "abc", "abc") |
|
| 166 |
+ testCheck(c, check.Matches, true, "", "abc", "a.c") |
|
| 167 |
+ |
|
| 168 |
+ // Must match fully |
|
| 169 |
+ testCheck(c, check.Matches, false, "", "abc", "ab") |
|
| 170 |
+ testCheck(c, check.Matches, false, "", "abc", "bc") |
|
| 171 |
+ |
|
| 172 |
+ // String()-enabled values accepted |
|
| 173 |
+ testCheck(c, check.Matches, true, "", reflect.ValueOf("abc"), "a.c")
|
|
| 174 |
+ testCheck(c, check.Matches, false, "", reflect.ValueOf("abc"), "a.d")
|
|
| 175 |
+ |
|
| 176 |
+ // Some error conditions. |
|
| 177 |
+ testCheck(c, check.Matches, false, "Obtained value is not a string and has no .String()", 1, "a.c") |
|
| 178 |
+ testCheck(c, check.Matches, false, "Can't compile regex: error parsing regexp: missing closing ]: `[c$`", "abc", "a[c") |
|
| 179 |
+} |
|
| 180 |
+ |
|
| 181 |
+func (s *CheckersS) TestPanics(c *check.C) {
|
|
| 182 |
+ testInfo(c, check.Panics, "Panics", []string{"function", "expected"})
|
|
| 183 |
+ |
|
| 184 |
+ // Some errors. |
|
| 185 |
+ testCheck(c, check.Panics, false, "Function has not panicked", func() bool { return false }, "BOOM")
|
|
| 186 |
+ testCheck(c, check.Panics, false, "Function must take zero arguments", 1, "BOOM") |
|
| 187 |
+ |
|
| 188 |
+ // Plain strings. |
|
| 189 |
+ testCheck(c, check.Panics, true, "", func() { panic("BOOM") }, "BOOM")
|
|
| 190 |
+ testCheck(c, check.Panics, false, "", func() { panic("KABOOM") }, "BOOM")
|
|
| 191 |
+ testCheck(c, check.Panics, true, "", func() bool { panic("BOOM") }, "BOOM")
|
|
| 192 |
+ |
|
| 193 |
+ // Error values. |
|
| 194 |
+ testCheck(c, check.Panics, true, "", func() { panic(errors.New("BOOM")) }, errors.New("BOOM"))
|
|
| 195 |
+ testCheck(c, check.Panics, false, "", func() { panic(errors.New("KABOOM")) }, errors.New("BOOM"))
|
|
| 196 |
+ |
|
| 197 |
+ type deep struct{ i int }
|
|
| 198 |
+ // Deep value |
|
| 199 |
+ testCheck(c, check.Panics, true, "", func() { panic(&deep{99}) }, &deep{99})
|
|
| 200 |
+ |
|
| 201 |
+ // Verify params/names mutation |
|
| 202 |
+ params, names := testCheck(c, check.Panics, false, "", func() { panic(errors.New("KABOOM")) }, errors.New("BOOM"))
|
|
| 203 |
+ c.Assert(params[0], check.ErrorMatches, "KABOOM") |
|
| 204 |
+ c.Assert(names[0], check.Equals, "panic") |
|
| 205 |
+ |
|
| 206 |
+ // Verify a nil panic |
|
| 207 |
+ testCheck(c, check.Panics, true, "", func() { panic(nil) }, nil)
|
|
| 208 |
+ testCheck(c, check.Panics, false, "", func() { panic(nil) }, "NOPE")
|
|
| 209 |
+} |
|
| 210 |
+ |
|
| 211 |
+func (s *CheckersS) TestPanicMatches(c *check.C) {
|
|
| 212 |
+ testInfo(c, check.PanicMatches, "PanicMatches", []string{"function", "expected"})
|
|
| 213 |
+ |
|
| 214 |
+ // Error matching. |
|
| 215 |
+ testCheck(c, check.PanicMatches, true, "", func() { panic(errors.New("BOOM")) }, "BO.M")
|
|
| 216 |
+ testCheck(c, check.PanicMatches, false, "", func() { panic(errors.New("KABOOM")) }, "BO.M")
|
|
| 217 |
+ |
|
| 218 |
+ // Some errors. |
|
| 219 |
+ testCheck(c, check.PanicMatches, false, "Function has not panicked", func() bool { return false }, "BOOM")
|
|
| 220 |
+ testCheck(c, check.PanicMatches, false, "Function must take zero arguments", 1, "BOOM") |
|
| 221 |
+ |
|
| 222 |
+ // Plain strings. |
|
| 223 |
+ testCheck(c, check.PanicMatches, true, "", func() { panic("BOOM") }, "BO.M")
|
|
| 224 |
+ testCheck(c, check.PanicMatches, false, "", func() { panic("KABOOM") }, "BOOM")
|
|
| 225 |
+ testCheck(c, check.PanicMatches, true, "", func() bool { panic("BOOM") }, "BO.M")
|
|
| 226 |
+ |
|
| 227 |
+ // Verify params/names mutation |
|
| 228 |
+ params, names := testCheck(c, check.PanicMatches, false, "", func() { panic(errors.New("KABOOM")) }, "BOOM")
|
|
| 229 |
+ c.Assert(params[0], check.Equals, "KABOOM") |
|
| 230 |
+ c.Assert(names[0], check.Equals, "panic") |
|
| 231 |
+ |
|
| 232 |
+ // Verify a nil panic |
|
| 233 |
+ testCheck(c, check.PanicMatches, false, "Panic value is not a string or an error", func() { panic(nil) }, "")
|
|
| 234 |
+} |
|
| 235 |
+ |
|
| 236 |
+func (s *CheckersS) TestFitsTypeOf(c *check.C) {
|
|
| 237 |
+ testInfo(c, check.FitsTypeOf, "FitsTypeOf", []string{"obtained", "sample"})
|
|
| 238 |
+ |
|
| 239 |
+ // Basic types |
|
| 240 |
+ testCheck(c, check.FitsTypeOf, true, "", 1, 0) |
|
| 241 |
+ testCheck(c, check.FitsTypeOf, false, "", 1, int64(0)) |
|
| 242 |
+ |
|
| 243 |
+ // Aliases |
|
| 244 |
+ testCheck(c, check.FitsTypeOf, false, "", 1, errors.New(""))
|
|
| 245 |
+ testCheck(c, check.FitsTypeOf, false, "", "error", errors.New(""))
|
|
| 246 |
+ testCheck(c, check.FitsTypeOf, true, "", errors.New("error"), errors.New(""))
|
|
| 247 |
+ |
|
| 248 |
+ // Structures |
|
| 249 |
+ testCheck(c, check.FitsTypeOf, false, "", 1, simpleStruct{})
|
|
| 250 |
+ testCheck(c, check.FitsTypeOf, false, "", simpleStruct{42}, &simpleStruct{})
|
|
| 251 |
+ testCheck(c, check.FitsTypeOf, true, "", simpleStruct{42}, simpleStruct{})
|
|
| 252 |
+ testCheck(c, check.FitsTypeOf, true, "", &simpleStruct{42}, &simpleStruct{})
|
|
| 253 |
+ |
|
| 254 |
+ // Some bad values |
|
| 255 |
+ testCheck(c, check.FitsTypeOf, false, "Invalid sample value", 1, interface{}(nil))
|
|
| 256 |
+ testCheck(c, check.FitsTypeOf, false, "", interface{}(nil), 0)
|
|
| 257 |
+} |
|
| 258 |
+ |
|
| 259 |
+func (s *CheckersS) TestImplements(c *check.C) {
|
|
| 260 |
+ testInfo(c, check.Implements, "Implements", []string{"obtained", "ifaceptr"})
|
|
| 261 |
+ |
|
| 262 |
+ var e error |
|
| 263 |
+ var re runtime.Error |
|
| 264 |
+ testCheck(c, check.Implements, true, "", errors.New(""), &e)
|
|
| 265 |
+ testCheck(c, check.Implements, false, "", errors.New(""), &re)
|
|
| 266 |
+ |
|
| 267 |
+ // Some bad values |
|
| 268 |
+ testCheck(c, check.Implements, false, "ifaceptr should be a pointer to an interface variable", 0, errors.New(""))
|
|
| 269 |
+ testCheck(c, check.Implements, false, "ifaceptr should be a pointer to an interface variable", 0, interface{}(nil))
|
|
| 270 |
+ testCheck(c, check.Implements, false, "", interface{}(nil), &e)
|
|
| 271 |
+} |
| 0 | 9 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,484 @@ |
| 0 |
+// Tests for the behavior of the test fixture system. |
|
| 1 |
+ |
|
| 2 |
+package check_test |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ . "gopkg.in/check.v1" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+// ----------------------------------------------------------------------- |
|
| 9 |
+// Fixture test suite. |
|
| 10 |
+ |
|
| 11 |
+type FixtureS struct{}
|
|
| 12 |
+ |
|
| 13 |
+var fixtureS = Suite(&FixtureS{})
|
|
| 14 |
+ |
|
| 15 |
+func (s *FixtureS) TestCountSuite(c *C) {
|
|
| 16 |
+ suitesRun += 1 |
|
| 17 |
+} |
|
| 18 |
+ |
|
| 19 |
+// ----------------------------------------------------------------------- |
|
| 20 |
+// Basic fixture ordering verification. |
|
| 21 |
+ |
|
| 22 |
+func (s *FixtureS) TestOrder(c *C) {
|
|
| 23 |
+ helper := FixtureHelper{}
|
|
| 24 |
+ Run(&helper, nil) |
|
| 25 |
+ c.Check(helper.calls[0], Equals, "SetUpSuite") |
|
| 26 |
+ c.Check(helper.calls[1], Equals, "SetUpTest") |
|
| 27 |
+ c.Check(helper.calls[2], Equals, "Test1") |
|
| 28 |
+ c.Check(helper.calls[3], Equals, "TearDownTest") |
|
| 29 |
+ c.Check(helper.calls[4], Equals, "SetUpTest") |
|
| 30 |
+ c.Check(helper.calls[5], Equals, "Test2") |
|
| 31 |
+ c.Check(helper.calls[6], Equals, "TearDownTest") |
|
| 32 |
+ c.Check(helper.calls[7], Equals, "TearDownSuite") |
|
| 33 |
+ c.Check(len(helper.calls), Equals, 8) |
|
| 34 |
+} |
|
| 35 |
+ |
|
| 36 |
+// ----------------------------------------------------------------------- |
|
| 37 |
+// Check the behavior when panics occur within tests and fixtures. |
|
| 38 |
+ |
|
| 39 |
+func (s *FixtureS) TestPanicOnTest(c *C) {
|
|
| 40 |
+ helper := FixtureHelper{panicOn: "Test1"}
|
|
| 41 |
+ output := String{}
|
|
| 42 |
+ Run(&helper, &RunConf{Output: &output})
|
|
| 43 |
+ c.Check(helper.calls[0], Equals, "SetUpSuite") |
|
| 44 |
+ c.Check(helper.calls[1], Equals, "SetUpTest") |
|
| 45 |
+ c.Check(helper.calls[2], Equals, "Test1") |
|
| 46 |
+ c.Check(helper.calls[3], Equals, "TearDownTest") |
|
| 47 |
+ c.Check(helper.calls[4], Equals, "SetUpTest") |
|
| 48 |
+ c.Check(helper.calls[5], Equals, "Test2") |
|
| 49 |
+ c.Check(helper.calls[6], Equals, "TearDownTest") |
|
| 50 |
+ c.Check(helper.calls[7], Equals, "TearDownSuite") |
|
| 51 |
+ c.Check(len(helper.calls), Equals, 8) |
|
| 52 |
+ |
|
| 53 |
+ expected := "^\n-+\n" + |
|
| 54 |
+ "PANIC: check_test\\.go:[0-9]+: FixtureHelper.Test1\n\n" + |
|
| 55 |
+ "\\.\\.\\. Panic: Test1 \\(PC=[xA-F0-9]+\\)\n\n" + |
|
| 56 |
+ ".+:[0-9]+\n" + |
|
| 57 |
+ " in (go)?panic\n" + |
|
| 58 |
+ ".*check_test.go:[0-9]+\n" + |
|
| 59 |
+ " in FixtureHelper.trace\n" + |
|
| 60 |
+ ".*check_test.go:[0-9]+\n" + |
|
| 61 |
+ " in FixtureHelper.Test1\n" + |
|
| 62 |
+ "(.|\n)*$" |
|
| 63 |
+ |
|
| 64 |
+ c.Check(output.value, Matches, expected) |
|
| 65 |
+} |
|
| 66 |
+ |
|
| 67 |
+func (s *FixtureS) TestPanicOnSetUpTest(c *C) {
|
|
| 68 |
+ helper := FixtureHelper{panicOn: "SetUpTest"}
|
|
| 69 |
+ output := String{}
|
|
| 70 |
+ Run(&helper, &RunConf{Output: &output})
|
|
| 71 |
+ c.Check(helper.calls[0], Equals, "SetUpSuite") |
|
| 72 |
+ c.Check(helper.calls[1], Equals, "SetUpTest") |
|
| 73 |
+ c.Check(helper.calls[2], Equals, "TearDownTest") |
|
| 74 |
+ c.Check(helper.calls[3], Equals, "TearDownSuite") |
|
| 75 |
+ c.Check(len(helper.calls), Equals, 4) |
|
| 76 |
+ |
|
| 77 |
+ expected := "^\n-+\n" + |
|
| 78 |
+ "PANIC: check_test\\.go:[0-9]+: " + |
|
| 79 |
+ "FixtureHelper\\.SetUpTest\n\n" + |
|
| 80 |
+ "\\.\\.\\. Panic: SetUpTest \\(PC=[xA-F0-9]+\\)\n\n" + |
|
| 81 |
+ ".+:[0-9]+\n" + |
|
| 82 |
+ " in (go)?panic\n" + |
|
| 83 |
+ ".*check_test.go:[0-9]+\n" + |
|
| 84 |
+ " in FixtureHelper.trace\n" + |
|
| 85 |
+ ".*check_test.go:[0-9]+\n" + |
|
| 86 |
+ " in FixtureHelper.SetUpTest\n" + |
|
| 87 |
+ "(.|\n)*" + |
|
| 88 |
+ "\n-+\n" + |
|
| 89 |
+ "PANIC: check_test\\.go:[0-9]+: " + |
|
| 90 |
+ "FixtureHelper\\.Test1\n\n" + |
|
| 91 |
+ "\\.\\.\\. Panic: Fixture has panicked " + |
|
| 92 |
+ "\\(see related PANIC\\)\n$" |
|
| 93 |
+ |
|
| 94 |
+ c.Check(output.value, Matches, expected) |
|
| 95 |
+} |
|
| 96 |
+ |
|
| 97 |
+func (s *FixtureS) TestPanicOnTearDownTest(c *C) {
|
|
| 98 |
+ helper := FixtureHelper{panicOn: "TearDownTest"}
|
|
| 99 |
+ output := String{}
|
|
| 100 |
+ Run(&helper, &RunConf{Output: &output})
|
|
| 101 |
+ c.Check(helper.calls[0], Equals, "SetUpSuite") |
|
| 102 |
+ c.Check(helper.calls[1], Equals, "SetUpTest") |
|
| 103 |
+ c.Check(helper.calls[2], Equals, "Test1") |
|
| 104 |
+ c.Check(helper.calls[3], Equals, "TearDownTest") |
|
| 105 |
+ c.Check(helper.calls[4], Equals, "TearDownSuite") |
|
| 106 |
+ c.Check(len(helper.calls), Equals, 5) |
|
| 107 |
+ |
|
| 108 |
+ expected := "^\n-+\n" + |
|
| 109 |
+ "PANIC: check_test\\.go:[0-9]+: " + |
|
| 110 |
+ "FixtureHelper.TearDownTest\n\n" + |
|
| 111 |
+ "\\.\\.\\. Panic: TearDownTest \\(PC=[xA-F0-9]+\\)\n\n" + |
|
| 112 |
+ ".+:[0-9]+\n" + |
|
| 113 |
+ " in (go)?panic\n" + |
|
| 114 |
+ ".*check_test.go:[0-9]+\n" + |
|
| 115 |
+ " in FixtureHelper.trace\n" + |
|
| 116 |
+ ".*check_test.go:[0-9]+\n" + |
|
| 117 |
+ " in FixtureHelper.TearDownTest\n" + |
|
| 118 |
+ "(.|\n)*" + |
|
| 119 |
+ "\n-+\n" + |
|
| 120 |
+ "PANIC: check_test\\.go:[0-9]+: " + |
|
| 121 |
+ "FixtureHelper\\.Test1\n\n" + |
|
| 122 |
+ "\\.\\.\\. Panic: Fixture has panicked " + |
|
| 123 |
+ "\\(see related PANIC\\)\n$" |
|
| 124 |
+ |
|
| 125 |
+ c.Check(output.value, Matches, expected) |
|
| 126 |
+} |
|
| 127 |
+ |
|
| 128 |
+func (s *FixtureS) TestPanicOnSetUpSuite(c *C) {
|
|
| 129 |
+ helper := FixtureHelper{panicOn: "SetUpSuite"}
|
|
| 130 |
+ output := String{}
|
|
| 131 |
+ Run(&helper, &RunConf{Output: &output})
|
|
| 132 |
+ c.Check(helper.calls[0], Equals, "SetUpSuite") |
|
| 133 |
+ c.Check(helper.calls[1], Equals, "TearDownSuite") |
|
| 134 |
+ c.Check(len(helper.calls), Equals, 2) |
|
| 135 |
+ |
|
| 136 |
+ expected := "^\n-+\n" + |
|
| 137 |
+ "PANIC: check_test\\.go:[0-9]+: " + |
|
| 138 |
+ "FixtureHelper.SetUpSuite\n\n" + |
|
| 139 |
+ "\\.\\.\\. Panic: SetUpSuite \\(PC=[xA-F0-9]+\\)\n\n" + |
|
| 140 |
+ ".+:[0-9]+\n" + |
|
| 141 |
+ " in (go)?panic\n" + |
|
| 142 |
+ ".*check_test.go:[0-9]+\n" + |
|
| 143 |
+ " in FixtureHelper.trace\n" + |
|
| 144 |
+ ".*check_test.go:[0-9]+\n" + |
|
| 145 |
+ " in FixtureHelper.SetUpSuite\n" + |
|
| 146 |
+ "(.|\n)*$" |
|
| 147 |
+ |
|
| 148 |
+ c.Check(output.value, Matches, expected) |
|
| 149 |
+} |
|
| 150 |
+ |
|
| 151 |
+func (s *FixtureS) TestPanicOnTearDownSuite(c *C) {
|
|
| 152 |
+ helper := FixtureHelper{panicOn: "TearDownSuite"}
|
|
| 153 |
+ output := String{}
|
|
| 154 |
+ Run(&helper, &RunConf{Output: &output})
|
|
| 155 |
+ c.Check(helper.calls[0], Equals, "SetUpSuite") |
|
| 156 |
+ c.Check(helper.calls[1], Equals, "SetUpTest") |
|
| 157 |
+ c.Check(helper.calls[2], Equals, "Test1") |
|
| 158 |
+ c.Check(helper.calls[3], Equals, "TearDownTest") |
|
| 159 |
+ c.Check(helper.calls[4], Equals, "SetUpTest") |
|
| 160 |
+ c.Check(helper.calls[5], Equals, "Test2") |
|
| 161 |
+ c.Check(helper.calls[6], Equals, "TearDownTest") |
|
| 162 |
+ c.Check(helper.calls[7], Equals, "TearDownSuite") |
|
| 163 |
+ c.Check(len(helper.calls), Equals, 8) |
|
| 164 |
+ |
|
| 165 |
+ expected := "^\n-+\n" + |
|
| 166 |
+ "PANIC: check_test\\.go:[0-9]+: " + |
|
| 167 |
+ "FixtureHelper.TearDownSuite\n\n" + |
|
| 168 |
+ "\\.\\.\\. Panic: TearDownSuite \\(PC=[xA-F0-9]+\\)\n\n" + |
|
| 169 |
+ ".+:[0-9]+\n" + |
|
| 170 |
+ " in (go)?panic\n" + |
|
| 171 |
+ ".*check_test.go:[0-9]+\n" + |
|
| 172 |
+ " in FixtureHelper.trace\n" + |
|
| 173 |
+ ".*check_test.go:[0-9]+\n" + |
|
| 174 |
+ " in FixtureHelper.TearDownSuite\n" + |
|
| 175 |
+ "(.|\n)*$" |
|
| 176 |
+ |
|
| 177 |
+ c.Check(output.value, Matches, expected) |
|
| 178 |
+} |
|
| 179 |
+ |
|
| 180 |
+// ----------------------------------------------------------------------- |
|
| 181 |
+// A wrong argument on a test or fixture will produce a nice error. |
|
| 182 |
+ |
|
| 183 |
+func (s *FixtureS) TestPanicOnWrongTestArg(c *C) {
|
|
| 184 |
+ helper := WrongTestArgHelper{}
|
|
| 185 |
+ output := String{}
|
|
| 186 |
+ Run(&helper, &RunConf{Output: &output})
|
|
| 187 |
+ c.Check(helper.calls[0], Equals, "SetUpSuite") |
|
| 188 |
+ c.Check(helper.calls[1], Equals, "SetUpTest") |
|
| 189 |
+ c.Check(helper.calls[2], Equals, "TearDownTest") |
|
| 190 |
+ c.Check(helper.calls[3], Equals, "SetUpTest") |
|
| 191 |
+ c.Check(helper.calls[4], Equals, "Test2") |
|
| 192 |
+ c.Check(helper.calls[5], Equals, "TearDownTest") |
|
| 193 |
+ c.Check(helper.calls[6], Equals, "TearDownSuite") |
|
| 194 |
+ c.Check(len(helper.calls), Equals, 7) |
|
| 195 |
+ |
|
| 196 |
+ expected := "^\n-+\n" + |
|
| 197 |
+ "PANIC: fixture_test\\.go:[0-9]+: " + |
|
| 198 |
+ "WrongTestArgHelper\\.Test1\n\n" + |
|
| 199 |
+ "\\.\\.\\. Panic: WrongTestArgHelper\\.Test1 argument " + |
|
| 200 |
+ "should be \\*check\\.C\n" |
|
| 201 |
+ |
|
| 202 |
+ c.Check(output.value, Matches, expected) |
|
| 203 |
+} |
|
| 204 |
+ |
|
| 205 |
+func (s *FixtureS) TestPanicOnWrongSetUpTestArg(c *C) {
|
|
| 206 |
+ helper := WrongSetUpTestArgHelper{}
|
|
| 207 |
+ output := String{}
|
|
| 208 |
+ Run(&helper, &RunConf{Output: &output})
|
|
| 209 |
+ c.Check(len(helper.calls), Equals, 0) |
|
| 210 |
+ |
|
| 211 |
+ expected := |
|
| 212 |
+ "^\n-+\n" + |
|
| 213 |
+ "PANIC: fixture_test\\.go:[0-9]+: " + |
|
| 214 |
+ "WrongSetUpTestArgHelper\\.SetUpTest\n\n" + |
|
| 215 |
+ "\\.\\.\\. Panic: WrongSetUpTestArgHelper\\.SetUpTest argument " + |
|
| 216 |
+ "should be \\*check\\.C\n" |
|
| 217 |
+ |
|
| 218 |
+ c.Check(output.value, Matches, expected) |
|
| 219 |
+} |
|
| 220 |
+ |
|
| 221 |
+func (s *FixtureS) TestPanicOnWrongSetUpSuiteArg(c *C) {
|
|
| 222 |
+ helper := WrongSetUpSuiteArgHelper{}
|
|
| 223 |
+ output := String{}
|
|
| 224 |
+ Run(&helper, &RunConf{Output: &output})
|
|
| 225 |
+ c.Check(len(helper.calls), Equals, 0) |
|
| 226 |
+ |
|
| 227 |
+ expected := |
|
| 228 |
+ "^\n-+\n" + |
|
| 229 |
+ "PANIC: fixture_test\\.go:[0-9]+: " + |
|
| 230 |
+ "WrongSetUpSuiteArgHelper\\.SetUpSuite\n\n" + |
|
| 231 |
+ "\\.\\.\\. Panic: WrongSetUpSuiteArgHelper\\.SetUpSuite argument " + |
|
| 232 |
+ "should be \\*check\\.C\n" |
|
| 233 |
+ |
|
| 234 |
+ c.Check(output.value, Matches, expected) |
|
| 235 |
+} |
|
| 236 |
+ |
|
| 237 |
+// ----------------------------------------------------------------------- |
|
| 238 |
+// Nice errors also when tests or fixture have wrong arg count. |
|
| 239 |
+ |
|
| 240 |
+func (s *FixtureS) TestPanicOnWrongTestArgCount(c *C) {
|
|
| 241 |
+ helper := WrongTestArgCountHelper{}
|
|
| 242 |
+ output := String{}
|
|
| 243 |
+ Run(&helper, &RunConf{Output: &output})
|
|
| 244 |
+ c.Check(helper.calls[0], Equals, "SetUpSuite") |
|
| 245 |
+ c.Check(helper.calls[1], Equals, "SetUpTest") |
|
| 246 |
+ c.Check(helper.calls[2], Equals, "TearDownTest") |
|
| 247 |
+ c.Check(helper.calls[3], Equals, "SetUpTest") |
|
| 248 |
+ c.Check(helper.calls[4], Equals, "Test2") |
|
| 249 |
+ c.Check(helper.calls[5], Equals, "TearDownTest") |
|
| 250 |
+ c.Check(helper.calls[6], Equals, "TearDownSuite") |
|
| 251 |
+ c.Check(len(helper.calls), Equals, 7) |
|
| 252 |
+ |
|
| 253 |
+ expected := "^\n-+\n" + |
|
| 254 |
+ "PANIC: fixture_test\\.go:[0-9]+: " + |
|
| 255 |
+ "WrongTestArgCountHelper\\.Test1\n\n" + |
|
| 256 |
+ "\\.\\.\\. Panic: WrongTestArgCountHelper\\.Test1 argument " + |
|
| 257 |
+ "should be \\*check\\.C\n" |
|
| 258 |
+ |
|
| 259 |
+ c.Check(output.value, Matches, expected) |
|
| 260 |
+} |
|
| 261 |
+ |
|
| 262 |
+func (s *FixtureS) TestPanicOnWrongSetUpTestArgCount(c *C) {
|
|
| 263 |
+ helper := WrongSetUpTestArgCountHelper{}
|
|
| 264 |
+ output := String{}
|
|
| 265 |
+ Run(&helper, &RunConf{Output: &output})
|
|
| 266 |
+ c.Check(len(helper.calls), Equals, 0) |
|
| 267 |
+ |
|
| 268 |
+ expected := |
|
| 269 |
+ "^\n-+\n" + |
|
| 270 |
+ "PANIC: fixture_test\\.go:[0-9]+: " + |
|
| 271 |
+ "WrongSetUpTestArgCountHelper\\.SetUpTest\n\n" + |
|
| 272 |
+ "\\.\\.\\. Panic: WrongSetUpTestArgCountHelper\\.SetUpTest argument " + |
|
| 273 |
+ "should be \\*check\\.C\n" |
|
| 274 |
+ |
|
| 275 |
+ c.Check(output.value, Matches, expected) |
|
| 276 |
+} |
|
| 277 |
+ |
|
| 278 |
+func (s *FixtureS) TestPanicOnWrongSetUpSuiteArgCount(c *C) {
|
|
| 279 |
+ helper := WrongSetUpSuiteArgCountHelper{}
|
|
| 280 |
+ output := String{}
|
|
| 281 |
+ Run(&helper, &RunConf{Output: &output})
|
|
| 282 |
+ c.Check(len(helper.calls), Equals, 0) |
|
| 283 |
+ |
|
| 284 |
+ expected := |
|
| 285 |
+ "^\n-+\n" + |
|
| 286 |
+ "PANIC: fixture_test\\.go:[0-9]+: " + |
|
| 287 |
+ "WrongSetUpSuiteArgCountHelper\\.SetUpSuite\n\n" + |
|
| 288 |
+ "\\.\\.\\. Panic: WrongSetUpSuiteArgCountHelper" + |
|
| 289 |
+ "\\.SetUpSuite argument should be \\*check\\.C\n" |
|
| 290 |
+ |
|
| 291 |
+ c.Check(output.value, Matches, expected) |
|
| 292 |
+} |
|
| 293 |
+ |
|
| 294 |
+// ----------------------------------------------------------------------- |
|
| 295 |
+// Helper test suites with wrong function arguments. |
|
| 296 |
+ |
|
| 297 |
+type WrongTestArgHelper struct {
|
|
| 298 |
+ FixtureHelper |
|
| 299 |
+} |
|
| 300 |
+ |
|
| 301 |
+func (s *WrongTestArgHelper) Test1(t int) {
|
|
| 302 |
+} |
|
| 303 |
+ |
|
| 304 |
+type WrongSetUpTestArgHelper struct {
|
|
| 305 |
+ FixtureHelper |
|
| 306 |
+} |
|
| 307 |
+ |
|
| 308 |
+func (s *WrongSetUpTestArgHelper) SetUpTest(t int) {
|
|
| 309 |
+} |
|
| 310 |
+ |
|
| 311 |
+type WrongSetUpSuiteArgHelper struct {
|
|
| 312 |
+ FixtureHelper |
|
| 313 |
+} |
|
| 314 |
+ |
|
| 315 |
+func (s *WrongSetUpSuiteArgHelper) SetUpSuite(t int) {
|
|
| 316 |
+} |
|
| 317 |
+ |
|
| 318 |
+type WrongTestArgCountHelper struct {
|
|
| 319 |
+ FixtureHelper |
|
| 320 |
+} |
|
| 321 |
+ |
|
| 322 |
+func (s *WrongTestArgCountHelper) Test1(c *C, i int) {
|
|
| 323 |
+} |
|
| 324 |
+ |
|
| 325 |
+type WrongSetUpTestArgCountHelper struct {
|
|
| 326 |
+ FixtureHelper |
|
| 327 |
+} |
|
| 328 |
+ |
|
| 329 |
+func (s *WrongSetUpTestArgCountHelper) SetUpTest(c *C, i int) {
|
|
| 330 |
+} |
|
| 331 |
+ |
|
| 332 |
+type WrongSetUpSuiteArgCountHelper struct {
|
|
| 333 |
+ FixtureHelper |
|
| 334 |
+} |
|
| 335 |
+ |
|
| 336 |
+func (s *WrongSetUpSuiteArgCountHelper) SetUpSuite(c *C, i int) {
|
|
| 337 |
+} |
|
| 338 |
+ |
|
| 339 |
+// ----------------------------------------------------------------------- |
|
| 340 |
+// Ensure fixture doesn't run without tests. |
|
| 341 |
+ |
|
| 342 |
+type NoTestsHelper struct {
|
|
| 343 |
+ hasRun bool |
|
| 344 |
+} |
|
| 345 |
+ |
|
| 346 |
+func (s *NoTestsHelper) SetUpSuite(c *C) {
|
|
| 347 |
+ s.hasRun = true |
|
| 348 |
+} |
|
| 349 |
+ |
|
| 350 |
+func (s *NoTestsHelper) TearDownSuite(c *C) {
|
|
| 351 |
+ s.hasRun = true |
|
| 352 |
+} |
|
| 353 |
+ |
|
| 354 |
+func (s *FixtureS) TestFixtureDoesntRunWithoutTests(c *C) {
|
|
| 355 |
+ helper := NoTestsHelper{}
|
|
| 356 |
+ output := String{}
|
|
| 357 |
+ Run(&helper, &RunConf{Output: &output})
|
|
| 358 |
+ c.Check(helper.hasRun, Equals, false) |
|
| 359 |
+} |
|
| 360 |
+ |
|
| 361 |
+// ----------------------------------------------------------------------- |
|
| 362 |
+// Verify that checks and assertions work correctly inside the fixture. |
|
| 363 |
+ |
|
| 364 |
+type FixtureCheckHelper struct {
|
|
| 365 |
+ fail string |
|
| 366 |
+ completed bool |
|
| 367 |
+} |
|
| 368 |
+ |
|
| 369 |
+func (s *FixtureCheckHelper) SetUpSuite(c *C) {
|
|
| 370 |
+ switch s.fail {
|
|
| 371 |
+ case "SetUpSuiteAssert": |
|
| 372 |
+ c.Assert(false, Equals, true) |
|
| 373 |
+ case "SetUpSuiteCheck": |
|
| 374 |
+ c.Check(false, Equals, true) |
|
| 375 |
+ } |
|
| 376 |
+ s.completed = true |
|
| 377 |
+} |
|
| 378 |
+ |
|
| 379 |
+func (s *FixtureCheckHelper) SetUpTest(c *C) {
|
|
| 380 |
+ switch s.fail {
|
|
| 381 |
+ case "SetUpTestAssert": |
|
| 382 |
+ c.Assert(false, Equals, true) |
|
| 383 |
+ case "SetUpTestCheck": |
|
| 384 |
+ c.Check(false, Equals, true) |
|
| 385 |
+ } |
|
| 386 |
+ s.completed = true |
|
| 387 |
+} |
|
| 388 |
+ |
|
| 389 |
+func (s *FixtureCheckHelper) Test(c *C) {
|
|
| 390 |
+ // Do nothing. |
|
| 391 |
+} |
|
| 392 |
+ |
|
| 393 |
+func (s *FixtureS) TestSetUpSuiteCheck(c *C) {
|
|
| 394 |
+ helper := FixtureCheckHelper{fail: "SetUpSuiteCheck"}
|
|
| 395 |
+ output := String{}
|
|
| 396 |
+ Run(&helper, &RunConf{Output: &output})
|
|
| 397 |
+ c.Assert(output.value, Matches, |
|
| 398 |
+ "\n---+\n"+ |
|
| 399 |
+ "FAIL: fixture_test\\.go:[0-9]+: "+ |
|
| 400 |
+ "FixtureCheckHelper\\.SetUpSuite\n\n"+ |
|
| 401 |
+ "fixture_test\\.go:[0-9]+:\n"+ |
|
| 402 |
+ " c\\.Check\\(false, Equals, true\\)\n"+ |
|
| 403 |
+ "\\.+ obtained bool = false\n"+ |
|
| 404 |
+ "\\.+ expected bool = true\n\n") |
|
| 405 |
+ c.Assert(helper.completed, Equals, true) |
|
| 406 |
+} |
|
| 407 |
+ |
|
| 408 |
+func (s *FixtureS) TestSetUpSuiteAssert(c *C) {
|
|
| 409 |
+ helper := FixtureCheckHelper{fail: "SetUpSuiteAssert"}
|
|
| 410 |
+ output := String{}
|
|
| 411 |
+ Run(&helper, &RunConf{Output: &output})
|
|
| 412 |
+ c.Assert(output.value, Matches, |
|
| 413 |
+ "\n---+\n"+ |
|
| 414 |
+ "FAIL: fixture_test\\.go:[0-9]+: "+ |
|
| 415 |
+ "FixtureCheckHelper\\.SetUpSuite\n\n"+ |
|
| 416 |
+ "fixture_test\\.go:[0-9]+:\n"+ |
|
| 417 |
+ " c\\.Assert\\(false, Equals, true\\)\n"+ |
|
| 418 |
+ "\\.+ obtained bool = false\n"+ |
|
| 419 |
+ "\\.+ expected bool = true\n\n") |
|
| 420 |
+ c.Assert(helper.completed, Equals, false) |
|
| 421 |
+} |
|
| 422 |
+ |
|
| 423 |
+// ----------------------------------------------------------------------- |
|
| 424 |
+// Verify that logging within SetUpTest() persists within the test log itself. |
|
| 425 |
+ |
|
| 426 |
+type FixtureLogHelper struct {
|
|
| 427 |
+ c *C |
|
| 428 |
+} |
|
| 429 |
+ |
|
| 430 |
+func (s *FixtureLogHelper) SetUpTest(c *C) {
|
|
| 431 |
+ s.c = c |
|
| 432 |
+ c.Log("1")
|
|
| 433 |
+} |
|
| 434 |
+ |
|
| 435 |
+func (s *FixtureLogHelper) Test(c *C) {
|
|
| 436 |
+ c.Log("2")
|
|
| 437 |
+ s.c.Log("3")
|
|
| 438 |
+ c.Log("4")
|
|
| 439 |
+ c.Fail() |
|
| 440 |
+} |
|
| 441 |
+ |
|
| 442 |
+func (s *FixtureLogHelper) TearDownTest(c *C) {
|
|
| 443 |
+ s.c.Log("5")
|
|
| 444 |
+} |
|
| 445 |
+ |
|
| 446 |
+func (s *FixtureS) TestFixtureLogging(c *C) {
|
|
| 447 |
+ helper := FixtureLogHelper{}
|
|
| 448 |
+ output := String{}
|
|
| 449 |
+ Run(&helper, &RunConf{Output: &output})
|
|
| 450 |
+ c.Assert(output.value, Matches, |
|
| 451 |
+ "\n---+\n"+ |
|
| 452 |
+ "FAIL: fixture_test\\.go:[0-9]+: "+ |
|
| 453 |
+ "FixtureLogHelper\\.Test\n\n"+ |
|
| 454 |
+ "1\n2\n3\n4\n5\n") |
|
| 455 |
+} |
|
| 456 |
+ |
|
| 457 |
+// ----------------------------------------------------------------------- |
|
| 458 |
+// Skip() within fixture methods. |
|
| 459 |
+ |
|
| 460 |
+func (s *FixtureS) TestSkipSuite(c *C) {
|
|
| 461 |
+ helper := FixtureHelper{skip: true, skipOnN: 0}
|
|
| 462 |
+ output := String{}
|
|
| 463 |
+ result := Run(&helper, &RunConf{Output: &output})
|
|
| 464 |
+ c.Assert(output.value, Equals, "") |
|
| 465 |
+ c.Assert(helper.calls[0], Equals, "SetUpSuite") |
|
| 466 |
+ c.Assert(helper.calls[1], Equals, "TearDownSuite") |
|
| 467 |
+ c.Assert(len(helper.calls), Equals, 2) |
|
| 468 |
+ c.Assert(result.Skipped, Equals, 2) |
|
| 469 |
+} |
|
| 470 |
+ |
|
| 471 |
+func (s *FixtureS) TestSkipTest(c *C) {
|
|
| 472 |
+ helper := FixtureHelper{skip: true, skipOnN: 1}
|
|
| 473 |
+ output := String{}
|
|
| 474 |
+ result := Run(&helper, &RunConf{Output: &output})
|
|
| 475 |
+ c.Assert(helper.calls[0], Equals, "SetUpSuite") |
|
| 476 |
+ c.Assert(helper.calls[1], Equals, "SetUpTest") |
|
| 477 |
+ c.Assert(helper.calls[2], Equals, "SetUpTest") |
|
| 478 |
+ c.Assert(helper.calls[3], Equals, "Test2") |
|
| 479 |
+ c.Assert(helper.calls[4], Equals, "TearDownTest") |
|
| 480 |
+ c.Assert(helper.calls[5], Equals, "TearDownSuite") |
|
| 481 |
+ c.Assert(len(helper.calls), Equals, 6) |
|
| 482 |
+ c.Assert(result.Skipped, Equals, 1) |
|
| 483 |
+} |
| 0 | 484 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,335 @@ |
| 0 |
+// These tests check that the foundations of gocheck are working properly. |
|
| 1 |
+// They already assume that fundamental failing is working already, though, |
|
| 2 |
+// since this was tested in bootstrap_test.go. Even then, some care may |
|
| 3 |
+// still have to be taken when using external functions, since they should |
|
| 4 |
+// of course not rely on functionality tested here. |
|
| 5 |
+ |
|
| 6 |
+package check_test |
|
| 7 |
+ |
|
| 8 |
+import ( |
|
| 9 |
+ "fmt" |
|
| 10 |
+ "gopkg.in/check.v1" |
|
| 11 |
+ "log" |
|
| 12 |
+ "os" |
|
| 13 |
+ "regexp" |
|
| 14 |
+ "strings" |
|
| 15 |
+) |
|
| 16 |
+ |
|
| 17 |
+// ----------------------------------------------------------------------- |
|
| 18 |
+// Foundation test suite. |
|
| 19 |
+ |
|
| 20 |
+type FoundationS struct{}
|
|
| 21 |
+ |
|
| 22 |
+var foundationS = check.Suite(&FoundationS{})
|
|
| 23 |
+ |
|
| 24 |
+func (s *FoundationS) TestCountSuite(c *check.C) {
|
|
| 25 |
+ suitesRun += 1 |
|
| 26 |
+} |
|
| 27 |
+ |
|
| 28 |
+func (s *FoundationS) TestErrorf(c *check.C) {
|
|
| 29 |
+ // Do not use checkState() here. It depends on Errorf() working. |
|
| 30 |
+ expectedLog := fmt.Sprintf("foundation_test.go:%d:\n"+
|
|
| 31 |
+ " c.Errorf(\"Error %%v!\", \"message\")\n"+ |
|
| 32 |
+ "... Error: Error message!\n\n", |
|
| 33 |
+ getMyLine()+1) |
|
| 34 |
+ c.Errorf("Error %v!", "message")
|
|
| 35 |
+ failed := c.Failed() |
|
| 36 |
+ c.Succeed() |
|
| 37 |
+ if log := c.GetTestLog(); log != expectedLog {
|
|
| 38 |
+ c.Logf("Errorf() logged %#v rather than %#v", log, expectedLog)
|
|
| 39 |
+ c.Fail() |
|
| 40 |
+ } |
|
| 41 |
+ if !failed {
|
|
| 42 |
+ c.Logf("Errorf() didn't put the test in a failed state")
|
|
| 43 |
+ c.Fail() |
|
| 44 |
+ } |
|
| 45 |
+} |
|
| 46 |
+ |
|
| 47 |
+func (s *FoundationS) TestError(c *check.C) {
|
|
| 48 |
+ expectedLog := fmt.Sprintf("foundation_test.go:%d:\n"+
|
|
| 49 |
+ " c\\.Error\\(\"Error \", \"message!\"\\)\n"+ |
|
| 50 |
+ "\\.\\.\\. Error: Error message!\n\n", |
|
| 51 |
+ getMyLine()+1) |
|
| 52 |
+ c.Error("Error ", "message!")
|
|
| 53 |
+ checkState(c, nil, |
|
| 54 |
+ &expectedState{
|
|
| 55 |
+ name: "Error(`Error `, `message!`)", |
|
| 56 |
+ failed: true, |
|
| 57 |
+ log: expectedLog, |
|
| 58 |
+ }) |
|
| 59 |
+} |
|
| 60 |
+ |
|
| 61 |
+func (s *FoundationS) TestFailNow(c *check.C) {
|
|
| 62 |
+ defer (func() {
|
|
| 63 |
+ if !c.Failed() {
|
|
| 64 |
+ c.Error("FailNow() didn't fail the test")
|
|
| 65 |
+ } else {
|
|
| 66 |
+ c.Succeed() |
|
| 67 |
+ if c.GetTestLog() != "" {
|
|
| 68 |
+ c.Error("Something got logged:\n" + c.GetTestLog())
|
|
| 69 |
+ } |
|
| 70 |
+ } |
|
| 71 |
+ })() |
|
| 72 |
+ |
|
| 73 |
+ c.FailNow() |
|
| 74 |
+ c.Log("FailNow() didn't stop the test")
|
|
| 75 |
+} |
|
| 76 |
+ |
|
| 77 |
+func (s *FoundationS) TestSucceedNow(c *check.C) {
|
|
| 78 |
+ defer (func() {
|
|
| 79 |
+ if c.Failed() {
|
|
| 80 |
+ c.Error("SucceedNow() didn't succeed the test")
|
|
| 81 |
+ } |
|
| 82 |
+ if c.GetTestLog() != "" {
|
|
| 83 |
+ c.Error("Something got logged:\n" + c.GetTestLog())
|
|
| 84 |
+ } |
|
| 85 |
+ })() |
|
| 86 |
+ |
|
| 87 |
+ c.Fail() |
|
| 88 |
+ c.SucceedNow() |
|
| 89 |
+ c.Log("SucceedNow() didn't stop the test")
|
|
| 90 |
+} |
|
| 91 |
+ |
|
| 92 |
+func (s *FoundationS) TestFailureHeader(c *check.C) {
|
|
| 93 |
+ output := String{}
|
|
| 94 |
+ failHelper := FailHelper{}
|
|
| 95 |
+ check.Run(&failHelper, &check.RunConf{Output: &output})
|
|
| 96 |
+ header := fmt.Sprintf(""+
|
|
| 97 |
+ "\n-----------------------------------"+ |
|
| 98 |
+ "-----------------------------------\n"+ |
|
| 99 |
+ "FAIL: check_test.go:%d: FailHelper.TestLogAndFail\n", |
|
| 100 |
+ failHelper.testLine) |
|
| 101 |
+ if strings.Index(output.value, header) == -1 {
|
|
| 102 |
+ c.Errorf(""+
|
|
| 103 |
+ "Failure didn't print a proper header.\n"+ |
|
| 104 |
+ "... Got:\n%s... Expected something with:\n%s", |
|
| 105 |
+ output.value, header) |
|
| 106 |
+ } |
|
| 107 |
+} |
|
| 108 |
+ |
|
| 109 |
+func (s *FoundationS) TestFatal(c *check.C) {
|
|
| 110 |
+ var line int |
|
| 111 |
+ defer (func() {
|
|
| 112 |
+ if !c.Failed() {
|
|
| 113 |
+ c.Error("Fatal() didn't fail the test")
|
|
| 114 |
+ } else {
|
|
| 115 |
+ c.Succeed() |
|
| 116 |
+ expected := fmt.Sprintf("foundation_test.go:%d:\n"+
|
|
| 117 |
+ " c.Fatal(\"Die \", \"now!\")\n"+ |
|
| 118 |
+ "... Error: Die now!\n\n", |
|
| 119 |
+ line) |
|
| 120 |
+ if c.GetTestLog() != expected {
|
|
| 121 |
+ c.Error("Incorrect log:", c.GetTestLog())
|
|
| 122 |
+ } |
|
| 123 |
+ } |
|
| 124 |
+ })() |
|
| 125 |
+ |
|
| 126 |
+ line = getMyLine() + 1 |
|
| 127 |
+ c.Fatal("Die ", "now!")
|
|
| 128 |
+ c.Log("Fatal() didn't stop the test")
|
|
| 129 |
+} |
|
| 130 |
+ |
|
| 131 |
+func (s *FoundationS) TestFatalf(c *check.C) {
|
|
| 132 |
+ var line int |
|
| 133 |
+ defer (func() {
|
|
| 134 |
+ if !c.Failed() {
|
|
| 135 |
+ c.Error("Fatalf() didn't fail the test")
|
|
| 136 |
+ } else {
|
|
| 137 |
+ c.Succeed() |
|
| 138 |
+ expected := fmt.Sprintf("foundation_test.go:%d:\n"+
|
|
| 139 |
+ " c.Fatalf(\"Die %%s!\", \"now\")\n"+ |
|
| 140 |
+ "... Error: Die now!\n\n", |
|
| 141 |
+ line) |
|
| 142 |
+ if c.GetTestLog() != expected {
|
|
| 143 |
+ c.Error("Incorrect log:", c.GetTestLog())
|
|
| 144 |
+ } |
|
| 145 |
+ } |
|
| 146 |
+ })() |
|
| 147 |
+ |
|
| 148 |
+ line = getMyLine() + 1 |
|
| 149 |
+ c.Fatalf("Die %s!", "now")
|
|
| 150 |
+ c.Log("Fatalf() didn't stop the test")
|
|
| 151 |
+} |
|
| 152 |
+ |
|
| 153 |
+func (s *FoundationS) TestCallerLoggingInsideTest(c *check.C) {
|
|
| 154 |
+ log := fmt.Sprintf(""+
|
|
| 155 |
+ "foundation_test.go:%d:\n"+ |
|
| 156 |
+ " result := c.Check\\(10, check.Equals, 20\\)\n"+ |
|
| 157 |
+ "\\.\\.\\. obtained int = 10\n"+ |
|
| 158 |
+ "\\.\\.\\. expected int = 20\n\n", |
|
| 159 |
+ getMyLine()+1) |
|
| 160 |
+ result := c.Check(10, check.Equals, 20) |
|
| 161 |
+ checkState(c, result, |
|
| 162 |
+ &expectedState{
|
|
| 163 |
+ name: "Check(10, Equals, 20)", |
|
| 164 |
+ result: false, |
|
| 165 |
+ failed: true, |
|
| 166 |
+ log: log, |
|
| 167 |
+ }) |
|
| 168 |
+} |
|
| 169 |
+ |
|
| 170 |
+func (s *FoundationS) TestCallerLoggingInDifferentFile(c *check.C) {
|
|
| 171 |
+ result, line := checkEqualWrapper(c, 10, 20) |
|
| 172 |
+ testLine := getMyLine() - 1 |
|
| 173 |
+ log := fmt.Sprintf(""+
|
|
| 174 |
+ "foundation_test.go:%d:\n"+ |
|
| 175 |
+ " result, line := checkEqualWrapper\\(c, 10, 20\\)\n"+ |
|
| 176 |
+ "check_test.go:%d:\n"+ |
|
| 177 |
+ " return c.Check\\(obtained, check.Equals, expected\\), getMyLine\\(\\)\n"+ |
|
| 178 |
+ "\\.\\.\\. obtained int = 10\n"+ |
|
| 179 |
+ "\\.\\.\\. expected int = 20\n\n", |
|
| 180 |
+ testLine, line) |
|
| 181 |
+ checkState(c, result, |
|
| 182 |
+ &expectedState{
|
|
| 183 |
+ name: "Check(10, Equals, 20)", |
|
| 184 |
+ result: false, |
|
| 185 |
+ failed: true, |
|
| 186 |
+ log: log, |
|
| 187 |
+ }) |
|
| 188 |
+} |
|
| 189 |
+ |
|
| 190 |
+// ----------------------------------------------------------------------- |
|
| 191 |
+// ExpectFailure() inverts the logic of failure. |
|
| 192 |
+ |
|
| 193 |
+type ExpectFailureSucceedHelper struct{}
|
|
| 194 |
+ |
|
| 195 |
+func (s *ExpectFailureSucceedHelper) TestSucceed(c *check.C) {
|
|
| 196 |
+ c.ExpectFailure("It booms!")
|
|
| 197 |
+ c.Error("Boom!")
|
|
| 198 |
+} |
|
| 199 |
+ |
|
| 200 |
+type ExpectFailureFailHelper struct{}
|
|
| 201 |
+ |
|
| 202 |
+func (s *ExpectFailureFailHelper) TestFail(c *check.C) {
|
|
| 203 |
+ c.ExpectFailure("Bug #XYZ")
|
|
| 204 |
+} |
|
| 205 |
+ |
|
| 206 |
+func (s *FoundationS) TestExpectFailureFail(c *check.C) {
|
|
| 207 |
+ helper := ExpectFailureFailHelper{}
|
|
| 208 |
+ output := String{}
|
|
| 209 |
+ result := check.Run(&helper, &check.RunConf{Output: &output})
|
|
| 210 |
+ |
|
| 211 |
+ expected := "" + |
|
| 212 |
+ "^\n-+\n" + |
|
| 213 |
+ "FAIL: foundation_test\\.go:[0-9]+:" + |
|
| 214 |
+ " ExpectFailureFailHelper\\.TestFail\n\n" + |
|
| 215 |
+ "\\.\\.\\. Error: Test succeeded, but was expected to fail\n" + |
|
| 216 |
+ "\\.\\.\\. Reason: Bug #XYZ\n$" |
|
| 217 |
+ |
|
| 218 |
+ matched, err := regexp.MatchString(expected, output.value) |
|
| 219 |
+ if err != nil {
|
|
| 220 |
+ c.Error("Bad expression: ", expected)
|
|
| 221 |
+ } else if !matched {
|
|
| 222 |
+ c.Error("ExpectFailure() didn't log properly:\n", output.value)
|
|
| 223 |
+ } |
|
| 224 |
+ |
|
| 225 |
+ c.Assert(result.ExpectedFailures, check.Equals, 0) |
|
| 226 |
+} |
|
| 227 |
+ |
|
| 228 |
+func (s *FoundationS) TestExpectFailureSucceed(c *check.C) {
|
|
| 229 |
+ helper := ExpectFailureSucceedHelper{}
|
|
| 230 |
+ output := String{}
|
|
| 231 |
+ result := check.Run(&helper, &check.RunConf{Output: &output})
|
|
| 232 |
+ |
|
| 233 |
+ c.Assert(output.value, check.Equals, "") |
|
| 234 |
+ c.Assert(result.ExpectedFailures, check.Equals, 1) |
|
| 235 |
+} |
|
| 236 |
+ |
|
| 237 |
+func (s *FoundationS) TestExpectFailureSucceedVerbose(c *check.C) {
|
|
| 238 |
+ helper := ExpectFailureSucceedHelper{}
|
|
| 239 |
+ output := String{}
|
|
| 240 |
+ result := check.Run(&helper, &check.RunConf{Output: &output, Verbose: true})
|
|
| 241 |
+ |
|
| 242 |
+ expected := "" + |
|
| 243 |
+ "FAIL EXPECTED: foundation_test\\.go:[0-9]+:" + |
|
| 244 |
+ " ExpectFailureSucceedHelper\\.TestSucceed \\(It booms!\\)\t *[.0-9]+s\n" |
|
| 245 |
+ |
|
| 246 |
+ matched, err := regexp.MatchString(expected, output.value) |
|
| 247 |
+ if err != nil {
|
|
| 248 |
+ c.Error("Bad expression: ", expected)
|
|
| 249 |
+ } else if !matched {
|
|
| 250 |
+ c.Error("ExpectFailure() didn't log properly:\n", output.value)
|
|
| 251 |
+ } |
|
| 252 |
+ |
|
| 253 |
+ c.Assert(result.ExpectedFailures, check.Equals, 1) |
|
| 254 |
+} |
|
| 255 |
+ |
|
| 256 |
+// ----------------------------------------------------------------------- |
|
| 257 |
+// Skip() allows stopping a test without positive/negative results. |
|
| 258 |
+ |
|
| 259 |
+type SkipTestHelper struct{}
|
|
| 260 |
+ |
|
| 261 |
+func (s *SkipTestHelper) TestFail(c *check.C) {
|
|
| 262 |
+ c.Skip("Wrong platform or whatever")
|
|
| 263 |
+ c.Error("Boom!")
|
|
| 264 |
+} |
|
| 265 |
+ |
|
| 266 |
+func (s *FoundationS) TestSkip(c *check.C) {
|
|
| 267 |
+ helper := SkipTestHelper{}
|
|
| 268 |
+ output := String{}
|
|
| 269 |
+ check.Run(&helper, &check.RunConf{Output: &output})
|
|
| 270 |
+ |
|
| 271 |
+ if output.value != "" {
|
|
| 272 |
+ c.Error("Skip() logged something:\n", output.value)
|
|
| 273 |
+ } |
|
| 274 |
+} |
|
| 275 |
+ |
|
| 276 |
+func (s *FoundationS) TestSkipVerbose(c *check.C) {
|
|
| 277 |
+ helper := SkipTestHelper{}
|
|
| 278 |
+ output := String{}
|
|
| 279 |
+ check.Run(&helper, &check.RunConf{Output: &output, Verbose: true})
|
|
| 280 |
+ |
|
| 281 |
+ expected := "SKIP: foundation_test\\.go:[0-9]+: SkipTestHelper\\.TestFail" + |
|
| 282 |
+ " \\(Wrong platform or whatever\\)" |
|
| 283 |
+ matched, err := regexp.MatchString(expected, output.value) |
|
| 284 |
+ if err != nil {
|
|
| 285 |
+ c.Error("Bad expression: ", expected)
|
|
| 286 |
+ } else if !matched {
|
|
| 287 |
+ c.Error("Skip() didn't log properly:\n", output.value)
|
|
| 288 |
+ } |
|
| 289 |
+} |
|
| 290 |
+ |
|
| 291 |
+// ----------------------------------------------------------------------- |
|
| 292 |
+// Check minimum *log.Logger interface provided by *check.C. |
|
| 293 |
+ |
|
| 294 |
+type minLogger interface {
|
|
| 295 |
+ Output(calldepth int, s string) error |
|
| 296 |
+} |
|
| 297 |
+ |
|
| 298 |
+func (s *BootstrapS) TestMinLogger(c *check.C) {
|
|
| 299 |
+ var logger minLogger |
|
| 300 |
+ logger = log.New(os.Stderr, "", 0) |
|
| 301 |
+ logger = c |
|
| 302 |
+ logger.Output(0, "Hello there") |
|
| 303 |
+ expected := `\[LOG\] [0-9]+:[0-9][0-9]\.[0-9][0-9][0-9] +Hello there\n` |
|
| 304 |
+ output := c.GetTestLog() |
|
| 305 |
+ c.Assert(output, check.Matches, expected) |
|
| 306 |
+} |
|
| 307 |
+ |
|
| 308 |
+// ----------------------------------------------------------------------- |
|
| 309 |
+// Ensure that suites with embedded types are working fine, including the |
|
| 310 |
+// the workaround for issue 906. |
|
| 311 |
+ |
|
| 312 |
+type EmbeddedInternalS struct {
|
|
| 313 |
+ called bool |
|
| 314 |
+} |
|
| 315 |
+ |
|
| 316 |
+type EmbeddedS struct {
|
|
| 317 |
+ EmbeddedInternalS |
|
| 318 |
+} |
|
| 319 |
+ |
|
| 320 |
+var embeddedS = check.Suite(&EmbeddedS{})
|
|
| 321 |
+ |
|
| 322 |
+func (s *EmbeddedS) TestCountSuite(c *check.C) {
|
|
| 323 |
+ suitesRun += 1 |
|
| 324 |
+} |
|
| 325 |
+ |
|
| 326 |
+func (s *EmbeddedInternalS) TestMethod(c *check.C) {
|
|
| 327 |
+ c.Error("TestMethod() of the embedded type was called!?")
|
|
| 328 |
+} |
|
| 329 |
+ |
|
| 330 |
+func (s *EmbeddedS) TestMethod(c *check.C) {
|
|
| 331 |
+ // http://code.google.com/p/go/issues/detail?id=906 |
|
| 332 |
+ c.Check(s.called, check.Equals, false) // Go issue 906 is affecting the runner? |
|
| 333 |
+ s.called = true |
|
| 334 |
+} |
| 0 | 335 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,231 @@ |
| 0 |
+package check |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "strings" |
|
| 5 |
+ "time" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+// TestName returns the current test name in the form "SuiteName.TestName" |
|
| 9 |
+func (c *C) TestName() string {
|
|
| 10 |
+ return c.testName |
|
| 11 |
+} |
|
| 12 |
+ |
|
| 13 |
+// ----------------------------------------------------------------------- |
|
| 14 |
+// Basic succeeding/failing logic. |
|
| 15 |
+ |
|
| 16 |
+// Failed returns whether the currently running test has already failed. |
|
| 17 |
+func (c *C) Failed() bool {
|
|
| 18 |
+ return c.status == failedSt |
|
| 19 |
+} |
|
| 20 |
+ |
|
| 21 |
+// Fail marks the currently running test as failed. |
|
| 22 |
+// |
|
| 23 |
+// Something ought to have been previously logged so the developer can tell |
|
| 24 |
+// what went wrong. The higher level helper functions will fail the test |
|
| 25 |
+// and do the logging properly. |
|
| 26 |
+func (c *C) Fail() {
|
|
| 27 |
+ c.status = failedSt |
|
| 28 |
+} |
|
| 29 |
+ |
|
| 30 |
+// FailNow marks the currently running test as failed and stops running it. |
|
| 31 |
+// Something ought to have been previously logged so the developer can tell |
|
| 32 |
+// what went wrong. The higher level helper functions will fail the test |
|
| 33 |
+// and do the logging properly. |
|
| 34 |
+func (c *C) FailNow() {
|
|
| 35 |
+ c.Fail() |
|
| 36 |
+ c.stopNow() |
|
| 37 |
+} |
|
| 38 |
+ |
|
| 39 |
+// Succeed marks the currently running test as succeeded, undoing any |
|
| 40 |
+// previous failures. |
|
| 41 |
+func (c *C) Succeed() {
|
|
| 42 |
+ c.status = succeededSt |
|
| 43 |
+} |
|
| 44 |
+ |
|
| 45 |
+// SucceedNow marks the currently running test as succeeded, undoing any |
|
| 46 |
+// previous failures, and stops running the test. |
|
| 47 |
+func (c *C) SucceedNow() {
|
|
| 48 |
+ c.Succeed() |
|
| 49 |
+ c.stopNow() |
|
| 50 |
+} |
|
| 51 |
+ |
|
| 52 |
+// ExpectFailure informs that the running test is knowingly broken for |
|
| 53 |
+// the provided reason. If the test does not fail, an error will be reported |
|
| 54 |
+// to raise attention to this fact. This method is useful to temporarily |
|
| 55 |
+// disable tests which cover well known problems until a better time to |
|
| 56 |
+// fix the problem is found, without forgetting about the fact that a |
|
| 57 |
+// failure still exists. |
|
| 58 |
+func (c *C) ExpectFailure(reason string) {
|
|
| 59 |
+ if reason == "" {
|
|
| 60 |
+ panic("Missing reason why the test is expected to fail")
|
|
| 61 |
+ } |
|
| 62 |
+ c.mustFail = true |
|
| 63 |
+ c.reason = reason |
|
| 64 |
+} |
|
| 65 |
+ |
|
| 66 |
+// Skip skips the running test for the provided reason. If run from within |
|
| 67 |
+// SetUpTest, the individual test being set up will be skipped, and if run |
|
| 68 |
+// from within SetUpSuite, the whole suite is skipped. |
|
| 69 |
+func (c *C) Skip(reason string) {
|
|
| 70 |
+ if reason == "" {
|
|
| 71 |
+ panic("Missing reason why the test is being skipped")
|
|
| 72 |
+ } |
|
| 73 |
+ c.reason = reason |
|
| 74 |
+ c.status = skippedSt |
|
| 75 |
+ c.stopNow() |
|
| 76 |
+} |
|
| 77 |
+ |
|
| 78 |
+// ----------------------------------------------------------------------- |
|
| 79 |
+// Basic logging. |
|
| 80 |
+ |
|
| 81 |
+// GetTestLog returns the current test error output. |
|
| 82 |
+func (c *C) GetTestLog() string {
|
|
| 83 |
+ return c.logb.String() |
|
| 84 |
+} |
|
| 85 |
+ |
|
| 86 |
+// Log logs some information into the test error output. |
|
| 87 |
+// The provided arguments are assembled together into a string with fmt.Sprint. |
|
| 88 |
+func (c *C) Log(args ...interface{}) {
|
|
| 89 |
+ c.log(args...) |
|
| 90 |
+} |
|
| 91 |
+ |
|
| 92 |
+// Log logs some information into the test error output. |
|
| 93 |
+// The provided arguments are assembled together into a string with fmt.Sprintf. |
|
| 94 |
+func (c *C) Logf(format string, args ...interface{}) {
|
|
| 95 |
+ c.logf(format, args...) |
|
| 96 |
+} |
|
| 97 |
+ |
|
| 98 |
+// Output enables *C to be used as a logger in functions that require only |
|
| 99 |
+// the minimum interface of *log.Logger. |
|
| 100 |
+func (c *C) Output(calldepth int, s string) error {
|
|
| 101 |
+ d := time.Now().Sub(c.startTime) |
|
| 102 |
+ msec := d / time.Millisecond |
|
| 103 |
+ sec := d / time.Second |
|
| 104 |
+ min := d / time.Minute |
|
| 105 |
+ |
|
| 106 |
+ c.Logf("[LOG] %d:%02d.%03d %s", min, sec%60, msec%1000, s)
|
|
| 107 |
+ return nil |
|
| 108 |
+} |
|
| 109 |
+ |
|
| 110 |
+// Error logs an error into the test error output and marks the test as failed. |
|
| 111 |
+// The provided arguments are assembled together into a string with fmt.Sprint. |
|
| 112 |
+func (c *C) Error(args ...interface{}) {
|
|
| 113 |
+ c.logCaller(1) |
|
| 114 |
+ c.logString(fmt.Sprint("Error: ", fmt.Sprint(args...)))
|
|
| 115 |
+ c.logNewLine() |
|
| 116 |
+ c.Fail() |
|
| 117 |
+} |
|
| 118 |
+ |
|
| 119 |
+// Errorf logs an error into the test error output and marks the test as failed. |
|
| 120 |
+// The provided arguments are assembled together into a string with fmt.Sprintf. |
|
| 121 |
+func (c *C) Errorf(format string, args ...interface{}) {
|
|
| 122 |
+ c.logCaller(1) |
|
| 123 |
+ c.logString(fmt.Sprintf("Error: "+format, args...))
|
|
| 124 |
+ c.logNewLine() |
|
| 125 |
+ c.Fail() |
|
| 126 |
+} |
|
| 127 |
+ |
|
| 128 |
+// Fatal logs an error into the test error output, marks the test as failed, and |
|
| 129 |
+// stops the test execution. The provided arguments are assembled together into |
|
| 130 |
+// a string with fmt.Sprint. |
|
| 131 |
+func (c *C) Fatal(args ...interface{}) {
|
|
| 132 |
+ c.logCaller(1) |
|
| 133 |
+ c.logString(fmt.Sprint("Error: ", fmt.Sprint(args...)))
|
|
| 134 |
+ c.logNewLine() |
|
| 135 |
+ c.FailNow() |
|
| 136 |
+} |
|
| 137 |
+ |
|
| 138 |
+// Fatlaf logs an error into the test error output, marks the test as failed, and |
|
| 139 |
+// stops the test execution. The provided arguments are assembled together into |
|
| 140 |
+// a string with fmt.Sprintf. |
|
| 141 |
+func (c *C) Fatalf(format string, args ...interface{}) {
|
|
| 142 |
+ c.logCaller(1) |
|
| 143 |
+ c.logString(fmt.Sprint("Error: ", fmt.Sprintf(format, args...)))
|
|
| 144 |
+ c.logNewLine() |
|
| 145 |
+ c.FailNow() |
|
| 146 |
+} |
|
| 147 |
+ |
|
| 148 |
+// ----------------------------------------------------------------------- |
|
| 149 |
+// Generic checks and assertions based on checkers. |
|
| 150 |
+ |
|
| 151 |
+// Check verifies if the first value matches the expected value according |
|
| 152 |
+// to the provided checker. If they do not match, an error is logged, the |
|
| 153 |
+// test is marked as failed, and the test execution continues. |
|
| 154 |
+// |
|
| 155 |
+// Some checkers may not need the expected argument (e.g. IsNil). |
|
| 156 |
+// |
|
| 157 |
+// Extra arguments provided to the function are logged next to the reported |
|
| 158 |
+// problem when the matching fails. |
|
| 159 |
+func (c *C) Check(obtained interface{}, checker Checker, args ...interface{}) bool {
|
|
| 160 |
+ return c.internalCheck("Check", obtained, checker, args...)
|
|
| 161 |
+} |
|
| 162 |
+ |
|
| 163 |
+// Assert ensures that the first value matches the expected value according |
|
| 164 |
+// to the provided checker. If they do not match, an error is logged, the |
|
| 165 |
+// test is marked as failed, and the test execution stops. |
|
| 166 |
+// |
|
| 167 |
+// Some checkers may not need the expected argument (e.g. IsNil). |
|
| 168 |
+// |
|
| 169 |
+// Extra arguments provided to the function are logged next to the reported |
|
| 170 |
+// problem when the matching fails. |
|
| 171 |
+func (c *C) Assert(obtained interface{}, checker Checker, args ...interface{}) {
|
|
| 172 |
+ if !c.internalCheck("Assert", obtained, checker, args...) {
|
|
| 173 |
+ c.stopNow() |
|
| 174 |
+ } |
|
| 175 |
+} |
|
| 176 |
+ |
|
| 177 |
+func (c *C) internalCheck(funcName string, obtained interface{}, checker Checker, args ...interface{}) bool {
|
|
| 178 |
+ if checker == nil {
|
|
| 179 |
+ c.logCaller(2) |
|
| 180 |
+ c.logString(fmt.Sprintf("%s(obtained, nil!?, ...):", funcName))
|
|
| 181 |
+ c.logString("Oops.. you've provided a nil checker!")
|
|
| 182 |
+ c.logNewLine() |
|
| 183 |
+ c.Fail() |
|
| 184 |
+ return false |
|
| 185 |
+ } |
|
| 186 |
+ |
|
| 187 |
+ // If the last argument is a bug info, extract it out. |
|
| 188 |
+ var comment CommentInterface |
|
| 189 |
+ if len(args) > 0 {
|
|
| 190 |
+ if c, ok := args[len(args)-1].(CommentInterface); ok {
|
|
| 191 |
+ comment = c |
|
| 192 |
+ args = args[:len(args)-1] |
|
| 193 |
+ } |
|
| 194 |
+ } |
|
| 195 |
+ |
|
| 196 |
+ params := append([]interface{}{obtained}, args...)
|
|
| 197 |
+ info := checker.Info() |
|
| 198 |
+ |
|
| 199 |
+ if len(params) != len(info.Params) {
|
|
| 200 |
+ names := append([]string{info.Params[0], info.Name}, info.Params[1:]...)
|
|
| 201 |
+ c.logCaller(2) |
|
| 202 |
+ c.logString(fmt.Sprintf("%s(%s):", funcName, strings.Join(names, ", ")))
|
|
| 203 |
+ c.logString(fmt.Sprintf("Wrong number of parameters for %s: want %d, got %d", info.Name, len(names), len(params)+1))
|
|
| 204 |
+ c.logNewLine() |
|
| 205 |
+ c.Fail() |
|
| 206 |
+ return false |
|
| 207 |
+ } |
|
| 208 |
+ |
|
| 209 |
+ // Copy since it may be mutated by Check. |
|
| 210 |
+ names := append([]string{}, info.Params...)
|
|
| 211 |
+ |
|
| 212 |
+ // Do the actual check. |
|
| 213 |
+ result, error := checker.Check(params, names) |
|
| 214 |
+ if !result || error != "" {
|
|
| 215 |
+ c.logCaller(2) |
|
| 216 |
+ for i := 0; i != len(params); i++ {
|
|
| 217 |
+ c.logValue(names[i], params[i]) |
|
| 218 |
+ } |
|
| 219 |
+ if comment != nil {
|
|
| 220 |
+ c.logString(comment.CheckCommentString()) |
|
| 221 |
+ } |
|
| 222 |
+ if error != "" {
|
|
| 223 |
+ c.logString(error) |
|
| 224 |
+ } |
|
| 225 |
+ c.logNewLine() |
|
| 226 |
+ c.Fail() |
|
| 227 |
+ return false |
|
| 228 |
+ } |
|
| 229 |
+ return true |
|
| 230 |
+} |
| 0 | 231 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,519 @@ |
| 0 |
+// These tests verify the inner workings of the helper methods associated |
|
| 1 |
+// with check.T. |
|
| 2 |
+ |
|
| 3 |
+package check_test |
|
| 4 |
+ |
|
| 5 |
+import ( |
|
| 6 |
+ "gopkg.in/check.v1" |
|
| 7 |
+ "os" |
|
| 8 |
+ "reflect" |
|
| 9 |
+ "runtime" |
|
| 10 |
+ "sync" |
|
| 11 |
+) |
|
| 12 |
+ |
|
| 13 |
+var helpersS = check.Suite(&HelpersS{})
|
|
| 14 |
+ |
|
| 15 |
+type HelpersS struct{}
|
|
| 16 |
+ |
|
| 17 |
+func (s *HelpersS) TestCountSuite(c *check.C) {
|
|
| 18 |
+ suitesRun += 1 |
|
| 19 |
+} |
|
| 20 |
+ |
|
| 21 |
+// ----------------------------------------------------------------------- |
|
| 22 |
+// Fake checker and bug info to verify the behavior of Assert() and Check(). |
|
| 23 |
+ |
|
| 24 |
+type MyChecker struct {
|
|
| 25 |
+ info *check.CheckerInfo |
|
| 26 |
+ params []interface{}
|
|
| 27 |
+ names []string |
|
| 28 |
+ result bool |
|
| 29 |
+ error string |
|
| 30 |
+} |
|
| 31 |
+ |
|
| 32 |
+func (checker *MyChecker) Info() *check.CheckerInfo {
|
|
| 33 |
+ if checker.info == nil {
|
|
| 34 |
+ return &check.CheckerInfo{Name: "MyChecker", Params: []string{"myobtained", "myexpected"}}
|
|
| 35 |
+ } |
|
| 36 |
+ return checker.info |
|
| 37 |
+} |
|
| 38 |
+ |
|
| 39 |
+func (checker *MyChecker) Check(params []interface{}, names []string) (bool, string) {
|
|
| 40 |
+ rparams := checker.params |
|
| 41 |
+ rnames := checker.names |
|
| 42 |
+ checker.params = append([]interface{}{}, params...)
|
|
| 43 |
+ checker.names = append([]string{}, names...)
|
|
| 44 |
+ if rparams != nil {
|
|
| 45 |
+ copy(params, rparams) |
|
| 46 |
+ } |
|
| 47 |
+ if rnames != nil {
|
|
| 48 |
+ copy(names, rnames) |
|
| 49 |
+ } |
|
| 50 |
+ return checker.result, checker.error |
|
| 51 |
+} |
|
| 52 |
+ |
|
| 53 |
+type myCommentType string |
|
| 54 |
+ |
|
| 55 |
+func (c myCommentType) CheckCommentString() string {
|
|
| 56 |
+ return string(c) |
|
| 57 |
+} |
|
| 58 |
+ |
|
| 59 |
+func myComment(s string) myCommentType {
|
|
| 60 |
+ return myCommentType(s) |
|
| 61 |
+} |
|
| 62 |
+ |
|
| 63 |
+// ----------------------------------------------------------------------- |
|
| 64 |
+// Ensure a real checker actually works fine. |
|
| 65 |
+ |
|
| 66 |
+func (s *HelpersS) TestCheckerInterface(c *check.C) {
|
|
| 67 |
+ testHelperSuccess(c, "Check(1, Equals, 1)", true, func() interface{} {
|
|
| 68 |
+ return c.Check(1, check.Equals, 1) |
|
| 69 |
+ }) |
|
| 70 |
+} |
|
| 71 |
+ |
|
| 72 |
+// ----------------------------------------------------------------------- |
|
| 73 |
+// Tests for Check(), mostly the same as for Assert() following these. |
|
| 74 |
+ |
|
| 75 |
+func (s *HelpersS) TestCheckSucceedWithExpected(c *check.C) {
|
|
| 76 |
+ checker := &MyChecker{result: true}
|
|
| 77 |
+ testHelperSuccess(c, "Check(1, checker, 2)", true, func() interface{} {
|
|
| 78 |
+ return c.Check(1, checker, 2) |
|
| 79 |
+ }) |
|
| 80 |
+ if !reflect.DeepEqual(checker.params, []interface{}{1, 2}) {
|
|
| 81 |
+ c.Fatalf("Bad params for check: %#v", checker.params)
|
|
| 82 |
+ } |
|
| 83 |
+} |
|
| 84 |
+ |
|
| 85 |
+func (s *HelpersS) TestCheckSucceedWithoutExpected(c *check.C) {
|
|
| 86 |
+ checker := &MyChecker{result: true, info: &check.CheckerInfo{Params: []string{"myvalue"}}}
|
|
| 87 |
+ testHelperSuccess(c, "Check(1, checker)", true, func() interface{} {
|
|
| 88 |
+ return c.Check(1, checker) |
|
| 89 |
+ }) |
|
| 90 |
+ if !reflect.DeepEqual(checker.params, []interface{}{1}) {
|
|
| 91 |
+ c.Fatalf("Bad params for check: %#v", checker.params)
|
|
| 92 |
+ } |
|
| 93 |
+} |
|
| 94 |
+ |
|
| 95 |
+func (s *HelpersS) TestCheckFailWithExpected(c *check.C) {
|
|
| 96 |
+ checker := &MyChecker{result: false}
|
|
| 97 |
+ log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
|
| 98 |
+ " return c\\.Check\\(1, checker, 2\\)\n" + |
|
| 99 |
+ "\\.+ myobtained int = 1\n" + |
|
| 100 |
+ "\\.+ myexpected int = 2\n\n" |
|
| 101 |
+ testHelperFailure(c, "Check(1, checker, 2)", false, false, log, |
|
| 102 |
+ func() interface{} {
|
|
| 103 |
+ return c.Check(1, checker, 2) |
|
| 104 |
+ }) |
|
| 105 |
+} |
|
| 106 |
+ |
|
| 107 |
+func (s *HelpersS) TestCheckFailWithExpectedAndComment(c *check.C) {
|
|
| 108 |
+ checker := &MyChecker{result: false}
|
|
| 109 |
+ log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
|
| 110 |
+ " return c\\.Check\\(1, checker, 2, myComment\\(\"Hello world!\"\\)\\)\n" + |
|
| 111 |
+ "\\.+ myobtained int = 1\n" + |
|
| 112 |
+ "\\.+ myexpected int = 2\n" + |
|
| 113 |
+ "\\.+ Hello world!\n\n" |
|
| 114 |
+ testHelperFailure(c, "Check(1, checker, 2, msg)", false, false, log, |
|
| 115 |
+ func() interface{} {
|
|
| 116 |
+ return c.Check(1, checker, 2, myComment("Hello world!"))
|
|
| 117 |
+ }) |
|
| 118 |
+} |
|
| 119 |
+ |
|
| 120 |
+func (s *HelpersS) TestCheckFailWithExpectedAndStaticComment(c *check.C) {
|
|
| 121 |
+ checker := &MyChecker{result: false}
|
|
| 122 |
+ log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
|
| 123 |
+ " // Nice leading comment\\.\n" + |
|
| 124 |
+ " return c\\.Check\\(1, checker, 2\\) // Hello there\n" + |
|
| 125 |
+ "\\.+ myobtained int = 1\n" + |
|
| 126 |
+ "\\.+ myexpected int = 2\n\n" |
|
| 127 |
+ testHelperFailure(c, "Check(1, checker, 2, msg)", false, false, log, |
|
| 128 |
+ func() interface{} {
|
|
| 129 |
+ // Nice leading comment. |
|
| 130 |
+ return c.Check(1, checker, 2) // Hello there |
|
| 131 |
+ }) |
|
| 132 |
+} |
|
| 133 |
+ |
|
| 134 |
+func (s *HelpersS) TestCheckFailWithoutExpected(c *check.C) {
|
|
| 135 |
+ checker := &MyChecker{result: false, info: &check.CheckerInfo{Params: []string{"myvalue"}}}
|
|
| 136 |
+ log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
|
| 137 |
+ " return c\\.Check\\(1, checker\\)\n" + |
|
| 138 |
+ "\\.+ myvalue int = 1\n\n" |
|
| 139 |
+ testHelperFailure(c, "Check(1, checker)", false, false, log, |
|
| 140 |
+ func() interface{} {
|
|
| 141 |
+ return c.Check(1, checker) |
|
| 142 |
+ }) |
|
| 143 |
+} |
|
| 144 |
+ |
|
| 145 |
+func (s *HelpersS) TestCheckFailWithoutExpectedAndMessage(c *check.C) {
|
|
| 146 |
+ checker := &MyChecker{result: false, info: &check.CheckerInfo{Params: []string{"myvalue"}}}
|
|
| 147 |
+ log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
|
| 148 |
+ " return c\\.Check\\(1, checker, myComment\\(\"Hello world!\"\\)\\)\n" + |
|
| 149 |
+ "\\.+ myvalue int = 1\n" + |
|
| 150 |
+ "\\.+ Hello world!\n\n" |
|
| 151 |
+ testHelperFailure(c, "Check(1, checker, msg)", false, false, log, |
|
| 152 |
+ func() interface{} {
|
|
| 153 |
+ return c.Check(1, checker, myComment("Hello world!"))
|
|
| 154 |
+ }) |
|
| 155 |
+} |
|
| 156 |
+ |
|
| 157 |
+func (s *HelpersS) TestCheckWithMissingExpected(c *check.C) {
|
|
| 158 |
+ checker := &MyChecker{result: true}
|
|
| 159 |
+ log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
|
| 160 |
+ " return c\\.Check\\(1, checker\\)\n" + |
|
| 161 |
+ "\\.+ Check\\(myobtained, MyChecker, myexpected\\):\n" + |
|
| 162 |
+ "\\.+ Wrong number of parameters for MyChecker: " + |
|
| 163 |
+ "want 3, got 2\n\n" |
|
| 164 |
+ testHelperFailure(c, "Check(1, checker, !?)", false, false, log, |
|
| 165 |
+ func() interface{} {
|
|
| 166 |
+ return c.Check(1, checker) |
|
| 167 |
+ }) |
|
| 168 |
+} |
|
| 169 |
+ |
|
| 170 |
+func (s *HelpersS) TestCheckWithTooManyExpected(c *check.C) {
|
|
| 171 |
+ checker := &MyChecker{result: true}
|
|
| 172 |
+ log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
|
| 173 |
+ " return c\\.Check\\(1, checker, 2, 3\\)\n" + |
|
| 174 |
+ "\\.+ Check\\(myobtained, MyChecker, myexpected\\):\n" + |
|
| 175 |
+ "\\.+ Wrong number of parameters for MyChecker: " + |
|
| 176 |
+ "want 3, got 4\n\n" |
|
| 177 |
+ testHelperFailure(c, "Check(1, checker, 2, 3)", false, false, log, |
|
| 178 |
+ func() interface{} {
|
|
| 179 |
+ return c.Check(1, checker, 2, 3) |
|
| 180 |
+ }) |
|
| 181 |
+} |
|
| 182 |
+ |
|
| 183 |
+func (s *HelpersS) TestCheckWithError(c *check.C) {
|
|
| 184 |
+ checker := &MyChecker{result: false, error: "Some not so cool data provided!"}
|
|
| 185 |
+ log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
|
| 186 |
+ " return c\\.Check\\(1, checker, 2\\)\n" + |
|
| 187 |
+ "\\.+ myobtained int = 1\n" + |
|
| 188 |
+ "\\.+ myexpected int = 2\n" + |
|
| 189 |
+ "\\.+ Some not so cool data provided!\n\n" |
|
| 190 |
+ testHelperFailure(c, "Check(1, checker, 2)", false, false, log, |
|
| 191 |
+ func() interface{} {
|
|
| 192 |
+ return c.Check(1, checker, 2) |
|
| 193 |
+ }) |
|
| 194 |
+} |
|
| 195 |
+ |
|
| 196 |
+func (s *HelpersS) TestCheckWithNilChecker(c *check.C) {
|
|
| 197 |
+ log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
|
| 198 |
+ " return c\\.Check\\(1, nil\\)\n" + |
|
| 199 |
+ "\\.+ Check\\(obtained, nil!\\?, \\.\\.\\.\\):\n" + |
|
| 200 |
+ "\\.+ Oops\\.\\. you've provided a nil checker!\n\n" |
|
| 201 |
+ testHelperFailure(c, "Check(obtained, nil)", false, false, log, |
|
| 202 |
+ func() interface{} {
|
|
| 203 |
+ return c.Check(1, nil) |
|
| 204 |
+ }) |
|
| 205 |
+} |
|
| 206 |
+ |
|
| 207 |
+func (s *HelpersS) TestCheckWithParamsAndNamesMutation(c *check.C) {
|
|
| 208 |
+ checker := &MyChecker{result: false, params: []interface{}{3, 4}, names: []string{"newobtained", "newexpected"}}
|
|
| 209 |
+ log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
|
| 210 |
+ " return c\\.Check\\(1, checker, 2\\)\n" + |
|
| 211 |
+ "\\.+ newobtained int = 3\n" + |
|
| 212 |
+ "\\.+ newexpected int = 4\n\n" |
|
| 213 |
+ testHelperFailure(c, "Check(1, checker, 2) with mutation", false, false, log, |
|
| 214 |
+ func() interface{} {
|
|
| 215 |
+ return c.Check(1, checker, 2) |
|
| 216 |
+ }) |
|
| 217 |
+} |
|
| 218 |
+ |
|
| 219 |
+// ----------------------------------------------------------------------- |
|
| 220 |
+// Tests for Assert(), mostly the same as for Check() above. |
|
| 221 |
+ |
|
| 222 |
+func (s *HelpersS) TestAssertSucceedWithExpected(c *check.C) {
|
|
| 223 |
+ checker := &MyChecker{result: true}
|
|
| 224 |
+ testHelperSuccess(c, "Assert(1, checker, 2)", nil, func() interface{} {
|
|
| 225 |
+ c.Assert(1, checker, 2) |
|
| 226 |
+ return nil |
|
| 227 |
+ }) |
|
| 228 |
+ if !reflect.DeepEqual(checker.params, []interface{}{1, 2}) {
|
|
| 229 |
+ c.Fatalf("Bad params for check: %#v", checker.params)
|
|
| 230 |
+ } |
|
| 231 |
+} |
|
| 232 |
+ |
|
| 233 |
+func (s *HelpersS) TestAssertSucceedWithoutExpected(c *check.C) {
|
|
| 234 |
+ checker := &MyChecker{result: true, info: &check.CheckerInfo{Params: []string{"myvalue"}}}
|
|
| 235 |
+ testHelperSuccess(c, "Assert(1, checker)", nil, func() interface{} {
|
|
| 236 |
+ c.Assert(1, checker) |
|
| 237 |
+ return nil |
|
| 238 |
+ }) |
|
| 239 |
+ if !reflect.DeepEqual(checker.params, []interface{}{1}) {
|
|
| 240 |
+ c.Fatalf("Bad params for check: %#v", checker.params)
|
|
| 241 |
+ } |
|
| 242 |
+} |
|
| 243 |
+ |
|
| 244 |
+func (s *HelpersS) TestAssertFailWithExpected(c *check.C) {
|
|
| 245 |
+ checker := &MyChecker{result: false}
|
|
| 246 |
+ log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
|
| 247 |
+ " c\\.Assert\\(1, checker, 2\\)\n" + |
|
| 248 |
+ "\\.+ myobtained int = 1\n" + |
|
| 249 |
+ "\\.+ myexpected int = 2\n\n" |
|
| 250 |
+ testHelperFailure(c, "Assert(1, checker, 2)", nil, true, log, |
|
| 251 |
+ func() interface{} {
|
|
| 252 |
+ c.Assert(1, checker, 2) |
|
| 253 |
+ return nil |
|
| 254 |
+ }) |
|
| 255 |
+} |
|
| 256 |
+ |
|
| 257 |
+func (s *HelpersS) TestAssertFailWithExpectedAndMessage(c *check.C) {
|
|
| 258 |
+ checker := &MyChecker{result: false}
|
|
| 259 |
+ log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
|
| 260 |
+ " c\\.Assert\\(1, checker, 2, myComment\\(\"Hello world!\"\\)\\)\n" + |
|
| 261 |
+ "\\.+ myobtained int = 1\n" + |
|
| 262 |
+ "\\.+ myexpected int = 2\n" + |
|
| 263 |
+ "\\.+ Hello world!\n\n" |
|
| 264 |
+ testHelperFailure(c, "Assert(1, checker, 2, msg)", nil, true, log, |
|
| 265 |
+ func() interface{} {
|
|
| 266 |
+ c.Assert(1, checker, 2, myComment("Hello world!"))
|
|
| 267 |
+ return nil |
|
| 268 |
+ }) |
|
| 269 |
+} |
|
| 270 |
+ |
|
| 271 |
+func (s *HelpersS) TestAssertFailWithoutExpected(c *check.C) {
|
|
| 272 |
+ checker := &MyChecker{result: false, info: &check.CheckerInfo{Params: []string{"myvalue"}}}
|
|
| 273 |
+ log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
|
| 274 |
+ " c\\.Assert\\(1, checker\\)\n" + |
|
| 275 |
+ "\\.+ myvalue int = 1\n\n" |
|
| 276 |
+ testHelperFailure(c, "Assert(1, checker)", nil, true, log, |
|
| 277 |
+ func() interface{} {
|
|
| 278 |
+ c.Assert(1, checker) |
|
| 279 |
+ return nil |
|
| 280 |
+ }) |
|
| 281 |
+} |
|
| 282 |
+ |
|
| 283 |
+func (s *HelpersS) TestAssertFailWithoutExpectedAndMessage(c *check.C) {
|
|
| 284 |
+ checker := &MyChecker{result: false, info: &check.CheckerInfo{Params: []string{"myvalue"}}}
|
|
| 285 |
+ log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
|
| 286 |
+ " c\\.Assert\\(1, checker, myComment\\(\"Hello world!\"\\)\\)\n" + |
|
| 287 |
+ "\\.+ myvalue int = 1\n" + |
|
| 288 |
+ "\\.+ Hello world!\n\n" |
|
| 289 |
+ testHelperFailure(c, "Assert(1, checker, msg)", nil, true, log, |
|
| 290 |
+ func() interface{} {
|
|
| 291 |
+ c.Assert(1, checker, myComment("Hello world!"))
|
|
| 292 |
+ return nil |
|
| 293 |
+ }) |
|
| 294 |
+} |
|
| 295 |
+ |
|
| 296 |
+func (s *HelpersS) TestAssertWithMissingExpected(c *check.C) {
|
|
| 297 |
+ checker := &MyChecker{result: true}
|
|
| 298 |
+ log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
|
| 299 |
+ " c\\.Assert\\(1, checker\\)\n" + |
|
| 300 |
+ "\\.+ Assert\\(myobtained, MyChecker, myexpected\\):\n" + |
|
| 301 |
+ "\\.+ Wrong number of parameters for MyChecker: " + |
|
| 302 |
+ "want 3, got 2\n\n" |
|
| 303 |
+ testHelperFailure(c, "Assert(1, checker, !?)", nil, true, log, |
|
| 304 |
+ func() interface{} {
|
|
| 305 |
+ c.Assert(1, checker) |
|
| 306 |
+ return nil |
|
| 307 |
+ }) |
|
| 308 |
+} |
|
| 309 |
+ |
|
| 310 |
+func (s *HelpersS) TestAssertWithError(c *check.C) {
|
|
| 311 |
+ checker := &MyChecker{result: false, error: "Some not so cool data provided!"}
|
|
| 312 |
+ log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
|
| 313 |
+ " c\\.Assert\\(1, checker, 2\\)\n" + |
|
| 314 |
+ "\\.+ myobtained int = 1\n" + |
|
| 315 |
+ "\\.+ myexpected int = 2\n" + |
|
| 316 |
+ "\\.+ Some not so cool data provided!\n\n" |
|
| 317 |
+ testHelperFailure(c, "Assert(1, checker, 2)", nil, true, log, |
|
| 318 |
+ func() interface{} {
|
|
| 319 |
+ c.Assert(1, checker, 2) |
|
| 320 |
+ return nil |
|
| 321 |
+ }) |
|
| 322 |
+} |
|
| 323 |
+ |
|
| 324 |
+func (s *HelpersS) TestAssertWithNilChecker(c *check.C) {
|
|
| 325 |
+ log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
|
| 326 |
+ " c\\.Assert\\(1, nil\\)\n" + |
|
| 327 |
+ "\\.+ Assert\\(obtained, nil!\\?, \\.\\.\\.\\):\n" + |
|
| 328 |
+ "\\.+ Oops\\.\\. you've provided a nil checker!\n\n" |
|
| 329 |
+ testHelperFailure(c, "Assert(obtained, nil)", nil, true, log, |
|
| 330 |
+ func() interface{} {
|
|
| 331 |
+ c.Assert(1, nil) |
|
| 332 |
+ return nil |
|
| 333 |
+ }) |
|
| 334 |
+} |
|
| 335 |
+ |
|
| 336 |
+// ----------------------------------------------------------------------- |
|
| 337 |
+// Ensure that values logged work properly in some interesting cases. |
|
| 338 |
+ |
|
| 339 |
+func (s *HelpersS) TestValueLoggingWithArrays(c *check.C) {
|
|
| 340 |
+ checker := &MyChecker{result: false}
|
|
| 341 |
+ log := "(?s)helpers_test.go:[0-9]+:.*\nhelpers_test.go:[0-9]+:\n" + |
|
| 342 |
+ " return c\\.Check\\(\\[\\]byte{1, 2}, checker, \\[\\]byte{1, 3}\\)\n" +
|
|
| 343 |
+ "\\.+ myobtained \\[\\]uint8 = \\[\\]byte{0x1, 0x2}\n" +
|
|
| 344 |
+ "\\.+ myexpected \\[\\]uint8 = \\[\\]byte{0x1, 0x3}\n\n"
|
|
| 345 |
+ testHelperFailure(c, "Check([]byte{1}, chk, []byte{3})", false, false, log,
|
|
| 346 |
+ func() interface{} {
|
|
| 347 |
+ return c.Check([]byte{1, 2}, checker, []byte{1, 3})
|
|
| 348 |
+ }) |
|
| 349 |
+} |
|
| 350 |
+ |
|
| 351 |
+func (s *HelpersS) TestValueLoggingWithMultiLine(c *check.C) {
|
|
| 352 |
+ checker := &MyChecker{result: false}
|
|
| 353 |
+ log := "(?s)helpers_test.go:[0-9]+:.*\nhelpers_test.go:[0-9]+:\n" + |
|
| 354 |
+ " return c\\.Check\\(\"a\\\\nb\\\\n\", checker, \"a\\\\nb\\\\nc\"\\)\n" + |
|
| 355 |
+ "\\.+ myobtained string = \"\" \\+\n" + |
|
| 356 |
+ "\\.+ \"a\\\\n\" \\+\n" + |
|
| 357 |
+ "\\.+ \"b\\\\n\"\n" + |
|
| 358 |
+ "\\.+ myexpected string = \"\" \\+\n" + |
|
| 359 |
+ "\\.+ \"a\\\\n\" \\+\n" + |
|
| 360 |
+ "\\.+ \"b\\\\n\" \\+\n" + |
|
| 361 |
+ "\\.+ \"c\"\n\n" |
|
| 362 |
+ testHelperFailure(c, `Check("a\nb\n", chk, "a\nb\nc")`, false, false, log,
|
|
| 363 |
+ func() interface{} {
|
|
| 364 |
+ return c.Check("a\nb\n", checker, "a\nb\nc")
|
|
| 365 |
+ }) |
|
| 366 |
+} |
|
| 367 |
+ |
|
| 368 |
+func (s *HelpersS) TestValueLoggingWithMultiLineException(c *check.C) {
|
|
| 369 |
+ // If the newline is at the end of the string, don't log as multi-line. |
|
| 370 |
+ checker := &MyChecker{result: false}
|
|
| 371 |
+ log := "(?s)helpers_test.go:[0-9]+:.*\nhelpers_test.go:[0-9]+:\n" + |
|
| 372 |
+ " return c\\.Check\\(\"a b\\\\n\", checker, \"a\\\\nb\"\\)\n" + |
|
| 373 |
+ "\\.+ myobtained string = \"a b\\\\n\"\n" + |
|
| 374 |
+ "\\.+ myexpected string = \"\" \\+\n" + |
|
| 375 |
+ "\\.+ \"a\\\\n\" \\+\n" + |
|
| 376 |
+ "\\.+ \"b\"\n\n" |
|
| 377 |
+ testHelperFailure(c, `Check("a b\n", chk, "a\nb")`, false, false, log,
|
|
| 378 |
+ func() interface{} {
|
|
| 379 |
+ return c.Check("a b\n", checker, "a\nb")
|
|
| 380 |
+ }) |
|
| 381 |
+} |
|
| 382 |
+ |
|
| 383 |
+// ----------------------------------------------------------------------- |
|
| 384 |
+// MakeDir() tests. |
|
| 385 |
+ |
|
| 386 |
+type MkDirHelper struct {
|
|
| 387 |
+ path1 string |
|
| 388 |
+ path2 string |
|
| 389 |
+ isDir1 bool |
|
| 390 |
+ isDir2 bool |
|
| 391 |
+ isDir3 bool |
|
| 392 |
+ isDir4 bool |
|
| 393 |
+} |
|
| 394 |
+ |
|
| 395 |
+func (s *MkDirHelper) SetUpSuite(c *check.C) {
|
|
| 396 |
+ s.path1 = c.MkDir() |
|
| 397 |
+ s.isDir1 = isDir(s.path1) |
|
| 398 |
+} |
|
| 399 |
+ |
|
| 400 |
+func (s *MkDirHelper) Test(c *check.C) {
|
|
| 401 |
+ s.path2 = c.MkDir() |
|
| 402 |
+ s.isDir2 = isDir(s.path2) |
|
| 403 |
+} |
|
| 404 |
+ |
|
| 405 |
+func (s *MkDirHelper) TearDownSuite(c *check.C) {
|
|
| 406 |
+ s.isDir3 = isDir(s.path1) |
|
| 407 |
+ s.isDir4 = isDir(s.path2) |
|
| 408 |
+} |
|
| 409 |
+ |
|
| 410 |
+func (s *HelpersS) TestMkDir(c *check.C) {
|
|
| 411 |
+ helper := MkDirHelper{}
|
|
| 412 |
+ output := String{}
|
|
| 413 |
+ check.Run(&helper, &check.RunConf{Output: &output})
|
|
| 414 |
+ c.Assert(output.value, check.Equals, "") |
|
| 415 |
+ c.Check(helper.isDir1, check.Equals, true) |
|
| 416 |
+ c.Check(helper.isDir2, check.Equals, true) |
|
| 417 |
+ c.Check(helper.isDir3, check.Equals, true) |
|
| 418 |
+ c.Check(helper.isDir4, check.Equals, true) |
|
| 419 |
+ c.Check(helper.path1, check.Not(check.Equals), |
|
| 420 |
+ helper.path2) |
|
| 421 |
+ c.Check(isDir(helper.path1), check.Equals, false) |
|
| 422 |
+ c.Check(isDir(helper.path2), check.Equals, false) |
|
| 423 |
+} |
|
| 424 |
+ |
|
| 425 |
+func isDir(path string) bool {
|
|
| 426 |
+ if stat, err := os.Stat(path); err == nil {
|
|
| 427 |
+ return stat.IsDir() |
|
| 428 |
+ } |
|
| 429 |
+ return false |
|
| 430 |
+} |
|
| 431 |
+ |
|
| 432 |
+// Concurrent logging should not corrupt the underling buffer. |
|
| 433 |
+// Use go test -race to detect the race in this test. |
|
| 434 |
+func (s *HelpersS) TestConcurrentLogging(c *check.C) {
|
|
| 435 |
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(runtime.NumCPU())) |
|
| 436 |
+ var start, stop sync.WaitGroup |
|
| 437 |
+ start.Add(1) |
|
| 438 |
+ for i, n := 0, runtime.NumCPU()*2; i < n; i++ {
|
|
| 439 |
+ stop.Add(1) |
|
| 440 |
+ go func(i int) {
|
|
| 441 |
+ start.Wait() |
|
| 442 |
+ for j := 0; j < 30; j++ {
|
|
| 443 |
+ c.Logf("Worker %d: line %d", i, j)
|
|
| 444 |
+ } |
|
| 445 |
+ stop.Done() |
|
| 446 |
+ }(i) |
|
| 447 |
+ } |
|
| 448 |
+ start.Done() |
|
| 449 |
+ stop.Wait() |
|
| 450 |
+} |
|
| 451 |
+ |
|
| 452 |
+// ----------------------------------------------------------------------- |
|
| 453 |
+// Test the TestName function |
|
| 454 |
+ |
|
| 455 |
+type TestNameHelper struct {
|
|
| 456 |
+ name1 string |
|
| 457 |
+ name2 string |
|
| 458 |
+ name3 string |
|
| 459 |
+ name4 string |
|
| 460 |
+ name5 string |
|
| 461 |
+} |
|
| 462 |
+ |
|
| 463 |
+func (s *TestNameHelper) SetUpSuite(c *check.C) { s.name1 = c.TestName() }
|
|
| 464 |
+func (s *TestNameHelper) SetUpTest(c *check.C) { s.name2 = c.TestName() }
|
|
| 465 |
+func (s *TestNameHelper) Test(c *check.C) { s.name3 = c.TestName() }
|
|
| 466 |
+func (s *TestNameHelper) TearDownTest(c *check.C) { s.name4 = c.TestName() }
|
|
| 467 |
+func (s *TestNameHelper) TearDownSuite(c *check.C) { s.name5 = c.TestName() }
|
|
| 468 |
+ |
|
| 469 |
+func (s *HelpersS) TestTestName(c *check.C) {
|
|
| 470 |
+ helper := TestNameHelper{}
|
|
| 471 |
+ output := String{}
|
|
| 472 |
+ check.Run(&helper, &check.RunConf{Output: &output})
|
|
| 473 |
+ c.Check(helper.name1, check.Equals, "") |
|
| 474 |
+ c.Check(helper.name2, check.Equals, "TestNameHelper.Test") |
|
| 475 |
+ c.Check(helper.name3, check.Equals, "TestNameHelper.Test") |
|
| 476 |
+ c.Check(helper.name4, check.Equals, "TestNameHelper.Test") |
|
| 477 |
+ c.Check(helper.name5, check.Equals, "") |
|
| 478 |
+} |
|
| 479 |
+ |
|
| 480 |
+// ----------------------------------------------------------------------- |
|
| 481 |
+// A couple of helper functions to test helper functions. :-) |
|
| 482 |
+ |
|
| 483 |
+func testHelperSuccess(c *check.C, name string, expectedResult interface{}, closure func() interface{}) {
|
|
| 484 |
+ var result interface{}
|
|
| 485 |
+ defer (func() {
|
|
| 486 |
+ if err := recover(); err != nil {
|
|
| 487 |
+ panic(err) |
|
| 488 |
+ } |
|
| 489 |
+ checkState(c, result, |
|
| 490 |
+ &expectedState{
|
|
| 491 |
+ name: name, |
|
| 492 |
+ result: expectedResult, |
|
| 493 |
+ failed: false, |
|
| 494 |
+ log: "", |
|
| 495 |
+ }) |
|
| 496 |
+ })() |
|
| 497 |
+ result = closure() |
|
| 498 |
+} |
|
| 499 |
+ |
|
| 500 |
+func testHelperFailure(c *check.C, name string, expectedResult interface{}, shouldStop bool, log string, closure func() interface{}) {
|
|
| 501 |
+ var result interface{}
|
|
| 502 |
+ defer (func() {
|
|
| 503 |
+ if err := recover(); err != nil {
|
|
| 504 |
+ panic(err) |
|
| 505 |
+ } |
|
| 506 |
+ checkState(c, result, |
|
| 507 |
+ &expectedState{
|
|
| 508 |
+ name: name, |
|
| 509 |
+ result: expectedResult, |
|
| 510 |
+ failed: true, |
|
| 511 |
+ log: log, |
|
| 512 |
+ }) |
|
| 513 |
+ })() |
|
| 514 |
+ result = closure() |
|
| 515 |
+ if shouldStop {
|
|
| 516 |
+ c.Logf("%s didn't stop when it should", name)
|
|
| 517 |
+ } |
|
| 518 |
+} |
| 0 | 519 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,168 @@ |
| 0 |
+package check |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "bytes" |
|
| 4 |
+ "go/ast" |
|
| 5 |
+ "go/parser" |
|
| 6 |
+ "go/printer" |
|
| 7 |
+ "go/token" |
|
| 8 |
+ "os" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+func indent(s, with string) (r string) {
|
|
| 12 |
+ eol := true |
|
| 13 |
+ for i := 0; i != len(s); i++ {
|
|
| 14 |
+ c := s[i] |
|
| 15 |
+ switch {
|
|
| 16 |
+ case eol && c == '\n' || c == '\r': |
|
| 17 |
+ case c == '\n' || c == '\r': |
|
| 18 |
+ eol = true |
|
| 19 |
+ case eol: |
|
| 20 |
+ eol = false |
|
| 21 |
+ s = s[:i] + with + s[i:] |
|
| 22 |
+ i += len(with) |
|
| 23 |
+ } |
|
| 24 |
+ } |
|
| 25 |
+ return s |
|
| 26 |
+} |
|
| 27 |
+ |
|
| 28 |
+func printLine(filename string, line int) (string, error) {
|
|
| 29 |
+ fset := token.NewFileSet() |
|
| 30 |
+ file, err := os.Open(filename) |
|
| 31 |
+ if err != nil {
|
|
| 32 |
+ return "", err |
|
| 33 |
+ } |
|
| 34 |
+ fnode, err := parser.ParseFile(fset, filename, file, parser.ParseComments) |
|
| 35 |
+ if err != nil {
|
|
| 36 |
+ return "", err |
|
| 37 |
+ } |
|
| 38 |
+ config := &printer.Config{Mode: printer.UseSpaces, Tabwidth: 4}
|
|
| 39 |
+ lp := &linePrinter{fset: fset, fnode: fnode, line: line, config: config}
|
|
| 40 |
+ ast.Walk(lp, fnode) |
|
| 41 |
+ result := lp.output.Bytes() |
|
| 42 |
+ // Comments leave \n at the end. |
|
| 43 |
+ n := len(result) |
|
| 44 |
+ for n > 0 && result[n-1] == '\n' {
|
|
| 45 |
+ n-- |
|
| 46 |
+ } |
|
| 47 |
+ return string(result[:n]), nil |
|
| 48 |
+} |
|
| 49 |
+ |
|
| 50 |
+type linePrinter struct {
|
|
| 51 |
+ config *printer.Config |
|
| 52 |
+ fset *token.FileSet |
|
| 53 |
+ fnode *ast.File |
|
| 54 |
+ line int |
|
| 55 |
+ output bytes.Buffer |
|
| 56 |
+ stmt ast.Stmt |
|
| 57 |
+} |
|
| 58 |
+ |
|
| 59 |
+func (lp *linePrinter) emit() bool {
|
|
| 60 |
+ if lp.stmt != nil {
|
|
| 61 |
+ lp.trim(lp.stmt) |
|
| 62 |
+ lp.printWithComments(lp.stmt) |
|
| 63 |
+ lp.stmt = nil |
|
| 64 |
+ return true |
|
| 65 |
+ } |
|
| 66 |
+ return false |
|
| 67 |
+} |
|
| 68 |
+ |
|
| 69 |
+func (lp *linePrinter) printWithComments(n ast.Node) {
|
|
| 70 |
+ nfirst := lp.fset.Position(n.Pos()).Line |
|
| 71 |
+ nlast := lp.fset.Position(n.End()).Line |
|
| 72 |
+ for _, g := range lp.fnode.Comments {
|
|
| 73 |
+ cfirst := lp.fset.Position(g.Pos()).Line |
|
| 74 |
+ clast := lp.fset.Position(g.End()).Line |
|
| 75 |
+ if clast == nfirst-1 && lp.fset.Position(n.Pos()).Column == lp.fset.Position(g.Pos()).Column {
|
|
| 76 |
+ for _, c := range g.List {
|
|
| 77 |
+ lp.output.WriteString(c.Text) |
|
| 78 |
+ lp.output.WriteByte('\n')
|
|
| 79 |
+ } |
|
| 80 |
+ } |
|
| 81 |
+ if cfirst >= nfirst && cfirst <= nlast && n.End() <= g.List[0].Slash {
|
|
| 82 |
+ // The printer will not include the comment if it starts past |
|
| 83 |
+ // the node itself. Trick it into printing by overlapping the |
|
| 84 |
+ // slash with the end of the statement. |
|
| 85 |
+ g.List[0].Slash = n.End() - 1 |
|
| 86 |
+ } |
|
| 87 |
+ } |
|
| 88 |
+ node := &printer.CommentedNode{n, lp.fnode.Comments}
|
|
| 89 |
+ lp.config.Fprint(&lp.output, lp.fset, node) |
|
| 90 |
+} |
|
| 91 |
+ |
|
| 92 |
+func (lp *linePrinter) Visit(n ast.Node) (w ast.Visitor) {
|
|
| 93 |
+ if n == nil {
|
|
| 94 |
+ if lp.output.Len() == 0 {
|
|
| 95 |
+ lp.emit() |
|
| 96 |
+ } |
|
| 97 |
+ return nil |
|
| 98 |
+ } |
|
| 99 |
+ first := lp.fset.Position(n.Pos()).Line |
|
| 100 |
+ last := lp.fset.Position(n.End()).Line |
|
| 101 |
+ if first <= lp.line && last >= lp.line {
|
|
| 102 |
+ // Print the innermost statement containing the line. |
|
| 103 |
+ if stmt, ok := n.(ast.Stmt); ok {
|
|
| 104 |
+ if _, ok := n.(*ast.BlockStmt); !ok {
|
|
| 105 |
+ lp.stmt = stmt |
|
| 106 |
+ } |
|
| 107 |
+ } |
|
| 108 |
+ if first == lp.line && lp.emit() {
|
|
| 109 |
+ return nil |
|
| 110 |
+ } |
|
| 111 |
+ return lp |
|
| 112 |
+ } |
|
| 113 |
+ return nil |
|
| 114 |
+} |
|
| 115 |
+ |
|
| 116 |
+func (lp *linePrinter) trim(n ast.Node) bool {
|
|
| 117 |
+ stmt, ok := n.(ast.Stmt) |
|
| 118 |
+ if !ok {
|
|
| 119 |
+ return true |
|
| 120 |
+ } |
|
| 121 |
+ line := lp.fset.Position(n.Pos()).Line |
|
| 122 |
+ if line != lp.line {
|
|
| 123 |
+ return false |
|
| 124 |
+ } |
|
| 125 |
+ switch stmt := stmt.(type) {
|
|
| 126 |
+ case *ast.IfStmt: |
|
| 127 |
+ stmt.Body = lp.trimBlock(stmt.Body) |
|
| 128 |
+ case *ast.SwitchStmt: |
|
| 129 |
+ stmt.Body = lp.trimBlock(stmt.Body) |
|
| 130 |
+ case *ast.TypeSwitchStmt: |
|
| 131 |
+ stmt.Body = lp.trimBlock(stmt.Body) |
|
| 132 |
+ case *ast.CaseClause: |
|
| 133 |
+ stmt.Body = lp.trimList(stmt.Body) |
|
| 134 |
+ case *ast.CommClause: |
|
| 135 |
+ stmt.Body = lp.trimList(stmt.Body) |
|
| 136 |
+ case *ast.BlockStmt: |
|
| 137 |
+ stmt.List = lp.trimList(stmt.List) |
|
| 138 |
+ } |
|
| 139 |
+ return true |
|
| 140 |
+} |
|
| 141 |
+ |
|
| 142 |
+func (lp *linePrinter) trimBlock(stmt *ast.BlockStmt) *ast.BlockStmt {
|
|
| 143 |
+ if !lp.trim(stmt) {
|
|
| 144 |
+ return lp.emptyBlock(stmt) |
|
| 145 |
+ } |
|
| 146 |
+ stmt.Rbrace = stmt.Lbrace |
|
| 147 |
+ return stmt |
|
| 148 |
+} |
|
| 149 |
+ |
|
| 150 |
+func (lp *linePrinter) trimList(stmts []ast.Stmt) []ast.Stmt {
|
|
| 151 |
+ for i := 0; i != len(stmts); i++ {
|
|
| 152 |
+ if !lp.trim(stmts[i]) {
|
|
| 153 |
+ stmts[i] = lp.emptyStmt(stmts[i]) |
|
| 154 |
+ break |
|
| 155 |
+ } |
|
| 156 |
+ } |
|
| 157 |
+ return stmts |
|
| 158 |
+} |
|
| 159 |
+ |
|
| 160 |
+func (lp *linePrinter) emptyStmt(n ast.Node) *ast.ExprStmt {
|
|
| 161 |
+ return &ast.ExprStmt{&ast.Ellipsis{n.Pos(), nil}}
|
|
| 162 |
+} |
|
| 163 |
+ |
|
| 164 |
+func (lp *linePrinter) emptyBlock(n ast.Node) *ast.BlockStmt {
|
|
| 165 |
+ p := n.Pos() |
|
| 166 |
+ return &ast.BlockStmt{p, []ast.Stmt{lp.emptyStmt(n)}, p}
|
|
| 167 |
+} |
| 0 | 168 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,104 @@ |
| 0 |
+package check_test |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ . "gopkg.in/check.v1" |
|
| 4 |
+) |
|
| 5 |
+ |
|
| 6 |
+var _ = Suite(&PrinterS{})
|
|
| 7 |
+ |
|
| 8 |
+type PrinterS struct{}
|
|
| 9 |
+ |
|
| 10 |
+func (s *PrinterS) TestCountSuite(c *C) {
|
|
| 11 |
+ suitesRun += 1 |
|
| 12 |
+} |
|
| 13 |
+ |
|
| 14 |
+var printTestFuncLine int |
|
| 15 |
+ |
|
| 16 |
+func init() {
|
|
| 17 |
+ printTestFuncLine = getMyLine() + 3 |
|
| 18 |
+} |
|
| 19 |
+ |
|
| 20 |
+func printTestFunc() {
|
|
| 21 |
+ println(1) // Comment1 |
|
| 22 |
+ if 2 == 2 { // Comment2
|
|
| 23 |
+ println(3) // Comment3 |
|
| 24 |
+ } |
|
| 25 |
+ switch 5 {
|
|
| 26 |
+ case 6: println(6) // Comment6 |
|
| 27 |
+ println(7) |
|
| 28 |
+ } |
|
| 29 |
+ switch interface{}(9).(type) {// Comment9
|
|
| 30 |
+ case int: println(10) |
|
| 31 |
+ println(11) |
|
| 32 |
+ } |
|
| 33 |
+ select {
|
|
| 34 |
+ case <-(chan bool)(nil): println(14) |
|
| 35 |
+ println(15) |
|
| 36 |
+ default: println(16) |
|
| 37 |
+ println(17) |
|
| 38 |
+ } |
|
| 39 |
+ println(19, |
|
| 40 |
+ 20) |
|
| 41 |
+ _ = func() { println(21)
|
|
| 42 |
+ println(22) |
|
| 43 |
+ } |
|
| 44 |
+ println(24, func() {
|
|
| 45 |
+ println(25) |
|
| 46 |
+ }) |
|
| 47 |
+ // Leading comment |
|
| 48 |
+ // with multiple lines. |
|
| 49 |
+ println(29) // Comment29 |
|
| 50 |
+} |
|
| 51 |
+ |
|
| 52 |
+var printLineTests = []struct {
|
|
| 53 |
+ line int |
|
| 54 |
+ output string |
|
| 55 |
+}{
|
|
| 56 |
+ {1, "println(1) // Comment1"},
|
|
| 57 |
+ {2, "if 2 == 2 { // Comment2\n ...\n}"},
|
|
| 58 |
+ {3, "println(3) // Comment3"},
|
|
| 59 |
+ {5, "switch 5 {\n...\n}"},
|
|
| 60 |
+ {6, "case 6:\n println(6) // Comment6\n ..."},
|
|
| 61 |
+ {7, "println(7)"},
|
|
| 62 |
+ {9, "switch interface{}(9).(type) { // Comment9\n...\n}"},
|
|
| 63 |
+ {10, "case int:\n println(10)\n ..."},
|
|
| 64 |
+ {14, "case <-(chan bool)(nil):\n println(14)\n ..."},
|
|
| 65 |
+ {15, "println(15)"},
|
|
| 66 |
+ {16, "default:\n println(16)\n ..."},
|
|
| 67 |
+ {17, "println(17)"},
|
|
| 68 |
+ {19, "println(19,\n 20)"},
|
|
| 69 |
+ {20, "println(19,\n 20)"},
|
|
| 70 |
+ {21, "_ = func() {\n println(21)\n println(22)\n}"},
|
|
| 71 |
+ {22, "println(22)"},
|
|
| 72 |
+ {24, "println(24, func() {\n println(25)\n})"},
|
|
| 73 |
+ {25, "println(25)"},
|
|
| 74 |
+ {26, "println(24, func() {\n println(25)\n})"},
|
|
| 75 |
+ {29, "// Leading comment\n// with multiple lines.\nprintln(29) // Comment29"},
|
|
| 76 |
+} |
|
| 77 |
+ |
|
| 78 |
+func (s *PrinterS) TestPrintLine(c *C) {
|
|
| 79 |
+ for _, test := range printLineTests {
|
|
| 80 |
+ output, err := PrintLine("printer_test.go", printTestFuncLine+test.line)
|
|
| 81 |
+ c.Assert(err, IsNil) |
|
| 82 |
+ c.Assert(output, Equals, test.output) |
|
| 83 |
+ } |
|
| 84 |
+} |
|
| 85 |
+ |
|
| 86 |
+var indentTests = []struct {
|
|
| 87 |
+ in, out string |
|
| 88 |
+}{
|
|
| 89 |
+ {"", ""},
|
|
| 90 |
+ {"\n", "\n"},
|
|
| 91 |
+ {"a", ">>>a"},
|
|
| 92 |
+ {"a\n", ">>>a\n"},
|
|
| 93 |
+ {"a\nb", ">>>a\n>>>b"},
|
|
| 94 |
+ {" ", ">>> "},
|
|
| 95 |
+} |
|
| 96 |
+ |
|
| 97 |
+func (s *PrinterS) TestIndent(c *C) {
|
|
| 98 |
+ for _, test := range indentTests {
|
|
| 99 |
+ out := Indent(test.in, ">>>") |
|
| 100 |
+ c.Assert(out, Equals, test.out) |
|
| 101 |
+ } |
|
| 102 |
+ |
|
| 103 |
+} |
| 0 | 104 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,175 @@ |
| 0 |
+package check |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "bufio" |
|
| 4 |
+ "flag" |
|
| 5 |
+ "fmt" |
|
| 6 |
+ "os" |
|
| 7 |
+ "testing" |
|
| 8 |
+ "time" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+// ----------------------------------------------------------------------- |
|
| 12 |
+// Test suite registry. |
|
| 13 |
+ |
|
| 14 |
+var allSuites []interface{}
|
|
| 15 |
+ |
|
| 16 |
+// Suite registers the given value as a test suite to be run. Any methods |
|
| 17 |
+// starting with the Test prefix in the given value will be considered as |
|
| 18 |
+// a test method. |
|
| 19 |
+func Suite(suite interface{}) interface{} {
|
|
| 20 |
+ allSuites = append(allSuites, suite) |
|
| 21 |
+ return suite |
|
| 22 |
+} |
|
| 23 |
+ |
|
| 24 |
+// ----------------------------------------------------------------------- |
|
| 25 |
+// Public running interface. |
|
| 26 |
+ |
|
| 27 |
+var ( |
|
| 28 |
+ oldFilterFlag = flag.String("gocheck.f", "", "Regular expression selecting which tests and/or suites to run")
|
|
| 29 |
+ oldVerboseFlag = flag.Bool("gocheck.v", false, "Verbose mode")
|
|
| 30 |
+ oldStreamFlag = flag.Bool("gocheck.vv", false, "Super verbose mode (disables output caching)")
|
|
| 31 |
+ oldBenchFlag = flag.Bool("gocheck.b", false, "Run benchmarks")
|
|
| 32 |
+ oldBenchTime = flag.Duration("gocheck.btime", 1*time.Second, "approximate run time for each benchmark")
|
|
| 33 |
+ oldListFlag = flag.Bool("gocheck.list", false, "List the names of all tests that will be run")
|
|
| 34 |
+ oldWorkFlag = flag.Bool("gocheck.work", false, "Display and do not remove the test working directory")
|
|
| 35 |
+ |
|
| 36 |
+ newFilterFlag = flag.String("check.f", "", "Regular expression selecting which tests and/or suites to run")
|
|
| 37 |
+ newVerboseFlag = flag.Bool("check.v", false, "Verbose mode")
|
|
| 38 |
+ newStreamFlag = flag.Bool("check.vv", false, "Super verbose mode (disables output caching)")
|
|
| 39 |
+ newBenchFlag = flag.Bool("check.b", false, "Run benchmarks")
|
|
| 40 |
+ newBenchTime = flag.Duration("check.btime", 1*time.Second, "approximate run time for each benchmark")
|
|
| 41 |
+ newBenchMem = flag.Bool("check.bmem", false, "Report memory benchmarks")
|
|
| 42 |
+ newListFlag = flag.Bool("check.list", false, "List the names of all tests that will be run")
|
|
| 43 |
+ newWorkFlag = flag.Bool("check.work", false, "Display and do not remove the test working directory")
|
|
| 44 |
+) |
|
| 45 |
+ |
|
| 46 |
+// TestingT runs all test suites registered with the Suite function, |
|
| 47 |
+// printing results to stdout, and reporting any failures back to |
|
| 48 |
+// the "testing" package. |
|
| 49 |
+func TestingT(testingT *testing.T) {
|
|
| 50 |
+ benchTime := *newBenchTime |
|
| 51 |
+ if benchTime == 1*time.Second {
|
|
| 52 |
+ benchTime = *oldBenchTime |
|
| 53 |
+ } |
|
| 54 |
+ conf := &RunConf{
|
|
| 55 |
+ Filter: *oldFilterFlag + *newFilterFlag, |
|
| 56 |
+ Verbose: *oldVerboseFlag || *newVerboseFlag, |
|
| 57 |
+ Stream: *oldStreamFlag || *newStreamFlag, |
|
| 58 |
+ Benchmark: *oldBenchFlag || *newBenchFlag, |
|
| 59 |
+ BenchmarkTime: benchTime, |
|
| 60 |
+ BenchmarkMem: *newBenchMem, |
|
| 61 |
+ KeepWorkDir: *oldWorkFlag || *newWorkFlag, |
|
| 62 |
+ } |
|
| 63 |
+ if *oldListFlag || *newListFlag {
|
|
| 64 |
+ w := bufio.NewWriter(os.Stdout) |
|
| 65 |
+ for _, name := range ListAll(conf) {
|
|
| 66 |
+ fmt.Fprintln(w, name) |
|
| 67 |
+ } |
|
| 68 |
+ w.Flush() |
|
| 69 |
+ return |
|
| 70 |
+ } |
|
| 71 |
+ result := RunAll(conf) |
|
| 72 |
+ println(result.String()) |
|
| 73 |
+ if !result.Passed() {
|
|
| 74 |
+ testingT.Fail() |
|
| 75 |
+ } |
|
| 76 |
+} |
|
| 77 |
+ |
|
| 78 |
+// RunAll runs all test suites registered with the Suite function, using the |
|
| 79 |
+// provided run configuration. |
|
| 80 |
+func RunAll(runConf *RunConf) *Result {
|
|
| 81 |
+ result := Result{}
|
|
| 82 |
+ for _, suite := range allSuites {
|
|
| 83 |
+ result.Add(Run(suite, runConf)) |
|
| 84 |
+ } |
|
| 85 |
+ return &result |
|
| 86 |
+} |
|
| 87 |
+ |
|
| 88 |
+// Run runs the provided test suite using the provided run configuration. |
|
| 89 |
+func Run(suite interface{}, runConf *RunConf) *Result {
|
|
| 90 |
+ runner := newSuiteRunner(suite, runConf) |
|
| 91 |
+ return runner.run() |
|
| 92 |
+} |
|
| 93 |
+ |
|
| 94 |
+// ListAll returns the names of all the test functions registered with the |
|
| 95 |
+// Suite function that will be run with the provided run configuration. |
|
| 96 |
+func ListAll(runConf *RunConf) []string {
|
|
| 97 |
+ var names []string |
|
| 98 |
+ for _, suite := range allSuites {
|
|
| 99 |
+ names = append(names, List(suite, runConf)...) |
|
| 100 |
+ } |
|
| 101 |
+ return names |
|
| 102 |
+} |
|
| 103 |
+ |
|
| 104 |
+// List returns the names of the test functions in the given |
|
| 105 |
+// suite that will be run with the provided run configuration. |
|
| 106 |
+func List(suite interface{}, runConf *RunConf) []string {
|
|
| 107 |
+ var names []string |
|
| 108 |
+ runner := newSuiteRunner(suite, runConf) |
|
| 109 |
+ for _, t := range runner.tests {
|
|
| 110 |
+ names = append(names, t.String()) |
|
| 111 |
+ } |
|
| 112 |
+ return names |
|
| 113 |
+} |
|
| 114 |
+ |
|
| 115 |
+// ----------------------------------------------------------------------- |
|
| 116 |
+// Result methods. |
|
| 117 |
+ |
|
| 118 |
+func (r *Result) Add(other *Result) {
|
|
| 119 |
+ r.Succeeded += other.Succeeded |
|
| 120 |
+ r.Skipped += other.Skipped |
|
| 121 |
+ r.Failed += other.Failed |
|
| 122 |
+ r.Panicked += other.Panicked |
|
| 123 |
+ r.FixturePanicked += other.FixturePanicked |
|
| 124 |
+ r.ExpectedFailures += other.ExpectedFailures |
|
| 125 |
+ r.Missed += other.Missed |
|
| 126 |
+ if r.WorkDir != "" && other.WorkDir != "" {
|
|
| 127 |
+ r.WorkDir += ":" + other.WorkDir |
|
| 128 |
+ } else if other.WorkDir != "" {
|
|
| 129 |
+ r.WorkDir = other.WorkDir |
|
| 130 |
+ } |
|
| 131 |
+} |
|
| 132 |
+ |
|
| 133 |
+func (r *Result) Passed() bool {
|
|
| 134 |
+ return (r.Failed == 0 && r.Panicked == 0 && |
|
| 135 |
+ r.FixturePanicked == 0 && r.Missed == 0 && |
|
| 136 |
+ r.RunError == nil) |
|
| 137 |
+} |
|
| 138 |
+ |
|
| 139 |
+func (r *Result) String() string {
|
|
| 140 |
+ if r.RunError != nil {
|
|
| 141 |
+ return "ERROR: " + r.RunError.Error() |
|
| 142 |
+ } |
|
| 143 |
+ |
|
| 144 |
+ var value string |
|
| 145 |
+ if r.Failed == 0 && r.Panicked == 0 && r.FixturePanicked == 0 && |
|
| 146 |
+ r.Missed == 0 {
|
|
| 147 |
+ value = "OK: " |
|
| 148 |
+ } else {
|
|
| 149 |
+ value = "OOPS: " |
|
| 150 |
+ } |
|
| 151 |
+ value += fmt.Sprintf("%d passed", r.Succeeded)
|
|
| 152 |
+ if r.Skipped != 0 {
|
|
| 153 |
+ value += fmt.Sprintf(", %d skipped", r.Skipped)
|
|
| 154 |
+ } |
|
| 155 |
+ if r.ExpectedFailures != 0 {
|
|
| 156 |
+ value += fmt.Sprintf(", %d expected failures", r.ExpectedFailures)
|
|
| 157 |
+ } |
|
| 158 |
+ if r.Failed != 0 {
|
|
| 159 |
+ value += fmt.Sprintf(", %d FAILED", r.Failed)
|
|
| 160 |
+ } |
|
| 161 |
+ if r.Panicked != 0 {
|
|
| 162 |
+ value += fmt.Sprintf(", %d PANICKED", r.Panicked)
|
|
| 163 |
+ } |
|
| 164 |
+ if r.FixturePanicked != 0 {
|
|
| 165 |
+ value += fmt.Sprintf(", %d FIXTURE-PANICKED", r.FixturePanicked)
|
|
| 166 |
+ } |
|
| 167 |
+ if r.Missed != 0 {
|
|
| 168 |
+ value += fmt.Sprintf(", %d MISSED", r.Missed)
|
|
| 169 |
+ } |
|
| 170 |
+ if r.WorkDir != "" {
|
|
| 171 |
+ value += "\nWORK=" + r.WorkDir |
|
| 172 |
+ } |
|
| 173 |
+ return value |
|
| 174 |
+} |
| 0 | 175 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,419 @@ |
| 0 |
+// These tests verify the test running logic. |
|
| 1 |
+ |
|
| 2 |
+package check_test |
|
| 3 |
+ |
|
| 4 |
+import ( |
|
| 5 |
+ "errors" |
|
| 6 |
+ . "gopkg.in/check.v1" |
|
| 7 |
+ "os" |
|
| 8 |
+ "sync" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+var runnerS = Suite(&RunS{})
|
|
| 12 |
+ |
|
| 13 |
+type RunS struct{}
|
|
| 14 |
+ |
|
| 15 |
+func (s *RunS) TestCountSuite(c *C) {
|
|
| 16 |
+ suitesRun += 1 |
|
| 17 |
+} |
|
| 18 |
+ |
|
| 19 |
+// ----------------------------------------------------------------------- |
|
| 20 |
+// Tests ensuring result counting works properly. |
|
| 21 |
+ |
|
| 22 |
+func (s *RunS) TestSuccess(c *C) {
|
|
| 23 |
+ output := String{}
|
|
| 24 |
+ result := Run(&SuccessHelper{}, &RunConf{Output: &output})
|
|
| 25 |
+ c.Check(result.Succeeded, Equals, 1) |
|
| 26 |
+ c.Check(result.Failed, Equals, 0) |
|
| 27 |
+ c.Check(result.Skipped, Equals, 0) |
|
| 28 |
+ c.Check(result.Panicked, Equals, 0) |
|
| 29 |
+ c.Check(result.FixturePanicked, Equals, 0) |
|
| 30 |
+ c.Check(result.Missed, Equals, 0) |
|
| 31 |
+ c.Check(result.RunError, IsNil) |
|
| 32 |
+} |
|
| 33 |
+ |
|
| 34 |
+func (s *RunS) TestFailure(c *C) {
|
|
| 35 |
+ output := String{}
|
|
| 36 |
+ result := Run(&FailHelper{}, &RunConf{Output: &output})
|
|
| 37 |
+ c.Check(result.Succeeded, Equals, 0) |
|
| 38 |
+ c.Check(result.Failed, Equals, 1) |
|
| 39 |
+ c.Check(result.Skipped, Equals, 0) |
|
| 40 |
+ c.Check(result.Panicked, Equals, 0) |
|
| 41 |
+ c.Check(result.FixturePanicked, Equals, 0) |
|
| 42 |
+ c.Check(result.Missed, Equals, 0) |
|
| 43 |
+ c.Check(result.RunError, IsNil) |
|
| 44 |
+} |
|
| 45 |
+ |
|
| 46 |
+func (s *RunS) TestFixture(c *C) {
|
|
| 47 |
+ output := String{}
|
|
| 48 |
+ result := Run(&FixtureHelper{}, &RunConf{Output: &output})
|
|
| 49 |
+ c.Check(result.Succeeded, Equals, 2) |
|
| 50 |
+ c.Check(result.Failed, Equals, 0) |
|
| 51 |
+ c.Check(result.Skipped, Equals, 0) |
|
| 52 |
+ c.Check(result.Panicked, Equals, 0) |
|
| 53 |
+ c.Check(result.FixturePanicked, Equals, 0) |
|
| 54 |
+ c.Check(result.Missed, Equals, 0) |
|
| 55 |
+ c.Check(result.RunError, IsNil) |
|
| 56 |
+} |
|
| 57 |
+ |
|
| 58 |
+func (s *RunS) TestPanicOnTest(c *C) {
|
|
| 59 |
+ output := String{}
|
|
| 60 |
+ helper := &FixtureHelper{panicOn: "Test1"}
|
|
| 61 |
+ result := Run(helper, &RunConf{Output: &output})
|
|
| 62 |
+ c.Check(result.Succeeded, Equals, 1) |
|
| 63 |
+ c.Check(result.Failed, Equals, 0) |
|
| 64 |
+ c.Check(result.Skipped, Equals, 0) |
|
| 65 |
+ c.Check(result.Panicked, Equals, 1) |
|
| 66 |
+ c.Check(result.FixturePanicked, Equals, 0) |
|
| 67 |
+ c.Check(result.Missed, Equals, 0) |
|
| 68 |
+ c.Check(result.RunError, IsNil) |
|
| 69 |
+} |
|
| 70 |
+ |
|
| 71 |
+func (s *RunS) TestPanicOnSetUpTest(c *C) {
|
|
| 72 |
+ output := String{}
|
|
| 73 |
+ helper := &FixtureHelper{panicOn: "SetUpTest"}
|
|
| 74 |
+ result := Run(helper, &RunConf{Output: &output})
|
|
| 75 |
+ c.Check(result.Succeeded, Equals, 0) |
|
| 76 |
+ c.Check(result.Failed, Equals, 0) |
|
| 77 |
+ c.Check(result.Skipped, Equals, 0) |
|
| 78 |
+ c.Check(result.Panicked, Equals, 0) |
|
| 79 |
+ c.Check(result.FixturePanicked, Equals, 1) |
|
| 80 |
+ c.Check(result.Missed, Equals, 2) |
|
| 81 |
+ c.Check(result.RunError, IsNil) |
|
| 82 |
+} |
|
| 83 |
+ |
|
| 84 |
+func (s *RunS) TestPanicOnSetUpSuite(c *C) {
|
|
| 85 |
+ output := String{}
|
|
| 86 |
+ helper := &FixtureHelper{panicOn: "SetUpSuite"}
|
|
| 87 |
+ result := Run(helper, &RunConf{Output: &output})
|
|
| 88 |
+ c.Check(result.Succeeded, Equals, 0) |
|
| 89 |
+ c.Check(result.Failed, Equals, 0) |
|
| 90 |
+ c.Check(result.Skipped, Equals, 0) |
|
| 91 |
+ c.Check(result.Panicked, Equals, 0) |
|
| 92 |
+ c.Check(result.FixturePanicked, Equals, 1) |
|
| 93 |
+ c.Check(result.Missed, Equals, 2) |
|
| 94 |
+ c.Check(result.RunError, IsNil) |
|
| 95 |
+} |
|
| 96 |
+ |
|
| 97 |
+// ----------------------------------------------------------------------- |
|
| 98 |
+// Check result aggregation. |
|
| 99 |
+ |
|
| 100 |
+func (s *RunS) TestAdd(c *C) {
|
|
| 101 |
+ result := &Result{
|
|
| 102 |
+ Succeeded: 1, |
|
| 103 |
+ Skipped: 2, |
|
| 104 |
+ Failed: 3, |
|
| 105 |
+ Panicked: 4, |
|
| 106 |
+ FixturePanicked: 5, |
|
| 107 |
+ Missed: 6, |
|
| 108 |
+ ExpectedFailures: 7, |
|
| 109 |
+ } |
|
| 110 |
+ result.Add(&Result{
|
|
| 111 |
+ Succeeded: 10, |
|
| 112 |
+ Skipped: 20, |
|
| 113 |
+ Failed: 30, |
|
| 114 |
+ Panicked: 40, |
|
| 115 |
+ FixturePanicked: 50, |
|
| 116 |
+ Missed: 60, |
|
| 117 |
+ ExpectedFailures: 70, |
|
| 118 |
+ }) |
|
| 119 |
+ c.Check(result.Succeeded, Equals, 11) |
|
| 120 |
+ c.Check(result.Skipped, Equals, 22) |
|
| 121 |
+ c.Check(result.Failed, Equals, 33) |
|
| 122 |
+ c.Check(result.Panicked, Equals, 44) |
|
| 123 |
+ c.Check(result.FixturePanicked, Equals, 55) |
|
| 124 |
+ c.Check(result.Missed, Equals, 66) |
|
| 125 |
+ c.Check(result.ExpectedFailures, Equals, 77) |
|
| 126 |
+ c.Check(result.RunError, IsNil) |
|
| 127 |
+} |
|
| 128 |
+ |
|
| 129 |
+// ----------------------------------------------------------------------- |
|
| 130 |
+// Check the Passed() method. |
|
| 131 |
+ |
|
| 132 |
+func (s *RunS) TestPassed(c *C) {
|
|
| 133 |
+ c.Assert((&Result{}).Passed(), Equals, true)
|
|
| 134 |
+ c.Assert((&Result{Succeeded: 1}).Passed(), Equals, true)
|
|
| 135 |
+ c.Assert((&Result{Skipped: 1}).Passed(), Equals, true)
|
|
| 136 |
+ c.Assert((&Result{Failed: 1}).Passed(), Equals, false)
|
|
| 137 |
+ c.Assert((&Result{Panicked: 1}).Passed(), Equals, false)
|
|
| 138 |
+ c.Assert((&Result{FixturePanicked: 1}).Passed(), Equals, false)
|
|
| 139 |
+ c.Assert((&Result{Missed: 1}).Passed(), Equals, false)
|
|
| 140 |
+ c.Assert((&Result{RunError: errors.New("!")}).Passed(), Equals, false)
|
|
| 141 |
+} |
|
| 142 |
+ |
|
| 143 |
+// ----------------------------------------------------------------------- |
|
| 144 |
+// Check that result printing is working correctly. |
|
| 145 |
+ |
|
| 146 |
+func (s *RunS) TestPrintSuccess(c *C) {
|
|
| 147 |
+ result := &Result{Succeeded: 5}
|
|
| 148 |
+ c.Check(result.String(), Equals, "OK: 5 passed") |
|
| 149 |
+} |
|
| 150 |
+ |
|
| 151 |
+func (s *RunS) TestPrintFailure(c *C) {
|
|
| 152 |
+ result := &Result{Failed: 5}
|
|
| 153 |
+ c.Check(result.String(), Equals, "OOPS: 0 passed, 5 FAILED") |
|
| 154 |
+} |
|
| 155 |
+ |
|
| 156 |
+func (s *RunS) TestPrintSkipped(c *C) {
|
|
| 157 |
+ result := &Result{Skipped: 5}
|
|
| 158 |
+ c.Check(result.String(), Equals, "OK: 0 passed, 5 skipped") |
|
| 159 |
+} |
|
| 160 |
+ |
|
| 161 |
+func (s *RunS) TestPrintExpectedFailures(c *C) {
|
|
| 162 |
+ result := &Result{ExpectedFailures: 5}
|
|
| 163 |
+ c.Check(result.String(), Equals, "OK: 0 passed, 5 expected failures") |
|
| 164 |
+} |
|
| 165 |
+ |
|
| 166 |
+func (s *RunS) TestPrintPanicked(c *C) {
|
|
| 167 |
+ result := &Result{Panicked: 5}
|
|
| 168 |
+ c.Check(result.String(), Equals, "OOPS: 0 passed, 5 PANICKED") |
|
| 169 |
+} |
|
| 170 |
+ |
|
| 171 |
+func (s *RunS) TestPrintFixturePanicked(c *C) {
|
|
| 172 |
+ result := &Result{FixturePanicked: 5}
|
|
| 173 |
+ c.Check(result.String(), Equals, "OOPS: 0 passed, 5 FIXTURE-PANICKED") |
|
| 174 |
+} |
|
| 175 |
+ |
|
| 176 |
+func (s *RunS) TestPrintMissed(c *C) {
|
|
| 177 |
+ result := &Result{Missed: 5}
|
|
| 178 |
+ c.Check(result.String(), Equals, "OOPS: 0 passed, 5 MISSED") |
|
| 179 |
+} |
|
| 180 |
+ |
|
| 181 |
+func (s *RunS) TestPrintAll(c *C) {
|
|
| 182 |
+ result := &Result{Succeeded: 1, Skipped: 2, ExpectedFailures: 3,
|
|
| 183 |
+ Panicked: 4, FixturePanicked: 5, Missed: 6} |
|
| 184 |
+ c.Check(result.String(), Equals, |
|
| 185 |
+ "OOPS: 1 passed, 2 skipped, 3 expected failures, 4 PANICKED, "+ |
|
| 186 |
+ "5 FIXTURE-PANICKED, 6 MISSED") |
|
| 187 |
+} |
|
| 188 |
+ |
|
| 189 |
+func (s *RunS) TestPrintRunError(c *C) {
|
|
| 190 |
+ result := &Result{Succeeded: 1, Failed: 1,
|
|
| 191 |
+ RunError: errors.New("Kaboom!")}
|
|
| 192 |
+ c.Check(result.String(), Equals, "ERROR: Kaboom!") |
|
| 193 |
+} |
|
| 194 |
+ |
|
| 195 |
+// ----------------------------------------------------------------------- |
|
| 196 |
+// Verify that the method pattern flag works correctly. |
|
| 197 |
+ |
|
| 198 |
+func (s *RunS) TestFilterTestName(c *C) {
|
|
| 199 |
+ helper := FixtureHelper{}
|
|
| 200 |
+ output := String{}
|
|
| 201 |
+ runConf := RunConf{Output: &output, Filter: "Test[91]"}
|
|
| 202 |
+ Run(&helper, &runConf) |
|
| 203 |
+ c.Check(helper.calls[0], Equals, "SetUpSuite") |
|
| 204 |
+ c.Check(helper.calls[1], Equals, "SetUpTest") |
|
| 205 |
+ c.Check(helper.calls[2], Equals, "Test1") |
|
| 206 |
+ c.Check(helper.calls[3], Equals, "TearDownTest") |
|
| 207 |
+ c.Check(helper.calls[4], Equals, "TearDownSuite") |
|
| 208 |
+ c.Check(len(helper.calls), Equals, 5) |
|
| 209 |
+} |
|
| 210 |
+ |
|
| 211 |
+func (s *RunS) TestFilterTestNameWithAll(c *C) {
|
|
| 212 |
+ helper := FixtureHelper{}
|
|
| 213 |
+ output := String{}
|
|
| 214 |
+ runConf := RunConf{Output: &output, Filter: ".*"}
|
|
| 215 |
+ Run(&helper, &runConf) |
|
| 216 |
+ c.Check(helper.calls[0], Equals, "SetUpSuite") |
|
| 217 |
+ c.Check(helper.calls[1], Equals, "SetUpTest") |
|
| 218 |
+ c.Check(helper.calls[2], Equals, "Test1") |
|
| 219 |
+ c.Check(helper.calls[3], Equals, "TearDownTest") |
|
| 220 |
+ c.Check(helper.calls[4], Equals, "SetUpTest") |
|
| 221 |
+ c.Check(helper.calls[5], Equals, "Test2") |
|
| 222 |
+ c.Check(helper.calls[6], Equals, "TearDownTest") |
|
| 223 |
+ c.Check(helper.calls[7], Equals, "TearDownSuite") |
|
| 224 |
+ c.Check(len(helper.calls), Equals, 8) |
|
| 225 |
+} |
|
| 226 |
+ |
|
| 227 |
+func (s *RunS) TestFilterSuiteName(c *C) {
|
|
| 228 |
+ helper := FixtureHelper{}
|
|
| 229 |
+ output := String{}
|
|
| 230 |
+ runConf := RunConf{Output: &output, Filter: "FixtureHelper"}
|
|
| 231 |
+ Run(&helper, &runConf) |
|
| 232 |
+ c.Check(helper.calls[0], Equals, "SetUpSuite") |
|
| 233 |
+ c.Check(helper.calls[1], Equals, "SetUpTest") |
|
| 234 |
+ c.Check(helper.calls[2], Equals, "Test1") |
|
| 235 |
+ c.Check(helper.calls[3], Equals, "TearDownTest") |
|
| 236 |
+ c.Check(helper.calls[4], Equals, "SetUpTest") |
|
| 237 |
+ c.Check(helper.calls[5], Equals, "Test2") |
|
| 238 |
+ c.Check(helper.calls[6], Equals, "TearDownTest") |
|
| 239 |
+ c.Check(helper.calls[7], Equals, "TearDownSuite") |
|
| 240 |
+ c.Check(len(helper.calls), Equals, 8) |
|
| 241 |
+} |
|
| 242 |
+ |
|
| 243 |
+func (s *RunS) TestFilterSuiteNameAndTestName(c *C) {
|
|
| 244 |
+ helper := FixtureHelper{}
|
|
| 245 |
+ output := String{}
|
|
| 246 |
+ runConf := RunConf{Output: &output, Filter: "FixtureHelper\\.Test2"}
|
|
| 247 |
+ Run(&helper, &runConf) |
|
| 248 |
+ c.Check(helper.calls[0], Equals, "SetUpSuite") |
|
| 249 |
+ c.Check(helper.calls[1], Equals, "SetUpTest") |
|
| 250 |
+ c.Check(helper.calls[2], Equals, "Test2") |
|
| 251 |
+ c.Check(helper.calls[3], Equals, "TearDownTest") |
|
| 252 |
+ c.Check(helper.calls[4], Equals, "TearDownSuite") |
|
| 253 |
+ c.Check(len(helper.calls), Equals, 5) |
|
| 254 |
+} |
|
| 255 |
+ |
|
| 256 |
+func (s *RunS) TestFilterAllOut(c *C) {
|
|
| 257 |
+ helper := FixtureHelper{}
|
|
| 258 |
+ output := String{}
|
|
| 259 |
+ runConf := RunConf{Output: &output, Filter: "NotFound"}
|
|
| 260 |
+ Run(&helper, &runConf) |
|
| 261 |
+ c.Check(len(helper.calls), Equals, 0) |
|
| 262 |
+} |
|
| 263 |
+ |
|
| 264 |
+func (s *RunS) TestRequirePartialMatch(c *C) {
|
|
| 265 |
+ helper := FixtureHelper{}
|
|
| 266 |
+ output := String{}
|
|
| 267 |
+ runConf := RunConf{Output: &output, Filter: "est"}
|
|
| 268 |
+ Run(&helper, &runConf) |
|
| 269 |
+ c.Check(len(helper.calls), Equals, 8) |
|
| 270 |
+} |
|
| 271 |
+ |
|
| 272 |
+func (s *RunS) TestFilterError(c *C) {
|
|
| 273 |
+ helper := FixtureHelper{}
|
|
| 274 |
+ output := String{}
|
|
| 275 |
+ runConf := RunConf{Output: &output, Filter: "]["}
|
|
| 276 |
+ result := Run(&helper, &runConf) |
|
| 277 |
+ c.Check(result.String(), Equals, |
|
| 278 |
+ "ERROR: Bad filter expression: error parsing regexp: missing closing ]: `[`") |
|
| 279 |
+ c.Check(len(helper.calls), Equals, 0) |
|
| 280 |
+} |
|
| 281 |
+ |
|
| 282 |
+// ----------------------------------------------------------------------- |
|
| 283 |
+// Verify that List works correctly. |
|
| 284 |
+ |
|
| 285 |
+func (s *RunS) TestListFiltered(c *C) {
|
|
| 286 |
+ names := List(&FixtureHelper{}, &RunConf{Filter: "1"})
|
|
| 287 |
+ c.Assert(names, DeepEquals, []string{
|
|
| 288 |
+ "FixtureHelper.Test1", |
|
| 289 |
+ }) |
|
| 290 |
+} |
|
| 291 |
+ |
|
| 292 |
+func (s *RunS) TestList(c *C) {
|
|
| 293 |
+ names := List(&FixtureHelper{}, &RunConf{})
|
|
| 294 |
+ c.Assert(names, DeepEquals, []string{
|
|
| 295 |
+ "FixtureHelper.Test1", |
|
| 296 |
+ "FixtureHelper.Test2", |
|
| 297 |
+ }) |
|
| 298 |
+} |
|
| 299 |
+ |
|
| 300 |
+// ----------------------------------------------------------------------- |
|
| 301 |
+// Verify that verbose mode prints tests which pass as well. |
|
| 302 |
+ |
|
| 303 |
+func (s *RunS) TestVerboseMode(c *C) {
|
|
| 304 |
+ helper := FixtureHelper{}
|
|
| 305 |
+ output := String{}
|
|
| 306 |
+ runConf := RunConf{Output: &output, Verbose: true}
|
|
| 307 |
+ Run(&helper, &runConf) |
|
| 308 |
+ |
|
| 309 |
+ expected := "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Test1\t *[.0-9]+s\n" + |
|
| 310 |
+ "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Test2\t *[.0-9]+s\n" |
|
| 311 |
+ |
|
| 312 |
+ c.Assert(output.value, Matches, expected) |
|
| 313 |
+} |
|
| 314 |
+ |
|
| 315 |
+func (s *RunS) TestVerboseModeWithFailBeforePass(c *C) {
|
|
| 316 |
+ helper := FixtureHelper{panicOn: "Test1"}
|
|
| 317 |
+ output := String{}
|
|
| 318 |
+ runConf := RunConf{Output: &output, Verbose: true}
|
|
| 319 |
+ Run(&helper, &runConf) |
|
| 320 |
+ |
|
| 321 |
+ expected := "(?s).*PANIC.*\n-+\n" + // Should have an extra line. |
|
| 322 |
+ "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Test2\t *[.0-9]+s\n" |
|
| 323 |
+ |
|
| 324 |
+ c.Assert(output.value, Matches, expected) |
|
| 325 |
+} |
|
| 326 |
+ |
|
| 327 |
+// ----------------------------------------------------------------------- |
|
| 328 |
+// Verify the stream output mode. In this mode there's no output caching. |
|
| 329 |
+ |
|
| 330 |
+type StreamHelper struct {
|
|
| 331 |
+ l2 sync.Mutex |
|
| 332 |
+ l3 sync.Mutex |
|
| 333 |
+} |
|
| 334 |
+ |
|
| 335 |
+func (s *StreamHelper) SetUpSuite(c *C) {
|
|
| 336 |
+ c.Log("0")
|
|
| 337 |
+} |
|
| 338 |
+ |
|
| 339 |
+func (s *StreamHelper) Test1(c *C) {
|
|
| 340 |
+ c.Log("1")
|
|
| 341 |
+ s.l2.Lock() |
|
| 342 |
+ s.l3.Lock() |
|
| 343 |
+ go func() {
|
|
| 344 |
+ s.l2.Lock() // Wait for "2". |
|
| 345 |
+ c.Log("3")
|
|
| 346 |
+ s.l3.Unlock() |
|
| 347 |
+ }() |
|
| 348 |
+} |
|
| 349 |
+ |
|
| 350 |
+func (s *StreamHelper) Test2(c *C) {
|
|
| 351 |
+ c.Log("2")
|
|
| 352 |
+ s.l2.Unlock() |
|
| 353 |
+ s.l3.Lock() // Wait for "3". |
|
| 354 |
+ c.Fail() |
|
| 355 |
+ c.Log("4")
|
|
| 356 |
+} |
|
| 357 |
+ |
|
| 358 |
+func (s *RunS) TestStreamMode(c *C) {
|
|
| 359 |
+ helper := &StreamHelper{}
|
|
| 360 |
+ output := String{}
|
|
| 361 |
+ runConf := RunConf{Output: &output, Stream: true}
|
|
| 362 |
+ Run(helper, &runConf) |
|
| 363 |
+ |
|
| 364 |
+ expected := "START: run_test\\.go:[0-9]+: StreamHelper\\.SetUpSuite\n0\n" + |
|
| 365 |
+ "PASS: run_test\\.go:[0-9]+: StreamHelper\\.SetUpSuite\t *[.0-9]+s\n\n" + |
|
| 366 |
+ "START: run_test\\.go:[0-9]+: StreamHelper\\.Test1\n1\n" + |
|
| 367 |
+ "PASS: run_test\\.go:[0-9]+: StreamHelper\\.Test1\t *[.0-9]+s\n\n" + |
|
| 368 |
+ "START: run_test\\.go:[0-9]+: StreamHelper\\.Test2\n2\n3\n4\n" + |
|
| 369 |
+ "FAIL: run_test\\.go:[0-9]+: StreamHelper\\.Test2\n\n" |
|
| 370 |
+ |
|
| 371 |
+ c.Assert(output.value, Matches, expected) |
|
| 372 |
+} |
|
| 373 |
+ |
|
| 374 |
+type StreamMissHelper struct{}
|
|
| 375 |
+ |
|
| 376 |
+func (s *StreamMissHelper) SetUpSuite(c *C) {
|
|
| 377 |
+ c.Log("0")
|
|
| 378 |
+ c.Fail() |
|
| 379 |
+} |
|
| 380 |
+ |
|
| 381 |
+func (s *StreamMissHelper) Test1(c *C) {
|
|
| 382 |
+ c.Log("1")
|
|
| 383 |
+} |
|
| 384 |
+ |
|
| 385 |
+func (s *RunS) TestStreamModeWithMiss(c *C) {
|
|
| 386 |
+ helper := &StreamMissHelper{}
|
|
| 387 |
+ output := String{}
|
|
| 388 |
+ runConf := RunConf{Output: &output, Stream: true}
|
|
| 389 |
+ Run(helper, &runConf) |
|
| 390 |
+ |
|
| 391 |
+ expected := "START: run_test\\.go:[0-9]+: StreamMissHelper\\.SetUpSuite\n0\n" + |
|
| 392 |
+ "FAIL: run_test\\.go:[0-9]+: StreamMissHelper\\.SetUpSuite\n\n" + |
|
| 393 |
+ "START: run_test\\.go:[0-9]+: StreamMissHelper\\.Test1\n" + |
|
| 394 |
+ "MISS: run_test\\.go:[0-9]+: StreamMissHelper\\.Test1\n\n" |
|
| 395 |
+ |
|
| 396 |
+ c.Assert(output.value, Matches, expected) |
|
| 397 |
+} |
|
| 398 |
+ |
|
| 399 |
+// ----------------------------------------------------------------------- |
|
| 400 |
+// Verify that that the keep work dir request indeed does so. |
|
| 401 |
+ |
|
| 402 |
+type WorkDirSuite struct {}
|
|
| 403 |
+ |
|
| 404 |
+func (s *WorkDirSuite) Test(c *C) {
|
|
| 405 |
+ c.MkDir() |
|
| 406 |
+} |
|
| 407 |
+ |
|
| 408 |
+func (s *RunS) TestKeepWorkDir(c *C) {
|
|
| 409 |
+ output := String{}
|
|
| 410 |
+ runConf := RunConf{Output: &output, Verbose: true, KeepWorkDir: true}
|
|
| 411 |
+ result := Run(&WorkDirSuite{}, &runConf)
|
|
| 412 |
+ |
|
| 413 |
+ c.Assert(result.String(), Matches, ".*\nWORK=" + result.WorkDir) |
|
| 414 |
+ |
|
| 415 |
+ stat, err := os.Stat(result.WorkDir) |
|
| 416 |
+ c.Assert(err, IsNil) |
|
| 417 |
+ c.Assert(stat.IsDir(), Equals, true) |
|
| 418 |
+} |