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