parent
bd99a2185e
commit
0c68f52caf
@ -0,0 +1,4 @@ |
||||
_* |
||||
*.swp |
||||
*.[568] |
||||
[568].out |
@ -0,0 +1,25 @@ |
||||
Gocheck - A rich testing framework for Go |
||||
|
||||
Copyright (c) 2010-2013 Gustavo Niemeyer <gustavo@niemeyer.net> |
||||
|
||||
All rights reserved. |
||||
|
||||
Redistribution and use in source and binary forms, with or without |
||||
modification, are permitted provided that the following conditions are met: |
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this |
||||
list of conditions and the following disclaimer. |
||||
2. Redistributions in binary form must reproduce the above copyright notice, |
||||
this list of conditions and the following disclaimer in the documentation |
||||
and/or other materials provided with the distribution. |
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR |
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
@ -0,0 +1,20 @@ |
||||
Instructions |
||||
============ |
||||
|
||||
Install the package with: |
||||
|
||||
go get gopkg.in/check.v1 |
||||
|
||||
Import it with: |
||||
|
||||
import "gopkg.in/check.v1" |
||||
|
||||
and use _check_ as the package name inside the code. |
||||
|
||||
For more details, visit the project page: |
||||
|
||||
* http://labix.org/gocheck |
||||
|
||||
and the API documentation: |
||||
|
||||
* https://gopkg.in/check.v1 |
@ -0,0 +1,2 @@ |
||||
- Assert(slice, Contains, item) |
||||
- Parallel test support |
@ -0,0 +1,163 @@ |
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package check |
||||
|
||||
import ( |
||||
"fmt" |
||||
"runtime" |
||||
"time" |
||||
) |
||||
|
||||
var memStats runtime.MemStats |
||||
|
||||
// testingB is a type passed to Benchmark functions to manage benchmark
|
||||
// timing and to specify the number of iterations to run.
|
||||
type timer struct { |
||||
start time.Time // Time test or benchmark started
|
||||
duration time.Duration |
||||
N int |
||||
bytes int64 |
||||
timerOn bool |
||||
benchTime time.Duration |
||||
// The initial states of memStats.Mallocs and memStats.TotalAlloc.
|
||||
startAllocs uint64 |
||||
startBytes uint64 |
||||
// The net total of this test after being run.
|
||||
netAllocs uint64 |
||||
netBytes uint64 |
||||
} |
||||
|
||||
// StartTimer starts timing a test. This function is called automatically
|
||||
// before a benchmark starts, but it can also used to resume timing after
|
||||
// a call to StopTimer.
|
||||
func (c *C) StartTimer() { |
||||
if !c.timerOn { |
||||
c.start = time.Now() |
||||
c.timerOn = true |
||||
|
||||
runtime.ReadMemStats(&memStats) |
||||
c.startAllocs = memStats.Mallocs |
||||
c.startBytes = memStats.TotalAlloc |
||||
} |
||||
} |
||||
|
||||
// StopTimer stops timing a test. This can be used to pause the timer
|
||||
// while performing complex initialization that you don't
|
||||
// want to measure.
|
||||
func (c *C) StopTimer() { |
||||
if c.timerOn { |
||||
c.duration += time.Now().Sub(c.start) |
||||
c.timerOn = false |
||||
runtime.ReadMemStats(&memStats) |
||||
c.netAllocs += memStats.Mallocs - c.startAllocs |
||||
c.netBytes += memStats.TotalAlloc - c.startBytes |
||||
} |
||||
} |
||||
|
||||
// ResetTimer sets the elapsed benchmark time to zero.
|
||||
// It does not affect whether the timer is running.
|
||||
func (c *C) ResetTimer() { |
||||
if c.timerOn { |
||||
c.start = time.Now() |
||||
runtime.ReadMemStats(&memStats) |
||||
c.startAllocs = memStats.Mallocs |
||||
c.startBytes = memStats.TotalAlloc |
||||
} |
||||
c.duration = 0 |
||||
c.netAllocs = 0 |
||||
c.netBytes = 0 |
||||
} |
||||
|
||||
// SetBytes informs the number of bytes that the benchmark processes
|
||||
// on each iteration. If this is called in a benchmark it will also
|
||||
// report MB/s.
|
||||
func (c *C) SetBytes(n int64) { |
||||
c.bytes = n |
||||
} |
||||
|
||||
func (c *C) nsPerOp() int64 { |
||||
if c.N <= 0 { |
||||
return 0 |
||||
} |
||||
return c.duration.Nanoseconds() / int64(c.N) |
||||
} |
||||
|
||||
func (c *C) mbPerSec() float64 { |
||||
if c.bytes <= 0 || c.duration <= 0 || c.N <= 0 { |
||||
return 0 |
||||
} |
||||
return (float64(c.bytes) * float64(c.N) / 1e6) / c.duration.Seconds() |
||||
} |
||||
|
||||
func (c *C) timerString() string { |
||||
if c.N <= 0 { |
||||
return fmt.Sprintf("%3.3fs", float64(c.duration.Nanoseconds())/1e9) |
||||
} |
||||
mbs := c.mbPerSec() |
||||
mb := "" |
||||
if mbs != 0 { |
||||
mb = fmt.Sprintf("\t%7.2f MB/s", mbs) |
||||
} |
||||
nsop := c.nsPerOp() |
||||
ns := fmt.Sprintf("%10d ns/op", nsop) |
||||
if c.N > 0 && nsop < 100 { |
||||
// The format specifiers here make sure that
|
||||
// the ones digits line up for all three possible formats.
|
||||
if nsop < 10 { |
||||
ns = fmt.Sprintf("%13.2f ns/op", float64(c.duration.Nanoseconds())/float64(c.N)) |
||||
} else { |
||||
ns = fmt.Sprintf("%12.1f ns/op", float64(c.duration.Nanoseconds())/float64(c.N)) |
||||
} |
||||
} |
||||
memStats := "" |
||||
if c.benchMem { |
||||
allocedBytes := fmt.Sprintf("%8d B/op", int64(c.netBytes)/int64(c.N)) |
||||
allocs := fmt.Sprintf("%8d allocs/op", int64(c.netAllocs)/int64(c.N)) |
||||
memStats = fmt.Sprintf("\t%s\t%s", allocedBytes, allocs) |
||||
} |
||||
return fmt.Sprintf("%8d\t%s%s%s", c.N, ns, mb, memStats) |
||||
} |
||||
|
||||
func min(x, y int) int { |
||||
if x > y { |
||||
return y |
||||
} |
||||
return x |
||||
} |
||||
|
||||
func max(x, y int) int { |
||||
if x < y { |
||||
return y |
||||
} |
||||
return x |
||||
} |
||||
|
||||
// roundDown10 rounds a number down to the nearest power of 10.
|
||||
func roundDown10(n int) int { |
||||
var tens = 0 |
||||
// tens = floor(log_10(n))
|
||||
for n > 10 { |
||||
n = n / 10 |
||||
tens++ |
||||
} |
||||
// result = 10^tens
|
||||
result := 1 |
||||
for i := 0; i < tens; i++ { |
||||
result *= 10 |
||||
} |
||||
return result |
||||
} |
||||
|
||||
// roundUp rounds x up to a number of the form [1eX, 2eX, 5eX].
|
||||
func roundUp(n int) int { |
||||
base := roundDown10(n) |
||||
if n < (2 * base) { |
||||
return 2 * base |
||||
} |
||||
if n < (5 * base) { |
||||
return 5 * base |
||||
} |
||||
return 10 * base |
||||
} |
@ -0,0 +1,91 @@ |
||||
// These tests verify the test running logic.
|
||||
|
||||
package check_test |
||||
|
||||
import ( |
||||
"time" |
||||
. "gopkg.in/check.v1" |
||||
) |
||||
|
||||
var benchmarkS = Suite(&BenchmarkS{}) |
||||
|
||||
type BenchmarkS struct{} |
||||
|
||||
func (s *BenchmarkS) TestCountSuite(c *C) { |
||||
suitesRun += 1 |
||||
} |
||||
|
||||
func (s *BenchmarkS) TestBasicTestTiming(c *C) { |
||||
helper := FixtureHelper{sleepOn: "Test1", sleep: 1000000 * time.Nanosecond} |
||||
output := String{} |
||||
runConf := RunConf{Output: &output, Verbose: true} |
||||
Run(&helper, &runConf) |
||||
|
||||
expected := "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Test1\t0\\.001s\n" + |
||||
"PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Test2\t0\\.000s\n" |
||||
c.Assert(output.value, Matches, expected) |
||||
} |
||||
|
||||
func (s *BenchmarkS) TestStreamTestTiming(c *C) { |
||||
helper := FixtureHelper{sleepOn: "SetUpSuite", sleep: 1000000 * time.Nanosecond} |
||||
output := String{} |
||||
runConf := RunConf{Output: &output, Stream: true} |
||||
Run(&helper, &runConf) |
||||
|
||||
expected := "(?s).*\nPASS: check_test\\.go:[0-9]+: FixtureHelper\\.SetUpSuite\t *0\\.001s\n.*" |
||||
c.Assert(output.value, Matches, expected) |
||||
} |
||||
|
||||
func (s *BenchmarkS) TestBenchmark(c *C) { |
||||
helper := FixtureHelper{sleep: 100000} |
||||
output := String{} |
||||
runConf := RunConf{ |
||||
Output: &output, |
||||
Benchmark: true, |
||||
BenchmarkTime: 10000000, |
||||
Filter: "Benchmark1", |
||||
} |
||||
Run(&helper, &runConf) |
||||
c.Check(helper.calls[0], Equals, "SetUpSuite") |
||||
c.Check(helper.calls[1], Equals, "SetUpTest") |
||||
c.Check(helper.calls[2], Equals, "Benchmark1") |
||||
c.Check(helper.calls[3], Equals, "TearDownTest") |
||||
c.Check(helper.calls[4], Equals, "SetUpTest") |
||||
c.Check(helper.calls[5], Equals, "Benchmark1") |
||||
c.Check(helper.calls[6], Equals, "TearDownTest") |
||||
// ... and more.
|
||||
|
||||
expected := "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Benchmark1\t *100\t *[12][0-9]{5} ns/op\n" |
||||
c.Assert(output.value, Matches, expected) |
||||
} |
||||
|
||||
func (s *BenchmarkS) TestBenchmarkBytes(c *C) { |
||||
helper := FixtureHelper{sleep: 100000} |
||||
output := String{} |
||||
runConf := RunConf{ |
||||
Output: &output, |
||||
Benchmark: true, |
||||
BenchmarkTime: 10000000, |
||||
Filter: "Benchmark2", |
||||
} |
||||
Run(&helper, &runConf) |
||||
|
||||
expected := "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Benchmark2\t *100\t *[12][0-9]{5} ns/op\t *[4-9]\\.[0-9]{2} MB/s\n" |
||||
c.Assert(output.value, Matches, expected) |
||||
} |
||||
|
||||
func (s *BenchmarkS) TestBenchmarkMem(c *C) { |
||||
helper := FixtureHelper{sleep: 100000} |
||||
output := String{} |
||||
runConf := RunConf{ |
||||
Output: &output, |
||||
Benchmark: true, |
||||
BenchmarkMem: true, |
||||
BenchmarkTime: 10000000, |
||||
Filter: "Benchmark3", |
||||
} |
||||
Run(&helper, &runConf) |
||||
|
||||
expected := "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Benchmark3\t *100\t *[12][0-9]{5} ns/op\t *[0-9]+ B/op\t *[1-9] allocs/op\n" |
||||
c.Assert(output.value, Matches, expected) |
||||
} |
@ -0,0 +1,82 @@ |
||||
// These initial tests are for bootstrapping. They verify that we can
|
||||
// basically use the testing infrastructure itself to check if the test
|
||||
// system is working.
|
||||
//
|
||||
// These tests use will break down the test runner badly in case of
|
||||
// errors because if they simply fail, we can't be sure the developer
|
||||
// will ever see anything (because failing means the failing system
|
||||
// somehow isn't working! :-)
|
||||
//
|
||||
// Do not assume *any* internal functionality works as expected besides
|
||||
// what's actually tested here.
|
||||
|
||||
package check_test |
||||
|
||||
import ( |
||||
"fmt" |
||||
"gopkg.in/check.v1" |
||||
"strings" |
||||
) |
||||
|
||||
type BootstrapS struct{} |
||||
|
||||
var boostrapS = check.Suite(&BootstrapS{}) |
||||
|
||||
func (s *BootstrapS) TestCountSuite(c *check.C) { |
||||
suitesRun += 1 |
||||
} |
||||
|
||||
func (s *BootstrapS) TestFailedAndFail(c *check.C) { |
||||
if c.Failed() { |
||||
critical("c.Failed() must be false first!") |
||||
} |
||||
c.Fail() |
||||
if !c.Failed() { |
||||
critical("c.Fail() didn't put the test in a failed state!") |
||||
} |
||||
c.Succeed() |
||||
} |
||||
|
||||
func (s *BootstrapS) TestFailedAndSucceed(c *check.C) { |
||||
c.Fail() |
||||
c.Succeed() |
||||
if c.Failed() { |
||||
critical("c.Succeed() didn't put the test back in a non-failed state") |
||||
} |
||||
} |
||||
|
||||
func (s *BootstrapS) TestLogAndGetTestLog(c *check.C) { |
||||
c.Log("Hello there!") |
||||
log := c.GetTestLog() |
||||
if log != "Hello there!\n" { |
||||
critical(fmt.Sprintf("Log() or GetTestLog() is not working! Got: %#v", log)) |
||||
} |
||||
} |
||||
|
||||
func (s *BootstrapS) TestLogfAndGetTestLog(c *check.C) { |
||||
c.Logf("Hello %v", "there!") |
||||
log := c.GetTestLog() |
||||
if log != "Hello there!\n" { |
||||
critical(fmt.Sprintf("Logf() or GetTestLog() is not working! Got: %#v", log)) |
||||
} |
||||
} |
||||
|
||||
func (s *BootstrapS) TestRunShowsErrors(c *check.C) { |
||||
output := String{} |
||||
check.Run(&FailHelper{}, &check.RunConf{Output: &output}) |
||||
if strings.Index(output.value, "Expected failure!") == -1 { |
||||
critical(fmt.Sprintf("RunWithWriter() output did not contain the "+ |
||||
"expected failure! Got: %#v", |
||||
output.value)) |
||||
} |
||||
} |
||||
|
||||
func (s *BootstrapS) TestRunDoesntShowSuccesses(c *check.C) { |
||||
output := String{} |
||||
check.Run(&SuccessHelper{}, &check.RunConf{Output: &output}) |
||||
if strings.Index(output.value, "Expected success!") != -1 { |
||||
critical(fmt.Sprintf("RunWithWriter() output contained a successful "+ |
||||
"test! Got: %#v", |
||||
output.value)) |
||||
} |
||||
} |
@ -0,0 +1,945 @@ |
||||
// Package check is a rich testing extension for Go's testing package.
|
||||
//
|
||||
// For details about the project, see:
|
||||
//
|
||||
// http://labix.org/gocheck
|
||||
//
|
||||
package check |
||||
|
||||
import ( |
||||
"bytes" |
||||
"errors" |
||||
"fmt" |
||||
"io" |
||||
"math/rand" |
||||
"os" |
||||
"path" |
||||
"path/filepath" |
||||
"reflect" |
||||
"regexp" |
||||
"runtime" |
||||
"strconv" |
||||
"strings" |
||||
"sync" |
||||
"time" |
||||
) |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Internal type which deals with suite method calling.
|
||||
|
||||
const ( |
||||
fixtureKd = iota |
||||
testKd |
||||
) |
||||
|
||||
type funcKind int |
||||
|
||||
const ( |
||||
succeededSt = iota |
||||
failedSt |
||||
skippedSt |
||||
panickedSt |
||||
fixturePanickedSt |
||||
missedSt |
||||
) |
||||
|
||||
type funcStatus int |
||||
|
||||
// A method value can't reach its own Method structure.
|
||||
type methodType struct { |
||||
reflect.Value |
||||
Info reflect.Method |
||||
} |
||||
|
||||
func newMethod(receiver reflect.Value, i int) *methodType { |
||||
return &methodType{receiver.Method(i), receiver.Type().Method(i)} |
||||
} |
||||
|
||||
func (method *methodType) PC() uintptr { |
||||
return method.Info.Func.Pointer() |
||||
} |
||||
|
||||
func (method *methodType) suiteName() string { |
||||
t := method.Info.Type.In(0) |
||||
if t.Kind() == reflect.Ptr { |
||||
t = t.Elem() |
||||
} |
||||
return t.Name() |
||||
} |
||||
|
||||
func (method *methodType) String() string { |
||||
return method.suiteName() + "." + method.Info.Name |
||||
} |
||||
|
||||
func (method *methodType) matches(re *regexp.Regexp) bool { |
||||
return (re.MatchString(method.Info.Name) || |
||||
re.MatchString(method.suiteName()) || |
||||
re.MatchString(method.String())) |
||||
} |
||||
|
||||
type C struct { |
||||
method *methodType |
||||
kind funcKind |
||||
testName string |
||||
status funcStatus |
||||
logb *logger |
||||
logw io.Writer |
||||
done chan *C |
||||
reason string |
||||
mustFail bool |
||||
tempDir *tempDir |
||||
benchMem bool |
||||
startTime time.Time |
||||
timer |
||||
} |
||||
|
||||
func (c *C) stopNow() { |
||||
runtime.Goexit() |
||||
} |
||||
|
||||
// logger is a concurrency safe byte.Buffer
|
||||
type logger struct { |
||||
sync.Mutex |
||||
writer bytes.Buffer |
||||
} |
||||
|
||||
func (l *logger) Write(buf []byte) (int, error) { |
||||
l.Lock() |
||||
defer l.Unlock() |
||||
return l.writer.Write(buf) |
||||
} |
||||
|
||||
func (l *logger) WriteTo(w io.Writer) (int64, error) { |
||||
l.Lock() |
||||
defer l.Unlock() |
||||
return l.writer.WriteTo(w) |
||||
} |
||||
|
||||
func (l *logger) String() string { |
||||
l.Lock() |
||||
defer l.Unlock() |
||||
return l.writer.String() |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Handling of temporary files and directories.
|
||||
|
||||
type tempDir struct { |
||||
sync.Mutex |
||||
path string |
||||
counter int |
||||
} |
||||
|
||||
func (td *tempDir) newPath() string { |
||||
td.Lock() |
||||
defer td.Unlock() |
||||
if td.path == "" { |
||||
var err error |
||||
for i := 0; i != 100; i++ { |
||||
path := fmt.Sprintf("%s%ccheck-%d", os.TempDir(), os.PathSeparator, rand.Int()) |
||||
if err = os.Mkdir(path, 0700); err == nil { |
||||
td.path = path |
||||
break |
||||
} |
||||
} |
||||
if td.path == "" { |
||||
panic("Couldn't create temporary directory: " + err.Error()) |
||||
} |
||||
} |
||||
result := filepath.Join(td.path, strconv.Itoa(td.counter)) |
||||
td.counter += 1 |
||||
return result |
||||
} |
||||
|
||||
func (td *tempDir) removeAll() { |
||||
td.Lock() |
||||
defer td.Unlock() |
||||
if td.path != "" { |
||||
err := os.RemoveAll(td.path) |
||||
if err != nil { |
||||
fmt.Fprintf(os.Stderr, "WARNING: Error cleaning up temporaries: "+err.Error()) |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Create a new temporary directory which is automatically removed after
|
||||
// the suite finishes running.
|
||||
func (c *C) MkDir() string { |
||||
path := c.tempDir.newPath() |
||||
if err := os.Mkdir(path, 0700); err != nil { |
||||
panic(fmt.Sprintf("Couldn't create temporary directory %s: %s", path, err.Error())) |
||||
} |
||||
return path |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Low-level logging functions.
|
||||
|
||||
func (c *C) log(args ...interface{}) { |
||||
c.writeLog([]byte(fmt.Sprint(args...) + "\n")) |
||||
} |
||||
|
||||
func (c *C) logf(format string, args ...interface{}) { |
||||
c.writeLog([]byte(fmt.Sprintf(format+"\n", args...))) |
||||
} |
||||
|
||||
func (c *C) logNewLine() { |
||||
c.writeLog([]byte{'\n'}) |
||||
} |
||||
|
||||
func (c *C) writeLog(buf []byte) { |
||||
c.logb.Write(buf) |
||||
if c.logw != nil { |
||||
c.logw.Write(buf) |
||||
} |
||||
} |
||||
|
||||
func hasStringOrError(x interface{}) (ok bool) { |
||||
_, ok = x.(fmt.Stringer) |
||||
if ok { |
||||
return |
||||
} |
||||
_, ok = x.(error) |
||||
return |
||||
} |
||||
|
||||
func (c *C) logValue(label string, value interface{}) { |
||||
if label == "" { |
||||
if hasStringOrError(value) { |
||||
c.logf("... %#v (%q)", value, value) |
||||
} else { |
||||
c.logf("... %#v", value) |
||||
} |
||||
} else if value == nil { |
||||
c.logf("... %s = nil", label) |
||||
} else { |
||||
if hasStringOrError(value) { |
||||
fv := fmt.Sprintf("%#v", value) |
||||
qv := fmt.Sprintf("%q", value) |
||||
if fv != qv { |
||||
c.logf("... %s %s = %s (%s)", label, reflect.TypeOf(value), fv, qv) |
||||
return |
||||
} |
||||
} |
||||
if s, ok := value.(string); ok && isMultiLine(s) { |
||||
c.logf(`... %s %s = "" +`, label, reflect.TypeOf(value)) |
||||
c.logMultiLine(s) |
||||
} else { |
||||
c.logf("... %s %s = %#v", label, reflect.TypeOf(value), value) |
||||
} |
||||
} |
||||
} |
||||
|
||||
func (c *C) logMultiLine(s string) { |
||||
b := make([]byte, 0, len(s)*2) |
||||
i := 0 |
||||
n := len(s) |
||||
for i < n { |
||||
j := i + 1 |
||||
for j < n && s[j-1] != '\n' { |
||||
j++ |
||||
} |
||||
b = append(b, "... "...) |
||||
b = strconv.AppendQuote(b, s[i:j]) |
||||
if j < n { |
||||
b = append(b, " +"...) |
||||
} |
||||
b = append(b, '\n') |
||||
i = j |
||||
} |
||||
c.writeLog(b) |
||||
} |
||||
|
||||
func isMultiLine(s string) bool { |
||||
for i := 0; i+1 < len(s); i++ { |
||||
if s[i] == '\n' { |
||||
return true |
||||
} |
||||
} |
||||
return false |
||||
} |
||||
|
||||
func (c *C) logString(issue string) { |
||||
c.log("... ", issue) |
||||
} |
||||
|
||||
func (c *C) logCaller(skip int) { |
||||
// This is a bit heavier than it ought to be.
|
||||
skip += 1 // Our own frame.
|
||||
pc, callerFile, callerLine, ok := runtime.Caller(skip) |
||||
if !ok { |
||||
return |
||||
} |
||||
var testFile string |
||||
var testLine int |
||||
testFunc := runtime.FuncForPC(c.method.PC()) |
||||
if runtime.FuncForPC(pc) != testFunc { |
||||
for { |
||||
skip += 1 |
||||
if pc, file, line, ok := runtime.Caller(skip); ok { |
||||
// Note that the test line may be different on
|
||||
// distinct calls for the same test. Showing
|
||||
// the "internal" line is helpful when debugging.
|
||||
if runtime.FuncForPC(pc) == testFunc { |
||||
testFile, testLine = file, line |
||||
break |
||||
} |
||||
} else { |
||||
break |
||||
} |
||||
} |
||||
} |
||||
if testFile != "" && (testFile != callerFile || testLine != callerLine) { |
||||
c.logCode(testFile, testLine) |
||||
} |
||||
c.logCode(callerFile, callerLine) |
||||
} |
||||
|
||||
func (c *C) logCode(path string, line int) { |
||||
c.logf("%s:%d:", nicePath(path), line) |
||||
code, err := printLine(path, line) |
||||
if code == "" { |
||||
code = "..." // XXX Open the file and take the raw line.
|
||||
if err != nil { |
||||
code += err.Error() |
||||
} |
||||
} |
||||
c.log(indent(code, " ")) |
||||
} |
||||
|
||||
var valueGo = filepath.Join("reflect", "value.go") |
||||
var asmGo = filepath.Join("runtime", "asm_") |
||||
|
||||
func (c *C) logPanic(skip int, value interface{}) { |
||||
skip++ // Our own frame.
|
||||
initialSkip := skip |
||||
for ; ; skip++ { |
||||
if pc, file, line, ok := runtime.Caller(skip); ok { |
||||
if skip == initialSkip { |
||||
c.logf("... Panic: %s (PC=0x%X)\n", value, pc) |
||||
} |
||||
name := niceFuncName(pc) |
||||
path := nicePath(file) |
||||
if strings.Contains(path, "/gopkg.in/check.v") { |
||||
continue |
||||
} |
||||
if name == "Value.call" && strings.HasSuffix(path, valueGo) { |
||||
continue |
||||
} |
||||
if name == "call16" && strings.Contains(path, asmGo) { |
||||
continue |
||||
} |
||||
c.logf("%s:%d\n in %s", nicePath(file), line, name) |
||||
} else { |
||||
break |
||||
} |
||||
} |
||||
} |
||||
|
||||
func (c *C) logSoftPanic(issue string) { |
||||
c.log("... Panic: ", issue) |
||||
} |
||||
|
||||
func (c *C) logArgPanic(method *methodType, expectedType string) { |
||||
c.logf("... Panic: %s argument should be %s", |
||||
niceFuncName(method.PC()), expectedType) |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Some simple formatting helpers.
|
||||
|
||||
var initWD, initWDErr = os.Getwd() |
||||
|
||||
func init() { |
||||
if initWDErr == nil { |
||||
initWD = strings.Replace(initWD, "\\", "/", -1) + "/" |
||||
} |
||||
} |
||||
|
||||
func nicePath(path string) string { |
||||
if initWDErr == nil { |
||||
if strings.HasPrefix(path, initWD) { |
||||
return path[len(initWD):] |
||||
} |
||||
} |
||||
return path |
||||
} |
||||
|
||||
func niceFuncPath(pc uintptr) string { |
||||
function := runtime.FuncForPC(pc) |
||||
if function != nil { |
||||
filename, line := function.FileLine(pc) |
||||
return fmt.Sprintf("%s:%d", nicePath(filename), line) |
||||
} |
||||
return "<unknown path>" |
||||
} |
||||
|
||||
func niceFuncName(pc uintptr) string { |
||||
function := runtime.FuncForPC(pc) |
||||
if function != nil { |
||||
name := path.Base(function.Name()) |
||||
if i := strings.Index(name, "."); i > 0 { |
||||
name = name[i+1:] |
||||
} |
||||
if strings.HasPrefix(name, "(*") { |
||||
if i := strings.Index(name, ")"); i > 0 { |
||||
name = name[2:i] + name[i+1:] |
||||
} |
||||
} |
||||
if i := strings.LastIndex(name, ".*"); i != -1 { |
||||
name = name[:i] + "." + name[i+2:] |
||||
} |
||||
if i := strings.LastIndex(name, "·"); i != -1 { |
||||
name = name[:i] + "." + name[i+2:] |
||||
} |
||||
return name |
||||
} |
||||
return "<unknown function>" |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Result tracker to aggregate call results.
|
||||
|
||||
type Result struct { |
||||
Succeeded int |
||||
Failed int |
||||
Skipped int |
||||
Panicked int |
||||
FixturePanicked int |
||||
ExpectedFailures int |
||||
Missed int // Not even tried to run, related to a panic in the fixture.
|
||||
RunError error // Houston, we've got a problem.
|
||||
WorkDir string // If KeepWorkDir is true
|
||||
} |
||||
|
||||
type resultTracker struct { |
||||
result Result |
||||
_lastWasProblem bool |
||||
_waiting int |
||||
_missed int |
||||
_expectChan chan *C |
||||
_doneChan chan *C |
||||
_stopChan chan bool |
||||
} |
||||
|
||||
func newResultTracker() *resultTracker { |
||||
return &resultTracker{_expectChan: make(chan *C), // Synchronous
|
||||
_doneChan: make(chan *C, 32), // Asynchronous
|
||||
_stopChan: make(chan bool)} // Synchronous
|
||||
} |
||||
|
||||
func (tracker *resultTracker) start() { |
||||
go tracker._loopRoutine() |
||||
} |
||||
|
||||
func (tracker *resultTracker) waitAndStop() { |
||||
<-tracker._stopChan |
||||
} |
||||
|
||||
func (tracker *resultTracker) expectCall(c *C) { |
||||
tracker._expectChan <- c |
||||
} |
||||
|
||||
func (tracker *resultTracker) callDone(c *C) { |
||||
tracker._doneChan <- c |
||||
} |
||||
|
||||
func (tracker *resultTracker) _loopRoutine() { |
||||
for { |
||||
var c *C |
||||
if tracker._waiting > 0 { |
||||
// Calls still running. Can't stop.
|
||||
select { |
||||
// XXX Reindent this (not now to make diff clear)
|
||||
case c = <-tracker._expectChan: |
||||
tracker._waiting += 1 |
||||
case c = <-tracker._doneChan: |
||||
tracker._waiting -= 1 |
||||
switch c.status { |
||||
case succeededSt: |
||||
if c.kind == testKd { |
||||
if c.mustFail { |
||||
tracker.result.ExpectedFailures++ |
||||
} else { |
||||
tracker.result.Succeeded++ |
||||
} |
||||
} |
||||
case failedSt: |
||||
tracker.result.Failed++ |
||||
case panickedSt: |
||||
if c.kind == fixtureKd { |
||||
tracker.result.FixturePanicked++ |
||||
} else { |
||||
tracker.result.Panicked++ |
||||
} |
||||
case fixturePanickedSt: |
||||
// Track it as missed, since the panic
|
||||
// was on the fixture, not on the test.
|
||||
tracker.result.Missed++ |
||||
case missedSt: |
||||
tracker.result.Missed++ |
||||
case skippedSt: |
||||
if c.kind == testKd { |
||||
tracker.result.Skipped++ |
||||
} |
||||
} |
||||
} |
||||
} else { |
||||
// No calls. Can stop, but no done calls here.
|
||||
select { |
||||
case tracker._stopChan <- true: |
||||
return |
||||
case c = <-tracker._expectChan: |
||||
tracker._waiting += 1 |
||||
case c = <-tracker._doneChan: |
||||
panic("Tracker got an unexpected done call.") |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// The underlying suite runner.
|
||||
|
||||
type suiteRunner struct { |
||||
suite interface{} |
||||
setUpSuite, tearDownSuite *methodType |
||||
setUpTest, tearDownTest *methodType |
||||
tests []*methodType |
||||
tracker *resultTracker |
||||
tempDir *tempDir |
||||
keepDir bool |
||||
output *outputWriter |
||||
reportedProblemLast bool |
||||
benchTime time.Duration |
||||
benchMem bool |
||||
} |
||||
|
||||
type RunConf struct { |
||||
Output io.Writer |
||||
Stream bool |
||||
Verbose bool |
||||
Filter string |
||||
Benchmark bool |
||||
BenchmarkTime time.Duration // Defaults to 1 second
|
||||
BenchmarkMem bool |
||||
KeepWorkDir bool |
||||
} |
||||
|
||||
// Create a new suiteRunner able to run all methods in the given suite.
|
||||
func newSuiteRunner(suite interface{}, runConf *RunConf) *suiteRunner { |
||||
var conf RunConf |
||||
if runConf != nil { |
||||
conf = *runConf |
||||
} |
||||
if conf.Output == nil { |
||||
conf.Output = os.Stdout |
||||
} |
||||
if conf.Benchmark { |
||||
conf.Verbose = true |
||||
} |
||||
|
||||
suiteType := reflect.TypeOf(suite) |
||||
suiteNumMethods := suiteType.NumMethod() |
||||
suiteValue := reflect.ValueOf(suite) |
||||
|
||||
runner := &suiteRunner{ |
||||
suite: suite, |
||||
output: newOutputWriter(conf.Output, conf.Stream, conf.Verbose), |
||||
tracker: newResultTracker(), |
||||
benchTime: conf.BenchmarkTime, |
||||
benchMem: conf.BenchmarkMem, |
||||
tempDir: &tempDir{}, |
||||
keepDir: conf.KeepWorkDir, |
||||
tests: make([]*methodType, 0, suiteNumMethods), |
||||
} |
||||
if runner.benchTime == 0 { |
||||
runner.benchTime = 1 * time.Second |
||||
} |
||||
|
||||
var filterRegexp *regexp.Regexp |
||||
if conf.Filter != "" { |
||||
if regexp, err := regexp.Compile(conf.Filter); err != nil { |
||||
msg := "Bad filter expression: " + err.Error() |
||||
runner.tracker.result.RunError = errors.New(msg) |
||||
return runner |
||||
} else { |
||||
filterRegexp = regexp |
||||
} |
||||
} |
||||
|
||||
for i := 0; i != suiteNumMethods; i++ { |
||||
method := newMethod(suiteValue, i) |
||||
switch method.Info.Name { |
||||
case "SetUpSuite": |
||||
runner.setUpSuite = method |
||||
case "TearDownSuite": |
||||
runner.tearDownSuite = method |
||||
case "SetUpTest": |
||||
runner.setUpTest = method |
||||
case "TearDownTest": |
||||
runner.tearDownTest = method |
||||
default: |
||||
prefix := "Test" |
||||
if conf.Benchmark { |
||||
prefix = "Benchmark" |
||||
} |
||||
if !strings.HasPrefix(method.Info.Name, prefix) { |
||||
continue |
||||
} |
||||
if filterRegexp == nil || method.matches(filterRegexp) { |
||||
runner.tests = append(runner.tests, method) |
||||
} |
||||
} |
||||
} |
||||
return runner |
||||
} |
||||
|
||||
// Run all methods in the given suite.
|
||||
func (runner *suiteRunner) run() *Result { |
||||
if runner.tracker.result.RunError == nil && len(runner.tests) > 0 { |
||||
runner.tracker.start() |
||||
if runner.checkFixtureArgs() { |
||||
c := runner.runFixture(runner.setUpSuite, "", nil) |
||||
if c == nil || c.status == succeededSt { |
||||
for i := 0; i != len(runner.tests); i++ { |
||||
c := runner.runTest(runner.tests[i]) |
||||
if c.status == fixturePanickedSt { |
||||
runner.skipTests(missedSt, runner.tests[i+1:]) |
||||
break |
||||
} |
||||
} |
||||
} else if c != nil && c.status == skippedSt { |
||||
runner.skipTests(skippedSt, runner.tests) |
||||
} else { |
||||
runner.skipTests(missedSt, runner.tests) |
||||
} |
||||
runner.runFixture(runner.tearDownSuite, "", nil) |
||||
} else { |
||||
runner.skipTests(missedSt, runner.tests) |
||||
} |
||||
runner.tracker.waitAndStop() |
||||
if runner.keepDir { |
||||
runner.tracker.result.WorkDir = runner.tempDir.path |
||||
} else { |
||||
runner.tempDir.removeAll() |
||||
} |
||||
} |
||||
return &runner.tracker.result |
||||
} |
||||
|
||||
// Create a call object with the given suite method, and fork a
|
||||
// goroutine with the provided dispatcher for running it.
|
||||
func (runner *suiteRunner) forkCall(method *methodType, kind funcKind, testName string, logb *logger, dispatcher func(c *C)) *C { |
||||
var logw io.Writer |
||||
if runner.output.Stream { |
||||
logw = runner.output |
||||
} |
||||
if logb == nil { |
||||
logb = new(logger) |
||||
} |
||||
c := &C{ |
||||
method: method, |
||||
kind: kind, |
||||
testName: testName, |
||||
logb: logb, |
||||
logw: logw, |
||||
tempDir: runner.tempDir, |
||||
done: make(chan *C, 1), |
||||
timer: timer{benchTime: runner.benchTime}, |
||||
startTime: time.Now(), |
||||
benchMem: runner.benchMem, |
||||
} |
||||
runner.tracker.expectCall(c) |
||||
go (func() { |
||||
runner.reportCallStarted(c) |
||||
defer runner.callDone(c) |
||||
dispatcher(c) |
||||
})() |
||||
return c |
||||
} |
||||
|
||||
// Same as forkCall(), but wait for call to finish before returning.
|
||||
func (runner *suiteRunner) runFunc(method *methodType, kind funcKind, testName string, logb *logger, dispatcher func(c *C)) *C { |
||||
c := runner.forkCall(method, kind, testName, logb, dispatcher) |
||||
<-c.done |
||||
return c |
||||
} |
||||
|
||||
// Handle a finished call. If there were any panics, update the call status
|
||||
// accordingly. Then, mark the call as done and report to the tracker.
|
||||
func (runner *suiteRunner) callDone(c *C) { |
||||
value := recover() |
||||
if value != nil { |
||||
switch v := value.(type) { |
||||
case *fixturePanic: |
||||
if v.status == skippedSt { |
||||
c.status = skippedSt |
||||
} else { |
||||
c.logSoftPanic("Fixture has panicked (see related PANIC)") |
||||
c.status = fixturePanickedSt |
||||
} |
||||
default: |
||||
c.logPanic(1, value) |
||||
c.status = panickedSt |
||||
} |
||||
} |
||||
if c.mustFail { |
||||
switch c.status { |
||||
case failedSt: |
||||
c.status = succeededSt |
||||
case succeededSt: |
||||
c.status = failedSt |
||||
c.logString("Error: Test succeeded, but was expected to fail") |
||||
c.logString("Reason: " + c.reason) |
||||
} |
||||
} |
||||
|
||||
runner.reportCallDone(c) |
||||
c.done <- c |
||||
} |
||||
|
||||
// Runs a fixture call synchronously. The fixture will still be run in a
|
||||
// goroutine like all suite methods, but this method will not return
|
||||
// while the fixture goroutine is not done, because the fixture must be
|
||||
// run in a desired order.
|
||||
func (runner *suiteRunner) runFixture(method *methodType, testName string, logb *logger) *C { |
||||
if method != nil { |
||||
c := runner.runFunc(method, fixtureKd, testName, logb, func(c *C) { |
||||
c.ResetTimer() |
||||
c.StartTimer() |
||||
defer c.StopTimer() |
||||
c.method.Call([]reflect.Value{reflect.ValueOf(c)}) |
||||
}) |
||||
return c |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// Run the fixture method with runFixture(), but panic with a fixturePanic{}
|
||||
// in case the fixture method panics. This makes it easier to track the
|
||||
// fixture panic together with other call panics within forkTest().
|
||||
func (runner *suiteRunner) runFixtureWithPanic(method *methodType, testName string, logb *logger, skipped *bool) *C { |
||||
if skipped != nil && *skipped { |
||||
return nil |
||||
} |
||||
c := runner.runFixture(method, testName, logb) |
||||
if c != nil && c.status != succeededSt { |
||||
if skipped != nil { |
||||
*skipped = c.status == skippedSt |
||||
} |
||||
panic(&fixturePanic{c.status, method}) |
||||
} |
||||
return c |
||||
} |
||||
|
||||
type fixturePanic struct { |
||||
status funcStatus |
||||
method *methodType |
||||
} |
||||
|
||||
// Run the suite test method, together with the test-specific fixture,
|
||||
// asynchronously.
|
||||
func (runner *suiteRunner) forkTest(method *methodType) *C { |
||||
testName := method.String() |
||||
return runner.forkCall(method, testKd, testName, nil, func(c *C) { |
||||
var skipped bool |
||||
defer runner.runFixtureWithPanic(runner.tearDownTest, testName, nil, &skipped) |
||||
defer c.StopTimer() |
||||
benchN := 1 |
||||
for { |
||||
runner.runFixtureWithPanic(runner.setUpTest, testName, c.logb, &skipped) |
||||
mt := c.method.Type() |
||||
if mt.NumIn() != 1 || mt.In(0) != reflect.TypeOf(c) { |
||||
// Rather than a plain panic, provide a more helpful message when
|
||||
// the argument type is incorrect.
|
||||
c.status = panickedSt |
||||
c.logArgPanic(c.method, "*check.C") |
||||
return |
||||
} |
||||
if strings.HasPrefix(c.method.Info.Name, "Test") { |
||||
c.ResetTimer() |
||||
c.StartTimer() |
||||
c.method.Call([]reflect.Value{reflect.ValueOf(c)}) |
||||
return |
||||
} |
||||
if !strings.HasPrefix(c.method.Info.Name, "Benchmark") { |
||||
panic("unexpected method prefix: " + c.method.Info.Name) |
||||
} |
||||
|
||||
runtime.GC() |
||||
c.N = benchN |
||||
c.ResetTimer() |
||||
c.StartTimer() |
||||
c.method.Call([]reflect.Value{reflect.ValueOf(c)}) |
||||
c.StopTimer() |
||||
if c.status != succeededSt || c.duration >= c.benchTime || benchN >= 1e9 { |
||||
return |
||||
} |
||||
perOpN := int(1e9) |
||||
if c.nsPerOp() != 0 { |
||||
perOpN = int(c.benchTime.Nanoseconds() / c.nsPerOp()) |
||||
} |
||||
|
||||
// Logic taken from the stock testing package:
|
||||
// - Run more iterations than we think we'll need for a second (1.5x).
|
||||
// - Don't grow too fast in case we had timing errors previously.
|
||||
// - Be sure to run at least one more than last time.
|
||||
benchN = max(min(perOpN+perOpN/2, 100*benchN), benchN+1) |
||||
benchN = roundUp(benchN) |
||||
|
||||
skipped = true // Don't run the deferred one if this panics.
|
||||
runner.runFixtureWithPanic(runner.tearDownTest, testName, nil, nil) |
||||
skipped = false |
||||
} |
||||
}) |
||||
} |
||||
|
||||
// Same as forkTest(), but wait for the test to finish before returning.
|
||||
func (runner *suiteRunner) runTest(method *methodType) *C { |
||||
c := runner.forkTest(method) |
||||
<-c.done |
||||
return c |
||||
} |
||||
|
||||
// Helper to mark tests as skipped or missed. A bit heavy for what
|
||||
// it does, but it enables homogeneous handling of tracking, including
|
||||
// nice verbose output.
|
||||
func (runner *suiteRunner) skipTests(status funcStatus, methods []*methodType) { |
||||
for _, method := range methods { |
||||
runner.runFunc(method, testKd, "", nil, func(c *C) { |
||||
c.status = status |
||||
}) |
||||
} |
||||
} |
||||
|
||||
// Verify if the fixture arguments are *check.C. In case of errors,
|
||||
// log the error as a panic in the fixture method call, and return false.
|
||||
func (runner *suiteRunner) checkFixtureArgs() bool { |
||||
succeeded := true |
||||
argType := reflect.TypeOf(&C{}) |
||||
for _, method := range []*methodType{runner.setUpSuite, runner.tearDownSuite, runner.setUpTest, runner.tearDownTest} { |
||||
if method != nil { |
||||
mt := method.Type() |
||||
if mt.NumIn() != 1 || mt.In(0) != argType { |
||||
succeeded = false |
||||
runner.runFunc(method, fixtureKd, "", nil, func(c *C) { |
||||
c.logArgPanic(method, "*check.C") |
||||
c.status = panickedSt |
||||
}) |
||||
} |
||||
} |
||||
} |
||||
return succeeded |
||||
} |
||||
|
||||
func (runner *suiteRunner) reportCallStarted(c *C) { |
||||
runner.output.WriteCallStarted("START", c) |
||||
} |
||||
|
||||
func (runner *suiteRunner) reportCallDone(c *C) { |
||||
runner.tracker.callDone(c) |
||||
switch c.status { |
||||
case succeededSt: |
||||
if c.mustFail { |
||||
runner.output.WriteCallSuccess("FAIL EXPECTED", c) |
||||
} else { |
||||
runner.output.WriteCallSuccess("PASS", c) |
||||
} |
||||
case skippedSt: |
||||
runner.output.WriteCallSuccess("SKIP", c) |
||||
case failedSt: |
||||
runner.output.WriteCallProblem("FAIL", c) |
||||
case panickedSt: |
||||
runner.output.WriteCallProblem("PANIC", c) |
||||
case fixturePanickedSt: |
||||
// That's a testKd call reporting that its fixture
|
||||
// has panicked. The fixture call which caused the
|
||||
// panic itself was tracked above. We'll report to
|
||||
// aid debugging.
|
||||
runner.output.WriteCallProblem("PANIC", c) |
||||
case missedSt: |
||||
runner.output.WriteCallSuccess("MISS", c) |
||||
} |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Output writer manages atomic output writing according to settings.
|
||||
|
||||
type outputWriter struct { |
||||
m sync.Mutex |
||||
writer io.Writer |
||||
wroteCallProblemLast bool |
||||
Stream bool |
||||
Verbose bool |
||||
} |
||||
|
||||
func newOutputWriter(writer io.Writer, stream, verbose bool) *outputWriter { |
||||
return &outputWriter{writer: writer, Stream: stream, Verbose: verbose} |
||||
} |
||||
|
||||
func (ow *outputWriter) Write(content []byte) (n int, err error) { |
||||
ow.m.Lock() |
||||
n, err = ow.writer.Write(content) |
||||
ow.m.Unlock() |
||||
return |
||||
} |
||||
|
||||
func (ow *outputWriter) WriteCallStarted(label string, c *C) { |
||||
if ow.Stream { |
||||
header := renderCallHeader(label, c, "", "\n") |
||||
ow.m.Lock() |
||||
ow.writer.Write([]byte(header)) |
||||
ow.m.Unlock() |
||||
} |
||||
} |
||||
|
||||
func (ow *outputWriter) WriteCallProblem(label string, c *C) { |
||||
var prefix string |
||||
if !ow.Stream { |
||||
prefix = "\n-----------------------------------" + |
||||
"-----------------------------------\n" |
||||
} |
||||
header := renderCallHeader(label, c, prefix, "\n\n") |
||||
ow.m.Lock() |
||||
ow.wroteCallProblemLast = true |
||||
ow.writer.Write([]byte(header)) |
||||
if !ow.Stream { |
||||
c.logb.WriteTo(ow.writer) |
||||
} |
||||
ow.m.Unlock() |
||||
} |
||||
|
||||
func (ow *outputWriter) WriteCallSuccess(label string, c *C) { |
||||
if ow.Stream || (ow.Verbose && c.kind == testKd) { |
||||
// TODO Use a buffer here.
|
||||
var suffix string |
||||
if c.reason != "" { |
||||
suffix = " (" + c.reason + ")" |
||||
} |
||||
if c.status == succeededSt { |
||||
suffix += "\t" + c.timerString() |
||||
} |
||||
suffix += "\n" |
||||
if ow.Stream { |
||||
suffix += "\n" |
||||
} |
||||
header := renderCallHeader(label, c, "", suffix) |
||||
ow.m.Lock() |
||||
// Resist temptation of using line as prefix above due to race.
|
||||
if !ow.Stream && ow.wroteCallProblemLast { |
||||
header = "\n-----------------------------------" + |
||||
"-----------------------------------\n" + |
||||
header |
||||
} |
||||
ow.wroteCallProblemLast = false |
||||
ow.writer.Write([]byte(header)) |
||||
ow.m.Unlock() |
||||
} |
||||
} |
||||
|
||||
func renderCallHeader(label string, c *C, prefix, suffix string) string { |
||||
pc := c.method.PC() |
||||
return fmt.Sprintf("%s%s: %s: %s%s", prefix, label, niceFuncPath(pc), |
||||
niceFuncName(pc), suffix) |
||||
} |
@ -0,0 +1,207 @@ |
||||
// This file contains just a few generic helpers which are used by the
|
||||
// other test files.
|
||||
|
||||
package check_test |
||||
|
||||
import ( |
||||
"flag" |
||||
"fmt" |
||||
"os" |
||||
"regexp" |
||||
"runtime" |
||||
"testing" |
||||
"time" |
||||
|
||||
"gopkg.in/check.v1" |
||||
) |
||||
|
||||
// We count the number of suites run at least to get a vague hint that the
|
||||
// test suite is behaving as it should. Otherwise a bug introduced at the
|
||||
// very core of the system could go unperceived.
|
||||
const suitesRunExpected = 8 |
||||
|
||||
var suitesRun int = 0 |
||||
|
||||
func Test(t *testing.T) { |
||||
check.TestingT(t) |
||||
if suitesRun != suitesRunExpected && flag.Lookup("check.f").Value.String() == "" { |
||||
critical(fmt.Sprintf("Expected %d suites to run rather than %d", |
||||
suitesRunExpected, suitesRun)) |
||||
} |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Helper functions.
|
||||
|
||||
// Break down badly. This is used in test cases which can't yet assume
|
||||
// that the fundamental bits are working.
|
||||
func critical(error string) { |
||||
fmt.Fprintln(os.Stderr, "CRITICAL: "+error) |
||||
os.Exit(1) |
||||
} |
||||
|
||||
// Return the file line where it's called.
|
||||
func getMyLine() int { |
||||
if _, _, line, ok := runtime.Caller(1); ok { |
||||
return line |
||||
} |
||||
return -1 |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Helper type implementing a basic io.Writer for testing output.
|
||||
|
||||
// Type implementing the io.Writer interface for analyzing output.
|
||||
type String struct { |
||||
value string |
||||
} |
||||
|
||||
// The only function required by the io.Writer interface. Will append
|
||||
// written data to the String.value string.
|
||||
func (s *String) Write(p []byte) (n int, err error) { |
||||
s.value += string(p) |
||||
return len(p), nil |
||||
} |
||||
|
||||
// Trivial wrapper to test errors happening on a different file
|
||||
// than the test itself.
|
||||
func checkEqualWrapper(c *check.C, obtained, expected interface{}) (result bool, line int) { |
||||
return c.Check(obtained, check.Equals, expected), getMyLine() |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Helper suite for testing basic fail behavior.
|
||||
|
||||
type FailHelper struct { |
||||
testLine int |
||||
} |
||||
|
||||
func (s *FailHelper) TestLogAndFail(c *check.C) { |
||||
s.testLine = getMyLine() - 1 |
||||
c.Log("Expected failure!") |
||||
c.Fail() |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Helper suite for testing basic success behavior.
|
||||
|
||||
type SuccessHelper struct{} |
||||
|
||||
func (s *SuccessHelper) TestLogAndSucceed(c *check.C) { |
||||
c.Log("Expected success!") |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Helper suite for testing ordering and behavior of fixture.
|
||||
|
||||
type FixtureHelper struct { |
||||
calls []string |
||||
panicOn string |
||||
skip bool |
||||
skipOnN int |
||||
sleepOn string |
||||
sleep time.Duration |
||||
bytes int64 |
||||
} |
||||
|
||||
func (s *FixtureHelper) trace(name string, c *check.C) { |
||||
s.calls = append(s.calls, name) |
||||
if name == s.panicOn { |
||||
panic(name) |
||||
} |
||||
if s.sleep > 0 && s.sleepOn == name { |
||||
time.Sleep(s.sleep) |
||||
} |
||||
if s.skip && s.skipOnN == len(s.calls)-1 { |
||||
c.Skip("skipOnN == n") |
||||
} |
||||
} |
||||
|
||||
func (s *FixtureHelper) SetUpSuite(c *check.C) { |
||||
s.trace("SetUpSuite", c) |
||||
} |
||||
|
||||
func (s *FixtureHelper) TearDownSuite(c *check.C) { |
||||
s.trace("TearDownSuite", c) |
||||
} |
||||
|
||||
func (s *FixtureHelper) SetUpTest(c *check.C) { |
||||
s.trace("SetUpTest", c) |
||||
} |
||||
|
||||
func (s *FixtureHelper) TearDownTest(c *check.C) { |
||||
s.trace("TearDownTest", c) |
||||
} |
||||
|
||||
func (s *FixtureHelper) Test1(c *check.C) { |
||||
s.trace("Test1", c) |
||||
} |
||||
|
||||
func (s *FixtureHelper) Test2(c *check.C) { |
||||
s.trace("Test2", c) |
||||
} |
||||
|
||||
func (s *FixtureHelper) Benchmark1(c *check.C) { |
||||
s.trace("Benchmark1", c) |
||||
for i := 0; i < c.N; i++ { |
||||
time.Sleep(s.sleep) |
||||
} |
||||
} |
||||
|
||||
func (s *FixtureHelper) Benchmark2(c *check.C) { |
||||
s.trace("Benchmark2", c) |
||||
c.SetBytes(1024) |
||||
for i := 0; i < c.N; i++ { |
||||
time.Sleep(s.sleep) |
||||
} |
||||
} |
||||
|
||||
func (s *FixtureHelper) Benchmark3(c *check.C) { |
||||
var x []int64 |
||||
s.trace("Benchmark3", c) |
||||
for i := 0; i < c.N; i++ { |
||||
time.Sleep(s.sleep) |
||||
x = make([]int64, 5) |
||||
_ = x |
||||
} |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Helper which checks the state of the test and ensures that it matches
|
||||
// the given expectations. Depends on c.Errorf() working, so shouldn't
|
||||
// be used to test this one function.
|
||||
|
||||
type expectedState struct { |
||||
name string |
||||
result interface{} |
||||
failed bool |
||||
log string |
||||
} |
||||
|
||||
// Verify the state of the test. Note that since this also verifies if
|
||||
// the test is supposed to be in a failed state, no other checks should
|
||||
// be done in addition to what is being tested.
|
||||
func checkState(c *check.C, result interface{}, expected *expectedState) { |
||||
failed := c.Failed() |
||||
c.Succeed() |
||||
log := c.GetTestLog() |
||||
matched, matchError := regexp.MatchString("^"+expected.log+"$", log) |
||||
if matchError != nil { |
||||
c.Errorf("Error in matching expression used in testing %s", |
||||
expected.name) |
||||
} else if !matched { |
||||
c.Errorf("%s logged:\n----------\n%s----------\n\nExpected:\n----------\n%s\n----------", |
||||
expected.name, log, expected.log) |
||||
} |
||||
if result != expected.result { |
||||
c.Errorf("%s returned %#v rather than %#v", |
||||
expected.name, result, expected.result) |
||||
} |
||||
if failed != expected.failed { |
||||
if failed { |
||||
c.Errorf("%s has failed when it shouldn't", expected.name) |
||||
} else { |
||||
c.Errorf("%s has not failed when it should", expected.name) |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,458 @@ |
||||
package check |
||||
|
||||
import ( |
||||
"fmt" |
||||
"reflect" |
||||
"regexp" |
||||
) |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// CommentInterface and Commentf helper, to attach extra information to checks.
|
||||
|
||||
type comment struct { |
||||
format string |
||||
args []interface{} |
||||
} |
||||
|
||||
// Commentf returns an infomational value to use with Assert or Check calls.
|
||||
// If the checker test fails, the provided arguments will be passed to
|
||||
// fmt.Sprintf, and will be presented next to the logged failure.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// c.Assert(v, Equals, 42, Commentf("Iteration #%d failed.", i))
|
||||
//
|
||||
// Note that if the comment is constant, a better option is to
|
||||
// simply use a normal comment right above or next to the line, as
|
||||
// it will also get printed with any errors:
|
||||
//
|
||||
// c.Assert(l, Equals, 8192) // Ensure buffer size is correct (bug #123)
|
||||
//
|
||||
func Commentf(format string, args ...interface{}) CommentInterface { |
||||
return &comment{format, args} |
||||
} |
||||
|
||||
// CommentInterface must be implemented by types that attach extra
|
||||
// information to failed checks. See the Commentf function for details.
|
||||
type CommentInterface interface { |
||||
CheckCommentString() string |
||||
} |
||||
|
||||
func (c *comment) CheckCommentString() string { |
||||
return fmt.Sprintf(c.format, c.args...) |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// The Checker interface.
|
||||
|
||||
// The Checker interface must be provided by checkers used with
|
||||
// the Assert and Check verification methods.
|
||||
type Checker interface { |
||||
Info() *CheckerInfo |
||||
Check(params []interface{}, names []string) (result bool, error string) |
||||
} |
||||
|
||||
// See the Checker interface.
|
||||
type CheckerInfo struct { |
||||
Name string |
||||
Params []string |
||||
} |
||||
|
||||
func (info *CheckerInfo) Info() *CheckerInfo { |
||||
return info |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Not checker logic inverter.
|
||||
|
||||
// The Not checker inverts the logic of the provided checker. The
|
||||
// resulting checker will succeed where the original one failed, and
|
||||
// vice-versa.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// c.Assert(a, Not(Equals), b)
|
||||
//
|
||||
func Not(checker Checker) Checker { |
||||
return ¬Checker{checker} |
||||
} |
||||
|
||||
type notChecker struct { |
||||
sub Checker |
||||
} |
||||
|
||||
func (checker *notChecker) Info() *CheckerInfo { |
||||
info := *checker.sub.Info() |
||||
info.Name = "Not(" + info.Name + ")" |
||||
return &info |
||||
} |
||||
|
||||
func (checker *notChecker) Check(params []interface{}, names []string) (result bool, error string) { |
||||
result, error = checker.sub.Check(params, names) |
||||
result = !result |
||||
return |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// IsNil checker.
|
||||
|
||||
type isNilChecker struct { |
||||
*CheckerInfo |
||||
} |
||||
|
||||
// The IsNil checker tests whether the obtained value is nil.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// c.Assert(err, IsNil)
|
||||
//
|
||||
var IsNil Checker = &isNilChecker{ |
||||
&CheckerInfo{Name: "IsNil", Params: []string{"value"}}, |
||||
} |
||||
|
||||
func (checker *isNilChecker) Check(params []interface{}, names []string) (result bool, error string) { |
||||
return isNil(params[0]), "" |
||||
} |
||||
|
||||
func isNil(obtained interface{}) (result bool) { |
||||
if obtained == nil { |
||||
result = true |
||||
} else { |
||||
switch v := reflect.ValueOf(obtained); v.Kind() { |
||||
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: |
||||
return v.IsNil() |
||||
} |
||||
} |
||||
return |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// NotNil checker. Alias for Not(IsNil), since it's so common.
|
||||
|
||||
type notNilChecker struct { |
||||
*CheckerInfo |
||||
} |
||||
|
||||
// The NotNil checker verifies that the obtained value is not nil.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// c.Assert(iface, NotNil)
|
||||
//
|
||||
// This is an alias for Not(IsNil), made available since it's a
|
||||
// fairly common check.
|
||||
//
|
||||
var NotNil Checker = ¬NilChecker{ |
||||
&CheckerInfo{Name: "NotNil", Params: []string{"value"}}, |
||||
} |
||||
|
||||
func (checker *notNilChecker) Check(params []interface{}, names []string) (result bool, error string) { |
||||
return !isNil(params[0]), "" |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Equals checker.
|
||||
|
||||
type equalsChecker struct { |
||||
*CheckerInfo |
||||
} |
||||
|
||||
// The Equals checker verifies that the obtained value is equal to
|
||||
// the expected value, according to usual Go semantics for ==.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// c.Assert(value, Equals, 42)
|
||||
//
|
||||
var Equals Checker = &equalsChecker{ |
||||
&CheckerInfo{Name: "Equals", Params: []string{"obtained", "expected"}}, |
||||
} |
||||
|
||||
func (checker *equalsChecker) Check(params []interface{}, names []string) (result bool, error string) { |
||||
defer func() { |
||||
if v := recover(); v != nil { |
||||
result = false |
||||
error = fmt.Sprint(v) |
||||
} |
||||
}() |
||||
return params[0] == params[1], "" |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// DeepEquals checker.
|
||||
|
||||
type deepEqualsChecker struct { |
||||
*CheckerInfo |
||||
} |
||||
|
||||
// The DeepEquals checker verifies that the obtained value is deep-equal to
|
||||
// the expected value. The check will work correctly even when facing
|
||||
// slices, interfaces, and values of different types (which always fail
|
||||
// the test).
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// c.Assert(value, DeepEquals, 42)
|
||||
// c.Assert(array, DeepEquals, []string{"hi", "there"})
|
||||
//
|
||||
var DeepEquals Checker = &deepEqualsChecker{ |
||||
&CheckerInfo{Name: "DeepEquals", Params: []string{"obtained", "expected"}}, |
||||
} |
||||
|
||||
func (checker *deepEqualsChecker) Check(params []interface{}, names []string) (result bool, error string) { |
||||
return reflect.DeepEqual(params[0], params[1]), "" |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// HasLen checker.
|
||||
|
||||
type hasLenChecker struct { |
||||
*CheckerInfo |
||||
} |
||||
|
||||
// The HasLen checker verifies that the obtained value has the
|
||||
// provided length. In many cases this is superior to using Equals
|
||||
// in conjuction with the len function because in case the check
|
||||
// fails the value itself will be printed, instead of its length,
|
||||
// providing more details for figuring the problem.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// c.Assert(list, HasLen, 5)
|
||||
//
|
||||
var HasLen Checker = &hasLenChecker{ |
||||
&CheckerInfo{Name: "HasLen", Params: []string{"obtained", "n"}}, |
||||
} |
||||
|
||||
func (checker *hasLenChecker) Check(params []interface{}, names []string) (result bool, error string) { |
||||
n, ok := params[1].(int) |
||||
if !ok { |
||||
return false, "n must be an int" |
||||
} |
||||
value := reflect.ValueOf(params[0]) |
||||
switch value.Kind() { |
||||
case reflect.Map, reflect.Array, reflect.Slice, reflect.Chan, reflect.String: |
||||
default: |
||||
return false, "obtained value type has no length" |
||||
} |
||||
return value.Len() == n, "" |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// ErrorMatches checker.
|
||||
|
||||
type errorMatchesChecker struct { |
||||
*CheckerInfo |
||||
} |
||||
|
||||
// The ErrorMatches checker verifies that the error value
|
||||
// is non nil and matches the regular expression provided.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// c.Assert(err, ErrorMatches, "perm.*denied")
|
||||
//
|
||||
var ErrorMatches Checker = errorMatchesChecker{ |
||||
&CheckerInfo{Name: "ErrorMatches", Params: []string{"value", "regex"}}, |
||||
} |
||||
|
||||
func (checker errorMatchesChecker) Check(params []interface{}, names []string) (result bool, errStr string) { |
||||
if params[0] == nil { |
||||
return false, "Error value is nil" |
||||
} |
||||
err, ok := params[0].(error) |
||||
if !ok { |
||||
return false, "Value is not an error" |
||||
} |
||||
params[0] = err.Error() |
||||
names[0] = "error" |
||||
return matches(params[0], params[1]) |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Matches checker.
|
||||
|
||||
type matchesChecker struct { |
||||
*CheckerInfo |
||||
} |
||||
|
||||
// The Matches checker verifies that the string provided as the obtained
|
||||
// value (or the string resulting from obtained.String()) matches the
|
||||
// regular expression provided.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// c.Assert(err, Matches, "perm.*denied")
|
||||
//
|
||||
var Matches Checker = &matchesChecker{ |
||||
&CheckerInfo{Name: "Matches", Params: []string{"value", "regex"}}, |
||||
} |
||||
|
||||
func (checker *matchesChecker) Check(params []interface{}, names []string) (result bool, error string) { |
||||
return matches(params[0], params[1]) |
||||
} |
||||
|
||||
func matches(value, regex interface{}) (result bool, error string) { |
||||
reStr, ok := regex.(string) |
||||
if !ok { |
||||
return false, "Regex must be a string" |
||||
} |
||||
valueStr, valueIsStr := value.(string) |
||||
if !valueIsStr { |
||||
if valueWithStr, valueHasStr := value.(fmt.Stringer); valueHasStr { |
||||
valueStr, valueIsStr = valueWithStr.String(), true |
||||
} |
||||
} |
||||
if valueIsStr { |
||||
matches, err := regexp.MatchString("^"+reStr+"$", valueStr) |
||||
if err != nil { |
||||
return false, "Can't compile regex: " + err.Error() |
||||
} |
||||
return matches, "" |
||||
} |
||||
return false, "Obtained value is not a string and has no .String()" |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Panics checker.
|
||||
|
||||
type panicsChecker struct { |
||||
*CheckerInfo |
||||
} |
||||
|
||||
// The Panics checker verifies that calling the provided zero-argument
|
||||
// function will cause a panic which is deep-equal to the provided value.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// c.Assert(func() { f(1, 2) }, Panics, &SomeErrorType{"BOOM"}).
|
||||
//
|
||||
//
|
||||
var Panics Checker = &panicsChecker{ |
||||
&CheckerInfo{Name: "Panics", Params: []string{"function", "expected"}}, |
||||
} |
||||
|
||||
func (checker *panicsChecker) Check(params []interface{}, names []string) (result bool, error string) { |
||||
f := reflect.ValueOf(params[0]) |
||||
if f.Kind() != reflect.Func || f.Type().NumIn() != 0 { |
||||
return false, "Function must take zero arguments" |
||||
} |
||||
defer func() { |
||||
// If the function has not panicked, then don't do the check.
|
||||
if error != "" { |
||||
return |
||||
} |
||||
params[0] = recover() |
||||
names[0] = "panic" |
||||
result = reflect.DeepEqual(params[0], params[1]) |
||||
}() |
||||
f.Call(nil) |
||||
return false, "Function has not panicked" |
||||
} |
||||
|
||||
type panicMatchesChecker struct { |
||||
*CheckerInfo |
||||
} |
||||
|
||||
// The PanicMatches checker verifies that calling the provided zero-argument
|
||||
// function will cause a panic with an error value matching
|
||||
// the regular expression provided.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// c.Assert(func() { f(1, 2) }, PanicMatches, `open.*: no such file or directory`).
|
||||
//
|
||||
//
|
||||
var PanicMatches Checker = &panicMatchesChecker{ |
||||
&CheckerInfo{Name: "PanicMatches", Params: []string{"function", "expected"}}, |
||||
} |
||||
|
||||
func (checker *panicMatchesChecker) Check(params []interface{}, names []string) (result bool, errmsg string) { |
||||
f := reflect.ValueOf(params[0]) |
||||
if f.Kind() != reflect.Func || f.Type().NumIn() != 0 { |
||||
return false, "Function must take zero arguments" |
||||
} |
||||
defer func() { |
||||
// If the function has not panicked, then don't do the check.
|
||||
if errmsg != "" { |
||||
return |
||||
} |
||||
obtained := recover() |
||||
names[0] = "panic" |
||||
if e, ok := obtained.(error); ok { |
||||
params[0] = e.Error() |
||||
} else if _, ok := obtained.(string); ok { |
||||
params[0] = obtained |
||||
} else { |
||||
errmsg = "Panic value is not a string or an error" |
||||
return |
||||
} |
||||
result, errmsg = matches(params[0], params[1]) |
||||
}() |
||||
f.Call(nil) |
||||
return false, "Function has not panicked" |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// FitsTypeOf checker.
|
||||
|
||||
type fitsTypeChecker struct { |
||||
*CheckerInfo |
||||
} |
||||
|
||||
// The FitsTypeOf checker verifies that the obtained value is
|
||||
// assignable to a variable with the same type as the provided
|
||||
// sample value.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// c.Assert(value, FitsTypeOf, int64(0))
|
||||
// c.Assert(value, FitsTypeOf, os.Error(nil))
|
||||
//
|
||||
var FitsTypeOf Checker = &fitsTypeChecker{ |
||||
&CheckerInfo{Name: "FitsTypeOf", Params: []string{"obtained", "sample"}}, |
||||
} |
||||
|
||||
func (checker *fitsTypeChecker) Check(params []interface{}, names []string) (result bool, error string) { |
||||
obtained := reflect.ValueOf(params[0]) |
||||
sample := reflect.ValueOf(params[1]) |
||||
if !obtained.IsValid() { |
||||
return false, "" |
||||
} |
||||
if !sample.IsValid() { |
||||
return false, "Invalid sample value" |
||||
} |
||||
return obtained.Type().AssignableTo(sample.Type()), "" |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Implements checker.
|
||||
|
||||
type implementsChecker struct { |
||||
*CheckerInfo |
||||
} |
||||
|
||||
// The Implements checker verifies that the obtained value
|
||||
// implements the interface specified via a pointer to an interface
|
||||
// variable.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// var e os.Error
|
||||
// c.Assert(err, Implements, &e)
|
||||
//
|
||||
var Implements Checker = &implementsChecker{ |
||||
&CheckerInfo{Name: "Implements", Params: []string{"obtained", "ifaceptr"}}, |
||||
} |
||||
|
||||
func (checker *implementsChecker) Check(params []interface{}, names []string) (result bool, error string) { |
||||
obtained := reflect.ValueOf(params[0]) |
||||
ifaceptr := reflect.ValueOf(params[1]) |
||||
if !obtained.IsValid() { |
||||
return false, "" |
||||
} |
||||
if !ifaceptr.IsValid() || ifaceptr.Kind() != reflect.Ptr || ifaceptr.Elem().Kind() != reflect.Interface { |
||||
return false, "ifaceptr should be a pointer to an interface variable" |
||||
} |
||||
return obtained.Type().Implements(ifaceptr.Elem().Type()), "" |
||||
} |
@ -0,0 +1,272 @@ |
||||
package check_test |
||||
|
||||
import ( |
||||
"errors" |
||||
"gopkg.in/check.v1" |
||||
"reflect" |
||||
"runtime" |
||||
) |
||||
|
||||
type CheckersS struct{} |
||||
|
||||
var _ = check.Suite(&CheckersS{}) |
||||
|
||||
func testInfo(c *check.C, checker check.Checker, name string, paramNames []string) { |
||||
info := checker.Info() |
||||
if info.Name != name { |
||||
c.Fatalf("Got name %s, expected %s", info.Name, name) |
||||
} |
||||
if !reflect.DeepEqual(info.Params, paramNames) { |
||||
c.Fatalf("Got param names %#v, expected %#v", info.Params, paramNames) |
||||
} |
||||
} |
||||
|
||||
func testCheck(c *check.C, checker check.Checker, result bool, error string, params ...interface{}) ([]interface{}, []string) { |
||||
info := checker.Info() |
||||
if len(params) != len(info.Params) { |
||||
c.Fatalf("unexpected param count in test; expected %d got %d", len(info.Params), len(params)) |
||||
} |
||||
names := append([]string{}, info.Params...) |
||||
result_, error_ := checker.Check(params, names) |
||||
if result_ != result || error_ != error { |
||||
c.Fatalf("%s.Check(%#v) returned (%#v, %#v) rather than (%#v, %#v)", |
||||
info.Name, params, result_, error_, result, error) |
||||
} |
||||
return params, names |
||||
} |
||||
|
||||
func (s *CheckersS) TestComment(c *check.C) { |
||||
bug := check.Commentf("a %d bc", 42) |
||||
comment := bug.CheckCommentString() |
||||
if comment != "a 42 bc" { |
||||
c.Fatalf("Commentf returned %#v", comment) |
||||
} |
||||
} |
||||
|
||||
func (s *CheckersS) TestIsNil(c *check.C) { |
||||
testInfo(c, check.IsNil, "IsNil", []string{"value"}) |
||||
|
||||
testCheck(c, check.IsNil, true, "", nil) |
||||
testCheck(c, check.IsNil, false, "", "a") |
||||
|
||||
testCheck(c, check.IsNil, true, "", (chan int)(nil)) |
||||
testCheck(c, check.IsNil, false, "", make(chan int)) |
||||
testCheck(c, check.IsNil, true, "", (error)(nil)) |
||||
testCheck(c, check.IsNil, false, "", errors.New("")) |
||||
testCheck(c, check.IsNil, true, "", ([]int)(nil)) |
||||
testCheck(c, check.IsNil, false, "", make([]int, 1)) |
||||
testCheck(c, check.IsNil, false, "", int(0)) |
||||
} |
||||
|
||||
func (s *CheckersS) TestNotNil(c *check.C) { |
||||
testInfo(c, check.NotNil, "NotNil", []string{"value"}) |
||||
|
||||
testCheck(c, check.NotNil, false, "", nil) |
||||
testCheck(c, check.NotNil, true, "", "a") |
||||
|
||||
testCheck(c, check.NotNil, false, "", (chan int)(nil)) |
||||
testCheck(c, check.NotNil, true, "", make(chan int)) |
||||
testCheck(c, check.NotNil, false, "", (error)(nil)) |
||||
testCheck(c, check.NotNil, true, "", errors.New("")) |
||||
testCheck(c, check.NotNil, false, "", ([]int)(nil)) |
||||
testCheck(c, check.NotNil, true, "", make([]int, 1)) |
||||
} |
||||
|
||||
func (s *CheckersS) TestNot(c *check.C) { |
||||
testInfo(c, check.Not(check.IsNil), "Not(IsNil)", []string{"value"}) |
||||
|
||||
testCheck(c, check.Not(check.IsNil), false, "", nil) |
||||
testCheck(c, check.Not(check.IsNil), true, "", "a") |
||||
} |
||||
|
||||
type simpleStruct struct { |
||||
i int |
||||
} |
||||
|
||||
func (s *CheckersS) TestEquals(c *check.C) { |
||||
testInfo(c, check.Equals, "Equals", []string{"obtained", "expected"}) |
||||
|
||||
// The simplest.
|
||||
testCheck(c, check.Equals, true, "", 42, 42) |
||||
testCheck(c, check.Equals, false, "", 42, 43) |
||||
|
||||
// Different native types.
|
||||
testCheck(c, check.Equals, false, "", int32(42), int64(42)) |
||||
|
||||
// With nil.
|
||||
testCheck(c, check.Equals, false, "", 42, nil) |
||||
|
||||
// Slices
|
||||
testCheck(c, check.Equals, false, "runtime error: comparing uncomparable type []uint8", []byte{1, 2}, []byte{1, 2}) |
||||
|
||||
// Struct values
|
||||
testCheck(c, check.Equals, true, "", simpleStruct{1}, simpleStruct{1}) |
||||
testCheck(c, check.Equals, false, "", simpleStruct{1}, simpleStruct{2}) |
||||
|
||||
// Struct pointers
|
||||
testCheck(c, check.Equals, false, "", &simpleStruct{1}, &simpleStruct{1}) |
||||
testCheck(c, check.Equals, false, "", &simpleStruct{1}, &simpleStruct{2}) |
||||
} |
||||
|
||||
func (s *CheckersS) TestDeepEquals(c *check.C) { |
||||
testInfo(c, check.DeepEquals, "DeepEquals", []string{"obtained", "expected"}) |
||||
|
||||
// The simplest.
|
||||
testCheck(c, check.DeepEquals, true, "", 42, 42) |
||||
testCheck(c, check.DeepEquals, false, "", 42, 43) |
||||
|
||||
// Different native types.
|
||||
testCheck(c, check.DeepEquals, false, "", int32(42), int64(42)) |
||||
|
||||
// With nil.
|
||||
testCheck(c, check.DeepEquals, false, "", 42, nil) |
||||
|
||||
// Slices
|
||||
testCheck(c, check.DeepEquals, true, "", []byte{1, 2}, []byte{1, 2}) |
||||
testCheck(c, check.DeepEquals, false, "", []byte{1, 2}, []byte{1, 3}) |
||||
|
||||
// Struct values
|
||||
testCheck(c, check.DeepEquals, true, "", simpleStruct{1}, simpleStruct{1}) |
||||
testCheck(c, check.DeepEquals, false, "", simpleStruct{1}, simpleStruct{2}) |
||||
|
||||
// Struct pointers
|
||||
testCheck(c, check.DeepEquals, true, "", &simpleStruct{1}, &simpleStruct{1}) |
||||
testCheck(c, check.DeepEquals, false, "", &simpleStruct{1}, &simpleStruct{2}) |
||||
} |
||||
|
||||
func (s *CheckersS) TestHasLen(c *check.C) { |
||||
testInfo(c, check.HasLen, "HasLen", []string{"obtained", "n"}) |
||||
|
||||
testCheck(c, check.HasLen, true, "", "abcd", 4) |
||||
testCheck(c, check.HasLen, true, "", []int{1, 2}, 2) |
||||
testCheck(c, check.HasLen, false, "", []int{1, 2}, 3) |
||||
|
||||
testCheck(c, check.HasLen, false, "n must be an int", []int{1, 2}, "2") |
||||
testCheck(c, check.HasLen, false, "obtained value type has no length", nil, 2) |
||||
} |
||||
|
||||
func (s *CheckersS) TestErrorMatches(c *check.C) { |
||||
testInfo(c, check.ErrorMatches, "ErrorMatches", []string{"value", "regex"}) |
||||
|
||||
testCheck(c, check.ErrorMatches, false, "Error value is nil", nil, "some error") |
||||
testCheck(c, check.ErrorMatches, false, "Value is not an error", 1, "some error") |
||||
testCheck(c, check.ErrorMatches, true, "", errors.New("some error"), "some error") |
||||
testCheck(c, check.ErrorMatches, true, "", errors.New("some error"), "so.*or") |
||||
|
||||
// Verify params mutation
|
||||
params, names := testCheck(c, check.ErrorMatches, false, "", errors.New("some error"), "other error") |
||||
c.Assert(params[0], check.Equals, "some error") |
||||
c.Assert(names[0], check.Equals, "error") |
||||
} |
||||
|
||||
func (s *CheckersS) TestMatches(c *check.C) { |
||||
testInfo(c, check.Matches, "Matches", []string{"value", "regex"}) |
||||
|
||||
// Simple matching
|
||||
testCheck(c, check.Matches, true, "", "abc", "abc") |
||||
testCheck(c, check.Matches, true, "", "abc", "a.c") |
||||
|
||||
// Must match fully
|
||||
testCheck(c, check.Matches, false, "", "abc", "ab") |
||||
testCheck(c, check.Matches, false, "", "abc", "bc") |
||||
|
||||
// String()-enabled values accepted
|
||||
testCheck(c, check.Matches, true, "", reflect.ValueOf("abc"), "a.c") |
||||
testCheck(c, check.Matches, false, "", reflect.ValueOf("abc"), "a.d") |
||||
|
||||
// Some error conditions.
|
||||
testCheck(c, check.Matches, false, "Obtained value is not a string and has no .String()", 1, "a.c") |
||||
testCheck(c, check.Matches, false, "Can't compile regex: error parsing regexp: missing closing ]: `[c$`", "abc", "a[c") |
||||
} |
||||
|
||||
func (s *CheckersS) TestPanics(c *check.C) { |
||||
testInfo(c, check.Panics, "Panics", []string{"function", "expected"}) |
||||
|
||||
// Some errors.
|
||||
testCheck(c, check.Panics, false, "Function has not panicked", func() bool { return false }, "BOOM") |
||||
testCheck(c, check.Panics, false, "Function must take zero arguments", 1, "BOOM") |
||||
|
||||
// Plain strings.
|
||||
testCheck(c, check.Panics, true, "", func() { panic("BOOM") }, "BOOM") |
||||
testCheck(c, check.Panics, false, "", func() { panic("KABOOM") }, "BOOM") |
||||
testCheck(c, check.Panics, true, "", func() bool { panic("BOOM") }, "BOOM") |
||||
|
||||
// Error values.
|
||||
testCheck(c, check.Panics, true, "", func() { panic(errors.New("BOOM")) }, errors.New("BOOM")) |
||||
testCheck(c, check.Panics, false, "", func() { panic(errors.New("KABOOM")) }, errors.New("BOOM")) |
||||
|
||||
type deep struct{ i int } |
||||
// Deep value
|
||||
testCheck(c, check.Panics, true, "", func() { panic(&deep{99}) }, &deep{99}) |
||||
|
||||
// Verify params/names mutation
|
||||
params, names := testCheck(c, check.Panics, false, "", func() { panic(errors.New("KABOOM")) }, errors.New("BOOM")) |
||||
c.Assert(params[0], check.ErrorMatches, "KABOOM") |
||||
c.Assert(names[0], check.Equals, "panic") |
||||
|
||||
// Verify a nil panic
|
||||
testCheck(c, check.Panics, true, "", func() { panic(nil) }, nil) |
||||
testCheck(c, check.Panics, false, "", func() { panic(nil) }, "NOPE") |
||||
} |
||||
|
||||
func (s *CheckersS) TestPanicMatches(c *check.C) { |
||||
testInfo(c, check.PanicMatches, "PanicMatches", []string{"function", "expected"}) |
||||
|
||||
// Error matching.
|
||||
testCheck(c, check.PanicMatches, true, "", func() { panic(errors.New("BOOM")) }, "BO.M") |
||||
testCheck(c, check.PanicMatches, false, "", func() { panic(errors.New("KABOOM")) }, "BO.M") |
||||
|
||||
// Some errors.
|
||||
testCheck(c, check.PanicMatches, false, "Function has not panicked", func() bool { return false }, "BOOM") |
||||
testCheck(c, check.PanicMatches, false, "Function must take zero arguments", 1, "BOOM") |
||||
|
||||
// Plain strings.
|
||||
testCheck(c, check.PanicMatches, true, "", func() { panic("BOOM") }, "BO.M") |
||||
testCheck(c, check.PanicMatches, false, "", func() { panic("KABOOM") }, "BOOM") |
||||
testCheck(c, check.PanicMatches, true, "", func() bool { panic("BOOM") }, "BO.M") |
||||
|
||||
// Verify params/names mutation
|
||||
params, names := testCheck(c, check.PanicMatches, false, "", func() { panic(errors.New("KABOOM")) }, "BOOM") |
||||
c.Assert(params[0], check.Equals, "KABOOM") |
||||
c.Assert(names[0], check.Equals, "panic") |
||||
|
||||
// Verify a nil panic
|
||||
testCheck(c, check.PanicMatches, false, "Panic value is not a string or an error", func() { panic(nil) }, "") |
||||
} |
||||
|
||||
func (s *CheckersS) TestFitsTypeOf(c *check.C) { |
||||
testInfo(c, check.FitsTypeOf, "FitsTypeOf", []string{"obtained", "sample"}) |
||||
|
||||
// Basic types
|
||||
testCheck(c, check.FitsTypeOf, true, "", 1, 0) |
||||
testCheck(c, check.FitsTypeOf, false, "", 1, int64(0)) |
||||
|
||||
// Aliases
|
||||
testCheck(c, check.FitsTypeOf, false, "", 1, errors.New("")) |
||||
testCheck(c, check.FitsTypeOf, false, "", "error", errors.New("")) |
||||
testCheck(c, check.FitsTypeOf, true, "", errors.New("error"), errors.New("")) |
||||
|
||||
// Structures
|
||||
testCheck(c, check.FitsTypeOf, false, "", 1, simpleStruct{}) |
||||
testCheck(c, check.FitsTypeOf, false, "", simpleStruct{42}, &simpleStruct{}) |
||||
testCheck(c, check.FitsTypeOf, true, "", simpleStruct{42}, simpleStruct{}) |
||||
testCheck(c, check.FitsTypeOf, true, "", &simpleStruct{42}, &simpleStruct{}) |
||||
|
||||
// Some bad values
|
||||
testCheck(c, check.FitsTypeOf, false, "Invalid sample value", 1, interface{}(nil)) |
||||
testCheck(c, check.FitsTypeOf, false, "", interface{}(nil), 0) |
||||
} |
||||
|
||||
func (s *CheckersS) TestImplements(c *check.C) { |
||||
testInfo(c, check.Implements, "Implements", []string{"obtained", "ifaceptr"}) |
||||
|
||||
var e error |
||||
var re runtime.Error |
||||
testCheck(c, check.Implements, true, "", errors.New(""), &e) |
||||
testCheck(c, check.Implements, false, "", errors.New(""), &re) |
||||
|
||||
// Some bad values
|
||||
testCheck(c, check.Implements, false, "ifaceptr should be a pointer to an interface variable", 0, errors.New("")) |
||||
testCheck(c, check.Implements, false, "ifaceptr should be a pointer to an interface variable", 0, interface{}(nil)) |
||||
testCheck(c, check.Implements, false, "", interface{}(nil), &e) |
||||
} |
@ -0,0 +1,9 @@ |
||||
package check |
||||
|
||||
func PrintLine(filename string, line int) (string, error) { |
||||
return printLine(filename, line) |
||||
} |
||||
|
||||
func Indent(s, with string) string { |
||||
return indent(s, with) |
||||
} |
@ -0,0 +1,484 @@ |
||||
// Tests for the behavior of the test fixture system.
|
||||
|
||||
package check_test |
||||
|
||||
import ( |
||||
. "gopkg.in/check.v1" |
||||
) |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Fixture test suite.
|
||||
|
||||
type FixtureS struct{} |
||||
|
||||
var fixtureS = Suite(&FixtureS{}) |
||||
|
||||
func (s *FixtureS) TestCountSuite(c *C) { |
||||
suitesRun += 1 |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Basic fixture ordering verification.
|
||||
|
||||
func (s *FixtureS) TestOrder(c *C) { |
||||
helper := FixtureHelper{} |
||||
Run(&helper, nil) |
||||
c.Check(helper.calls[0], Equals, "SetUpSuite") |
||||
c.Check(helper.calls[1], Equals, "SetUpTest") |
||||
c.Check(helper.calls[2], Equals, "Test1") |
||||
c.Check(helper.calls[3], Equals, "TearDownTest") |
||||
c.Check(helper.calls[4], Equals, "SetUpTest") |
||||
c.Check(helper.calls[5], Equals, "Test2") |
||||
c.Check(helper.calls[6], Equals, "TearDownTest") |
||||
c.Check(helper.calls[7], Equals, "TearDownSuite") |
||||
c.Check(len(helper.calls), Equals, 8) |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Check the behavior when panics occur within tests and fixtures.
|
||||
|
||||
func (s *FixtureS) TestPanicOnTest(c *C) { |
||||
helper := FixtureHelper{panicOn: "Test1"} |
||||
output := String{} |
||||
Run(&helper, &RunConf{Output: &output}) |
||||
c.Check(helper.calls[0], Equals, "SetUpSuite") |
||||
c.Check(helper.calls[1], Equals, "SetUpTest") |
||||
c.Check(helper.calls[2], Equals, "Test1") |
||||
c.Check(helper.calls[3], Equals, "TearDownTest") |
||||
c.Check(helper.calls[4], Equals, "SetUpTest") |
||||
c.Check(helper.calls[5], Equals, "Test2") |
||||
c.Check(helper.calls[6], Equals, "TearDownTest") |
||||
c.Check(helper.calls[7], Equals, "TearDownSuite") |
||||
c.Check(len(helper.calls), Equals, 8) |
||||
|
||||
expected := "^\n-+\n" + |
||||
"PANIC: check_test\\.go:[0-9]+: FixtureHelper.Test1\n\n" + |
||||
"\\.\\.\\. Panic: Test1 \\(PC=[xA-F0-9]+\\)\n\n" + |
||||
".+:[0-9]+\n" + |
||||
" in (go)?panic\n" + |
||||
".*check_test.go:[0-9]+\n" + |
||||
" in FixtureHelper.trace\n" + |
||||
".*check_test.go:[0-9]+\n" + |
||||
" in FixtureHelper.Test1\n" + |
||||
"(.|\n)*$" |
||||
|
||||
c.Check(output.value, Matches, expected) |
||||
} |
||||
|
||||
func (s *FixtureS) TestPanicOnSetUpTest(c *C) { |
||||
helper := FixtureHelper{panicOn: "SetUpTest"} |
||||
output := String{} |
||||
Run(&helper, &RunConf{Output: &output}) |
||||
c.Check(helper.calls[0], Equals, "SetUpSuite") |
||||
c.Check(helper.calls[1], Equals, "SetUpTest") |
||||
c.Check(helper.calls[2], Equals, "TearDownTest") |
||||
c.Check(helper.calls[3], Equals, "TearDownSuite") |
||||
c.Check(len(helper.calls), Equals, 4) |
||||
|
||||
expected := "^\n-+\n" + |
||||
"PANIC: check_test\\.go:[0-9]+: " + |
||||
"FixtureHelper\\.SetUpTest\n\n" + |
||||
"\\.\\.\\. Panic: SetUpTest \\(PC=[xA-F0-9]+\\)\n\n" + |
||||
".+:[0-9]+\n" + |
||||
" in (go)?panic\n" + |
||||
".*check_test.go:[0-9]+\n" + |
||||
" in FixtureHelper.trace\n" + |
||||
".*check_test.go:[0-9]+\n" + |
||||
" in FixtureHelper.SetUpTest\n" + |
||||
"(.|\n)*" + |
||||
"\n-+\n" + |
||||
"PANIC: check_test\\.go:[0-9]+: " + |
||||
"FixtureHelper\\.Test1\n\n" + |
||||
"\\.\\.\\. Panic: Fixture has panicked " + |
||||
"\\(see related PANIC\\)\n$" |
||||
|
||||
c.Check(output.value, Matches, expected) |
||||
} |
||||
|
||||
func (s *FixtureS) TestPanicOnTearDownTest(c *C) { |
||||
helper := FixtureHelper{panicOn: "TearDownTest"} |
||||
output := String{} |
||||
Run(&helper, &RunConf{Output: &output}) |
||||
c.Check(helper.calls[0], Equals, "SetUpSuite") |
||||
c.Check(helper.calls[1], Equals, "SetUpTest") |
||||
c.Check(helper.calls[2], Equals, "Test1") |
||||
c.Check(helper.calls[3], Equals, "TearDownTest") |
||||
c.Check(helper.calls[4], Equals, "TearDownSuite") |
||||
c.Check(len(helper.calls), Equals, 5) |
||||
|
||||
expected := "^\n-+\n" + |
||||
"PANIC: check_test\\.go:[0-9]+: " + |
||||
"FixtureHelper.TearDownTest\n\n" + |
||||
"\\.\\.\\. Panic: TearDownTest \\(PC=[xA-F0-9]+\\)\n\n" + |
||||
".+:[0-9]+\n" + |
||||
" in (go)?panic\n" + |
||||
".*check_test.go:[0-9]+\n" + |
||||
" in FixtureHelper.trace\n" + |
||||
".*check_test.go:[0-9]+\n" + |
||||
" in FixtureHelper.TearDownTest\n" + |
||||
"(.|\n)*" + |
||||
"\n-+\n" + |
||||
"PANIC: check_test\\.go:[0-9]+: " + |
||||
"FixtureHelper\\.Test1\n\n" + |
||||
"\\.\\.\\. Panic: Fixture has panicked " + |
||||
"\\(see related PANIC\\)\n$" |
||||
|
||||
c.Check(output.value, Matches, expected) |
||||
} |
||||
|
||||
func (s *FixtureS) TestPanicOnSetUpSuite(c *C) { |
||||
helper := FixtureHelper{panicOn: "SetUpSuite"} |
||||
output := String{} |
||||
Run(&helper, &RunConf{Output: &output}) |
||||
c.Check(helper.calls[0], Equals, "SetUpSuite") |
||||
c.Check(helper.calls[1], Equals, "TearDownSuite") |
||||
c.Check(len(helper.calls), Equals, 2) |
||||
|
||||
expected := "^\n-+\n" + |
||||
"PANIC: check_test\\.go:[0-9]+: " + |
||||
"FixtureHelper.SetUpSuite\n\n" + |
||||
"\\.\\.\\. Panic: SetUpSuite \\(PC=[xA-F0-9]+\\)\n\n" + |
||||
".+:[0-9]+\n" + |
||||
" in (go)?panic\n" + |
||||
".*check_test.go:[0-9]+\n" + |
||||
" in FixtureHelper.trace\n" + |
||||
".*check_test.go:[0-9]+\n" + |
||||
" in FixtureHelper.SetUpSuite\n" + |
||||
"(.|\n)*$" |
||||
|
||||
c.Check(output.value, Matches, expected) |
||||
} |
||||
|
||||
func (s *FixtureS) TestPanicOnTearDownSuite(c *C) { |
||||
helper := FixtureHelper{panicOn: "TearDownSuite"} |
||||
output := String{} |
||||
Run(&helper, &RunConf{Output: &output}) |
||||
c.Check(helper.calls[0], Equals, "SetUpSuite") |
||||
c.Check(helper.calls[1], Equals, "SetUpTest") |
||||
c.Check(helper.calls[2], Equals, "Test1") |
||||
c.Check(helper.calls[3], Equals, "TearDownTest") |
||||
c.Check(helper.calls[4], Equals, "SetUpTest") |
||||
c.Check(helper.calls[5], Equals, "Test2") |
||||
c.Check(helper.calls[6], Equals, "TearDownTest") |
||||
c.Check(helper.calls[7], Equals, "TearDownSuite") |
||||
c.Check(len(helper.calls), Equals, 8) |
||||
|
||||
expected := "^\n-+\n" + |
||||
"PANIC: check_test\\.go:[0-9]+: " + |
||||
"FixtureHelper.TearDownSuite\n\n" + |
||||
"\\.\\.\\. Panic: TearDownSuite \\(PC=[xA-F0-9]+\\)\n\n" + |
||||
".+:[0-9]+\n" + |
||||
" in (go)?panic\n" + |
||||
".*check_test.go:[0-9]+\n" + |
||||
" in FixtureHelper.trace\n" + |
||||
".*check_test.go:[0-9]+\n" + |
||||
" in FixtureHelper.TearDownSuite\n" + |
||||
"(.|\n)*$" |
||||
|
||||
c.Check(output.value, Matches, expected) |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// A wrong argument on a test or fixture will produce a nice error.
|
||||
|
||||
func (s *FixtureS) TestPanicOnWrongTestArg(c *C) { |
||||
helper := WrongTestArgHelper{} |
||||
output := String{} |
||||
Run(&helper, &RunConf{Output: &output}) |
||||
c.Check(helper.calls[0], Equals, "SetUpSuite") |
||||
c.Check(helper.calls[1], Equals, "SetUpTest") |
||||
c.Check(helper.calls[2], Equals, "TearDownTest") |
||||
c.Check(helper.calls[3], Equals, "SetUpTest") |
||||
c.Check(helper.calls[4], Equals, "Test2") |
||||
c.Check(helper.calls[5], Equals, "TearDownTest") |
||||
c.Check(helper.calls[6], Equals, "TearDownSuite") |
||||
c.Check(len(helper.calls), Equals, 7) |
||||
|
||||
expected := "^\n-+\n" + |
||||
"PANIC: fixture_test\\.go:[0-9]+: " + |
||||
"WrongTestArgHelper\\.Test1\n\n" + |
||||
"\\.\\.\\. Panic: WrongTestArgHelper\\.Test1 argument " + |
||||
"should be \\*check\\.C\n" |
||||
|
||||
c.Check(output.value, Matches, expected) |
||||
} |
||||
|
||||
func (s *FixtureS) TestPanicOnWrongSetUpTestArg(c *C) { |
||||
helper := WrongSetUpTestArgHelper{} |
||||
output := String{} |
||||
Run(&helper, &RunConf{Output: &output}) |
||||
c.Check(len(helper.calls), Equals, 0) |
||||
|
||||
expected := |
||||
"^\n-+\n" + |
||||
"PANIC: fixture_test\\.go:[0-9]+: " + |
||||
"WrongSetUpTestArgHelper\\.SetUpTest\n\n" + |
||||
"\\.\\.\\. Panic: WrongSetUpTestArgHelper\\.SetUpTest argument " + |
||||
"should be \\*check\\.C\n" |
||||
|
||||
c.Check(output.value, Matches, expected) |
||||
} |
||||
|
||||
func (s *FixtureS) TestPanicOnWrongSetUpSuiteArg(c *C) { |
||||
helper := WrongSetUpSuiteArgHelper{} |
||||
output := String{} |
||||
Run(&helper, &RunConf{Output: &output}) |
||||
c.Check(len(helper.calls), Equals, 0) |
||||
|
||||
expected := |
||||
"^\n-+\n" + |
||||
"PANIC: fixture_test\\.go:[0-9]+: " + |
||||
"WrongSetUpSuiteArgHelper\\.SetUpSuite\n\n" + |
||||
"\\.\\.\\. Panic: WrongSetUpSuiteArgHelper\\.SetUpSuite argument " + |
||||
"should be \\*check\\.C\n" |
||||
|
||||
c.Check(output.value, Matches, expected) |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Nice errors also when tests or fixture have wrong arg count.
|
||||
|
||||
func (s *FixtureS) TestPanicOnWrongTestArgCount(c *C) { |
||||
helper := WrongTestArgCountHelper{} |
||||
output := String{} |
||||
Run(&helper, &RunConf{Output: &output}) |
||||
c.Check(helper.calls[0], Equals, "SetUpSuite") |
||||
c.Check(helper.calls[1], Equals, "SetUpTest") |
||||
c.Check(helper.calls[2], Equals, "TearDownTest") |
||||
c.Check(helper.calls[3], Equals, "SetUpTest") |
||||
c.Check(helper.calls[4], Equals, "Test2") |
||||
c.Check(helper.calls[5], Equals, "TearDownTest") |
||||
c.Check(helper.calls[6], Equals, "TearDownSuite") |
||||
c.Check(len(helper.calls), Equals, 7) |
||||
|
||||
expected := "^\n-+\n" + |
||||
"PANIC: fixture_test\\.go:[0-9]+: " + |
||||
"WrongTestArgCountHelper\\.Test1\n\n" + |
||||
"\\.\\.\\. Panic: WrongTestArgCountHelper\\.Test1 argument " + |
||||
"should be \\*check\\.C\n" |
||||
|
||||
c.Check(output.value, Matches, expected) |
||||
} |
||||
|
||||
func (s *FixtureS) TestPanicOnWrongSetUpTestArgCount(c *C) { |
||||
helper := WrongSetUpTestArgCountHelper{} |
||||
output := String{} |
||||
Run(&helper, &RunConf{Output: &output}) |
||||
c.Check(len(helper.calls), Equals, 0) |
||||
|
||||
expected := |
||||
"^\n-+\n" + |
||||
"PANIC: fixture_test\\.go:[0-9]+: " + |
||||
"WrongSetUpTestArgCountHelper\\.SetUpTest\n\n" + |
||||
"\\.\\.\\. Panic: WrongSetUpTestArgCountHelper\\.SetUpTest argument " + |
||||
"should be \\*check\\.C\n" |
||||
|
||||
c.Check(output.value, Matches, expected) |
||||
} |
||||
|
||||
func (s *FixtureS) TestPanicOnWrongSetUpSuiteArgCount(c *C) { |
||||
helper := WrongSetUpSuiteArgCountHelper{} |
||||
output := String{} |
||||
Run(&helper, &RunConf{Output: &output}) |
||||
c.Check(len(helper.calls), Equals, 0) |
||||
|
||||
expected := |
||||
"^\n-+\n" + |
||||
"PANIC: fixture_test\\.go:[0-9]+: " + |
||||
"WrongSetUpSuiteArgCountHelper\\.SetUpSuite\n\n" + |
||||
"\\.\\.\\. Panic: WrongSetUpSuiteArgCountHelper" + |
||||
"\\.SetUpSuite argument should be \\*check\\.C\n" |
||||
|
||||
c.Check(output.value, Matches, expected) |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Helper test suites with wrong function arguments.
|
||||
|
||||
type WrongTestArgHelper struct { |
||||
FixtureHelper |
||||
} |
||||
|
||||
func (s *WrongTestArgHelper) Test1(t int) { |
||||
} |
||||
|
||||
type WrongSetUpTestArgHelper struct { |
||||
FixtureHelper |
||||
} |
||||
|
||||
func (s *WrongSetUpTestArgHelper) SetUpTest(t int) { |
||||
} |
||||
|
||||
type WrongSetUpSuiteArgHelper struct { |
||||
FixtureHelper |
||||
} |
||||
|
||||
func (s *WrongSetUpSuiteArgHelper) SetUpSuite(t int) { |
||||
} |
||||
|
||||
type WrongTestArgCountHelper struct { |
||||
FixtureHelper |
||||
} |
||||
|
||||
func (s *WrongTestArgCountHelper) Test1(c *C, i int) { |
||||
} |
||||
|
||||
type WrongSetUpTestArgCountHelper struct { |
||||
FixtureHelper |
||||
} |
||||
|
||||
func (s *WrongSetUpTestArgCountHelper) SetUpTest(c *C, i int) { |
||||
} |
||||
|
||||
type WrongSetUpSuiteArgCountHelper struct { |
||||
FixtureHelper |
||||
} |
||||
|
||||
func (s *WrongSetUpSuiteArgCountHelper) SetUpSuite(c *C, i int) { |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Ensure fixture doesn't run without tests.
|
||||
|
||||
type NoTestsHelper struct { |
||||
hasRun bool |
||||
} |
||||
|
||||
func (s *NoTestsHelper) SetUpSuite(c *C) { |
||||
s.hasRun = true |
||||
} |
||||
|
||||
func (s *NoTestsHelper) TearDownSuite(c *C) { |
||||
s.hasRun = true |
||||
} |
||||
|
||||
func (s *FixtureS) TestFixtureDoesntRunWithoutTests(c *C) { |
||||
helper := NoTestsHelper{} |
||||
output := String{} |
||||
Run(&helper, &RunConf{Output: &output}) |
||||
c.Check(helper.hasRun, Equals, false) |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Verify that checks and assertions work correctly inside the fixture.
|
||||
|
||||
type FixtureCheckHelper struct { |
||||
fail string |
||||
completed bool |
||||
} |
||||
|
||||
func (s *FixtureCheckHelper) SetUpSuite(c *C) { |
||||
switch s.fail { |
||||
case "SetUpSuiteAssert": |
||||
c.Assert(false, Equals, true) |
||||
case "SetUpSuiteCheck": |
||||
c.Check(false, Equals, true) |
||||
} |
||||
s.completed = true |
||||
} |
||||
|
||||
func (s *FixtureCheckHelper) SetUpTest(c *C) { |
||||
switch s.fail { |
||||
case "SetUpTestAssert": |
||||
c.Assert(false, Equals, true) |
||||
case "SetUpTestCheck": |
||||
c.Check(false, Equals, true) |
||||
} |
||||
s.completed = true |
||||
} |
||||
|
||||
func (s *FixtureCheckHelper) Test(c *C) { |
||||
// Do nothing.
|
||||
} |
||||
|
||||
func (s *FixtureS) TestSetUpSuiteCheck(c *C) { |
||||
helper := FixtureCheckHelper{fail: "SetUpSuiteCheck"} |
||||
output := String{} |
||||
Run(&helper, &RunConf{Output: &output}) |
||||
c.Assert(output.value, Matches, |
||||
"\n---+\n"+ |
||||
"FAIL: fixture_test\\.go:[0-9]+: "+ |
||||
"FixtureCheckHelper\\.SetUpSuite\n\n"+ |
||||
"fixture_test\\.go:[0-9]+:\n"+ |
||||
" c\\.Check\\(false, Equals, true\\)\n"+ |
||||
"\\.+ obtained bool = false\n"+ |
||||
"\\.+ expected bool = true\n\n") |
||||
c.Assert(helper.completed, Equals, true) |
||||
} |
||||
|
||||
func (s *FixtureS) TestSetUpSuiteAssert(c *C) { |
||||
helper := FixtureCheckHelper{fail: "SetUpSuiteAssert"} |
||||
output := String{} |
||||
Run(&helper, &RunConf{Output: &output}) |
||||
c.Assert(output.value, Matches, |
||||
"\n---+\n"+ |
||||
"FAIL: fixture_test\\.go:[0-9]+: "+ |
||||
"FixtureCheckHelper\\.SetUpSuite\n\n"+ |
||||
"fixture_test\\.go:[0-9]+:\n"+ |
||||
" c\\.Assert\\(false, Equals, true\\)\n"+ |
||||
"\\.+ obtained bool = false\n"+ |
||||
"\\.+ expected bool = true\n\n") |
||||
c.Assert(helper.completed, Equals, false) |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Verify that logging within SetUpTest() persists within the test log itself.
|
||||
|
||||
type FixtureLogHelper struct { |
||||
c *C |
||||
} |
||||
|
||||
func (s *FixtureLogHelper) SetUpTest(c *C) { |
||||
s.c = c |
||||
c.Log("1") |
||||
} |
||||
|
||||
func (s *FixtureLogHelper) Test(c *C) { |
||||
c.Log("2") |
||||
s.c.Log("3") |
||||
c.Log("4") |
||||
c.Fail() |
||||
} |
||||
|
||||
func (s *FixtureLogHelper) TearDownTest(c *C) { |
||||
s.c.Log("5") |
||||
} |
||||
|
||||
func (s *FixtureS) TestFixtureLogging(c *C) { |
||||
helper := FixtureLogHelper{} |
||||
output := String{} |
||||
Run(&helper, &RunConf{Output: &output}) |
||||
c.Assert(output.value, Matches, |
||||
"\n---+\n"+ |
||||
"FAIL: fixture_test\\.go:[0-9]+: "+ |
||||
"FixtureLogHelper\\.Test\n\n"+ |
||||
"1\n2\n3\n4\n5\n") |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Skip() within fixture methods.
|
||||
|
||||
func (s *FixtureS) TestSkipSuite(c *C) { |
||||
helper := FixtureHelper{skip: true, skipOnN: 0} |
||||
output := String{} |
||||
result := Run(&helper, &RunConf{Output: &output}) |
||||
c.Assert(output.value, Equals, "") |
||||
c.Assert(helper.calls[0], Equals, "SetUpSuite") |
||||
c.Assert(helper.calls[1], Equals, "TearDownSuite") |
||||
c.Assert(len(helper.calls), Equals, 2) |
||||
c.Assert(result.Skipped, Equals, 2) |
||||
} |
||||
|
||||
func (s *FixtureS) TestSkipTest(c *C) { |
||||
helper := FixtureHelper{skip: true, skipOnN: 1} |
||||
output := String{} |
||||
result := Run(&helper, &RunConf{Output: &output}) |
||||
c.Assert(helper.calls[0], Equals, "SetUpSuite") |
||||
c.Assert(helper.calls[1], Equals, "SetUpTest") |
||||
c.Assert(helper.calls[2], Equals, "SetUpTest") |
||||
c.Assert(helper.calls[3], Equals, "Test2") |
||||
c.Assert(helper.calls[4], Equals, "TearDownTest") |
||||
c.Assert(helper.calls[5], Equals, "TearDownSuite") |
||||
c.Assert(len(helper.calls), Equals, 6) |
||||
c.Assert(result.Skipped, Equals, 1) |
||||
} |
@ -0,0 +1,335 @@ |
||||
// These tests check that the foundations of gocheck are working properly.
|
||||
// They already assume that fundamental failing is working already, though,
|
||||
// since this was tested in bootstrap_test.go. Even then, some care may
|
||||
// still have to be taken when using external functions, since they should
|
||||
// of course not rely on functionality tested here.
|
||||
|
||||
package check_test |
||||
|
||||
import ( |
||||
"fmt" |
||||
"gopkg.in/check.v1" |
||||
"log" |
||||
"os" |
||||
"regexp" |
||||
"strings" |
||||
) |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Foundation test suite.
|
||||
|
||||
type FoundationS struct{} |
||||
|
||||
var foundationS = check.Suite(&FoundationS{}) |
||||
|
||||
func (s *FoundationS) TestCountSuite(c *check.C) { |
||||
suitesRun += 1 |
||||
} |
||||
|
||||
func (s *FoundationS) TestErrorf(c *check.C) { |
||||
// Do not use checkState() here. It depends on Errorf() working.
|
||||
expectedLog := fmt.Sprintf("foundation_test.go:%d:\n"+ |
||||
" c.Errorf(\"Error %%v!\", \"message\")\n"+ |
||||
"... Error: Error message!\n\n", |
||||
getMyLine()+1) |
||||
c.Errorf("Error %v!", "message") |
||||
failed := c.Failed() |
||||
c.Succeed() |
||||
if log := c.GetTestLog(); log != expectedLog { |
||||
c.Logf("Errorf() logged %#v rather than %#v", log, expectedLog) |
||||
c.Fail() |
||||
} |
||||
if !failed { |
||||
c.Logf("Errorf() didn't put the test in a failed state") |
||||
c.Fail() |
||||
} |
||||
} |
||||
|
||||
func (s *FoundationS) TestError(c *check.C) { |
||||
expectedLog := fmt.Sprintf("foundation_test.go:%d:\n"+ |
||||
" c\\.Error\\(\"Error \", \"message!\"\\)\n"+ |
||||
"\\.\\.\\. Error: Error message!\n\n", |
||||
getMyLine()+1) |
||||
c.Error("Error ", "message!") |
||||
checkState(c, nil, |
||||
&expectedState{ |
||||
name: "Error(`Error `, `message!`)", |
||||
failed: true, |
||||
log: expectedLog, |
||||
}) |
||||
} |
||||
|
||||
func (s *FoundationS) TestFailNow(c *check.C) { |
||||
defer (func() { |
||||
if !c.Failed() { |
||||
c.Error("FailNow() didn't fail the test") |
||||
} else { |
||||
c.Succeed() |
||||
if c.GetTestLog() != "" { |
||||
c.Error("Something got logged:\n" + c.GetTestLog()) |
||||
} |
||||
} |
||||
})() |
||||
|
||||
c.FailNow() |
||||
c.Log("FailNow() didn't stop the test") |
||||
} |
||||
|
||||
func (s *FoundationS) TestSucceedNow(c *check.C) { |
||||
defer (func() { |
||||
if c.Failed() { |
||||
c.Error("SucceedNow() didn't succeed the test") |
||||
} |
||||
if c.GetTestLog() != "" { |
||||
c.Error("Something got logged:\n" + c.GetTestLog()) |
||||
} |
||||
})() |
||||
|
||||
c.Fail() |
||||
c.SucceedNow() |
||||
c.Log("SucceedNow() didn't stop the test") |
||||
} |
||||
|
||||
func (s *FoundationS) TestFailureHeader(c *check.C) { |
||||
output := String{} |
||||
failHelper := FailHelper{} |
||||
check.Run(&failHelper, &check.RunConf{Output: &output}) |
||||
header := fmt.Sprintf(""+ |
||||
"\n-----------------------------------"+ |
||||
"-----------------------------------\n"+ |
||||
"FAIL: check_test.go:%d: FailHelper.TestLogAndFail\n", |
||||
failHelper.testLine) |
||||
if strings.Index(output.value, header) == -1 { |
||||
c.Errorf(""+ |
||||
"Failure didn't print a proper header.\n"+ |
||||
"... Got:\n%s... Expected something with:\n%s", |
||||
output.value, header) |
||||
} |
||||
} |
||||
|
||||
func (s *FoundationS) TestFatal(c *check.C) { |
||||
var line int |
||||
defer (func() { |
||||
if !c.Failed() { |
||||
c.Error("Fatal() didn't fail the test") |
||||
} else { |
||||
c.Succeed() |
||||
expected := fmt.Sprintf("foundation_test.go:%d:\n"+ |
||||
" c.Fatal(\"Die \", \"now!\")\n"+ |
||||
"... Error: Die now!\n\n", |
||||
line) |
||||
if c.GetTestLog() != expected { |
||||
c.Error("Incorrect log:", c.GetTestLog()) |
||||
} |
||||
} |
||||
})() |
||||
|
||||
line = getMyLine() + 1 |
||||
c.Fatal("Die ", "now!") |
||||
c.Log("Fatal() didn't stop the test") |
||||
} |
||||
|
||||
func (s *FoundationS) TestFatalf(c *check.C) { |
||||
var line int |
||||
defer (func() { |
||||
if !c.Failed() { |
||||
c.Error("Fatalf() didn't fail the test") |
||||
} else { |
||||
c.Succeed() |
||||
expected := fmt.Sprintf("foundation_test.go:%d:\n"+ |
||||
" c.Fatalf(\"Die %%s!\", \"now\")\n"+ |
||||
"... Error: Die now!\n\n", |
||||
line) |
||||
if c.GetTestLog() != expected { |
||||
c.Error("Incorrect log:", c.GetTestLog()) |
||||
} |
||||
} |
||||
})() |
||||
|
||||
line = getMyLine() + 1 |
||||
c.Fatalf("Die %s!", "now") |
||||
c.Log("Fatalf() didn't stop the test") |
||||
} |
||||
|
||||
func (s *FoundationS) TestCallerLoggingInsideTest(c *check.C) { |
||||
log := fmt.Sprintf(""+ |
||||
"foundation_test.go:%d:\n"+ |
||||
" result := c.Check\\(10, check.Equals, 20\\)\n"+ |
||||
"\\.\\.\\. obtained int = 10\n"+ |
||||
"\\.\\.\\. expected int = 20\n\n", |
||||
getMyLine()+1) |
||||
result := c.Check(10, check.Equals, 20) |
||||
checkState(c, result, |
||||
&expectedState{ |
||||
name: "Check(10, Equals, 20)", |
||||
result: false, |
||||
failed: true, |
||||
log: log, |
||||
}) |
||||
} |
||||
|
||||
func (s *FoundationS) TestCallerLoggingInDifferentFile(c *check.C) { |
||||
result, line := checkEqualWrapper(c, 10, 20) |
||||
testLine := getMyLine() - 1 |
||||
log := fmt.Sprintf(""+ |
||||
"foundation_test.go:%d:\n"+ |
||||
" result, line := checkEqualWrapper\\(c, 10, 20\\)\n"+ |
||||
"check_test.go:%d:\n"+ |
||||
" return c.Check\\(obtained, check.Equals, expected\\), getMyLine\\(\\)\n"+ |
||||
"\\.\\.\\. obtained int = 10\n"+ |
||||
"\\.\\.\\. expected int = 20\n\n", |
||||
testLine, line) |
||||
checkState(c, result, |
||||
&expectedState{ |
||||
name: "Check(10, Equals, 20)", |
||||
result: false, |
||||
failed: true, |
||||
log: log, |
||||
}) |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// ExpectFailure() inverts the logic of failure.
|
||||
|
||||
type ExpectFailureSucceedHelper struct{} |
||||
|
||||
func (s *ExpectFailureSucceedHelper) TestSucceed(c *check.C) { |
||||
c.ExpectFailure("It booms!") |
||||
c.Error("Boom!") |
||||
} |
||||
|
||||
type ExpectFailureFailHelper struct{} |
||||
|
||||
func (s *ExpectFailureFailHelper) TestFail(c *check.C) { |
||||
c.ExpectFailure("Bug #XYZ") |
||||
} |
||||
|
||||
func (s *FoundationS) TestExpectFailureFail(c *check.C) { |
||||
helper := ExpectFailureFailHelper{} |
||||
output := String{} |
||||
result := check.Run(&helper, &check.RunConf{Output: &output}) |
||||
|
||||
expected := "" + |
||||
"^\n-+\n" + |
||||
"FAIL: foundation_test\\.go:[0-9]+:" + |
||||
" ExpectFailureFailHelper\\.TestFail\n\n" + |
||||
"\\.\\.\\. Error: Test succeeded, but was expected to fail\n" + |
||||
"\\.\\.\\. Reason: Bug #XYZ\n$" |
||||
|
||||
matched, err := regexp.MatchString(expected, output.value) |
||||
if err != nil { |
||||
c.Error("Bad expression: ", expected) |
||||
} else if !matched { |
||||
c.Error("ExpectFailure() didn't log properly:\n", output.value) |
||||
} |
||||
|
||||
c.Assert(result.ExpectedFailures, check.Equals, 0) |
||||
} |
||||
|
||||
func (s *FoundationS) TestExpectFailureSucceed(c *check.C) { |
||||
helper := ExpectFailureSucceedHelper{} |
||||
output := String{} |
||||
result := check.Run(&helper, &check.RunConf{Output: &output}) |
||||
|
||||
c.Assert(output.value, check.Equals, "") |
||||
c.Assert(result.ExpectedFailures, check.Equals, 1) |
||||
} |
||||
|
||||
func (s *FoundationS) TestExpectFailureSucceedVerbose(c *check.C) { |
||||
helper := ExpectFailureSucceedHelper{} |
||||
output := String{} |
||||
result := check.Run(&helper, &check.RunConf{Output: &output, Verbose: true}) |
||||
|
||||
expected := "" + |
||||
"FAIL EXPECTED: foundation_test\\.go:[0-9]+:" + |
||||
" ExpectFailureSucceedHelper\\.TestSucceed \\(It booms!\\)\t *[.0-9]+s\n" |
||||
|
||||
matched, err := regexp.MatchString(expected, output.value) |
||||
if err != nil { |
||||
c.Error("Bad expression: ", expected) |
||||
} else if !matched { |
||||
c.Error("ExpectFailure() didn't log properly:\n", output.value) |
||||
} |
||||
|
||||
c.Assert(result.ExpectedFailures, check.Equals, 1) |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Skip() allows stopping a test without positive/negative results.
|
||||
|
||||
type SkipTestHelper struct{} |
||||
|
||||
func (s *SkipTestHelper) TestFail(c *check.C) { |
||||
c.Skip("Wrong platform or whatever") |
||||
c.Error("Boom!") |
||||
} |
||||
|
||||
func (s *FoundationS) TestSkip(c *check.C) { |
||||
helper := SkipTestHelper{} |
||||
output := String{} |
||||
check.Run(&helper, &check.RunConf{Output: &output}) |
||||
|
||||
if output.value != "" { |
||||
c.Error("Skip() logged something:\n", output.value) |
||||
} |
||||
} |
||||
|
||||
func (s *FoundationS) TestSkipVerbose(c *check.C) { |
||||
helper := SkipTestHelper{} |
||||
output := String{} |
||||
check.Run(&helper, &check.RunConf{Output: &output, Verbose: true}) |
||||
|
||||
expected := "SKIP: foundation_test\\.go:[0-9]+: SkipTestHelper\\.TestFail" + |
||||
" \\(Wrong platform or whatever\\)" |
||||
matched, err := regexp.MatchString(expected, output.value) |
||||
if err != nil { |
||||
c.Error("Bad expression: ", expected) |
||||
} else if !matched { |
||||
c.Error("Skip() didn't log properly:\n", output.value) |
||||
} |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Check minimum *log.Logger interface provided by *check.C.
|
||||
|
||||
type minLogger interface { |
||||
Output(calldepth int, s string) error |
||||
} |
||||
|
||||
func (s *BootstrapS) TestMinLogger(c *check.C) { |
||||
var logger minLogger |
||||
logger = log.New(os.Stderr, "", 0) |
||||
logger = c |
||||
logger.Output(0, "Hello there") |
||||
expected := `\[LOG\] [0-9]+:[0-9][0-9]\.[0-9][0-9][0-9] +Hello there\n` |
||||
output := c.GetTestLog() |
||||
c.Assert(output, check.Matches, expected) |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Ensure that suites with embedded types are working fine, including the
|
||||
// the workaround for issue 906.
|
||||
|
||||
type EmbeddedInternalS struct { |
||||
called bool |
||||
} |
||||
|
||||
type EmbeddedS struct { |
||||
EmbeddedInternalS |
||||
} |
||||
|
||||
var embeddedS = check.Suite(&EmbeddedS{}) |
||||
|
||||
func (s *EmbeddedS) TestCountSuite(c *check.C) { |
||||
suitesRun += 1 |
||||
} |
||||
|
||||
func (s *EmbeddedInternalS) TestMethod(c *check.C) { |
||||
c.Error("TestMethod() of the embedded type was called!?") |
||||
} |
||||
|
||||
func (s *EmbeddedS) TestMethod(c *check.C) { |
||||
// http://code.google.com/p/go/issues/detail?id=906
|
||||
c.Check(s.called, check.Equals, false) // Go issue 906 is affecting the runner?
|
||||
s.called = true |
||||
} |
@ -0,0 +1,231 @@ |
||||
package check |
||||
|
||||
import ( |
||||
"fmt" |
||||
"strings" |
||||
"time" |
||||
) |
||||
|
||||
// TestName returns the current test name in the form "SuiteName.TestName"
|
||||
func (c *C) TestName() string { |
||||
return c.testName |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Basic succeeding/failing logic.
|
||||
|
||||
// Failed returns whether the currently running test has already failed.
|
||||
func (c *C) Failed() bool { |
||||
return c.status == failedSt |
||||
} |
||||
|
||||
// Fail marks the currently running test as failed.
|
||||
//
|
||||
// Something ought to have been previously logged so the developer can tell
|
||||
// what went wrong. The higher level helper functions will fail the test
|
||||
// and do the logging properly.
|
||||
func (c *C) Fail() { |
||||
c.status = failedSt |
||||
} |
||||
|
||||
// FailNow marks the currently running test as failed and stops running it.
|
||||
// Something ought to have been previously logged so the developer can tell
|
||||
// what went wrong. The higher level helper functions will fail the test
|
||||
// and do the logging properly.
|
||||
func (c *C) FailNow() { |
||||
c.Fail() |
||||
c.stopNow() |
||||
} |
||||
|
||||
// Succeed marks the currently running test as succeeded, undoing any
|
||||
// previous failures.
|
||||
func (c *C) Succeed() { |
||||
c.status = succeededSt |
||||
} |
||||
|
||||
// SucceedNow marks the currently running test as succeeded, undoing any
|
||||
// previous failures, and stops running the test.
|
||||
func (c *C) SucceedNow() { |
||||
c.Succeed() |
||||
c.stopNow() |
||||
} |
||||
|
||||
// ExpectFailure informs that the running test is knowingly broken for
|
||||
// the provided reason. If the test does not fail, an error will be reported
|
||||
// to raise attention to this fact. This method is useful to temporarily
|
||||
// disable tests which cover well known problems until a better time to
|
||||
// fix the problem is found, without forgetting about the fact that a
|
||||
// failure still exists.
|
||||
func (c *C) ExpectFailure(reason string) { |
||||
if reason == "" { |
||||
panic("Missing reason why the test is expected to fail") |
||||
} |
||||
c.mustFail = true |
||||
c.reason = reason |
||||
} |
||||
|
||||
// Skip skips the running test for the provided reason. If run from within
|
||||
// SetUpTest, the individual test being set up will be skipped, and if run
|
||||
// from within SetUpSuite, the whole suite is skipped.
|
||||
func (c *C) Skip(reason string) { |
||||
if reason == "" { |
||||
panic("Missing reason why the test is being skipped") |
||||
} |
||||
c.reason = reason |
||||
c.status = skippedSt |
||||
c.stopNow() |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Basic logging.
|
||||
|
||||
// GetTestLog returns the current test error output.
|
||||
func (c *C) GetTestLog() string { |
||||
return c.logb.String() |
||||
} |
||||
|
||||
// Log logs some information into the test error output.
|
||||
// The provided arguments are assembled together into a string with fmt.Sprint.
|
||||
func (c *C) Log(args ...interface{}) { |
||||
c.log(args...) |
||||
} |
||||
|
||||
// Log logs some information into the test error output.
|
||||
// The provided arguments are assembled together into a string with fmt.Sprintf.
|
||||
func (c *C) Logf(format string, args ...interface{}) { |
||||
c.logf(format, args...) |
||||
} |
||||
|
||||
// Output enables *C to be used as a logger in functions that require only
|
||||
// the minimum interface of *log.Logger.
|
||||
func (c *C) Output(calldepth int, s string) error { |
||||
d := time.Now().Sub(c.startTime) |
||||
msec := d / time.Millisecond |
||||
sec := d / time.Second |
||||
min := d / time.Minute |
||||
|
||||
c.Logf("[LOG] %d:%02d.%03d %s", min, sec%60, msec%1000, s) |
||||
return nil |
||||
} |
||||
|
||||
// Error logs an error into the test error output and marks the test as failed.
|
||||
// The provided arguments are assembled together into a string with fmt.Sprint.
|
||||
func (c *C) Error(args ...interface{}) { |
||||
c.logCaller(1) |
||||
c.logString(fmt.Sprint("Error: ", fmt.Sprint(args...))) |
||||
c.logNewLine() |
||||
c.Fail() |
||||
} |
||||
|
||||
// Errorf logs an error into the test error output and marks the test as failed.
|
||||
// The provided arguments are assembled together into a string with fmt.Sprintf.
|
||||
func (c *C) Errorf(format string, args ...interface{}) { |
||||
c.logCaller(1) |
||||
c.logString(fmt.Sprintf("Error: "+format, args...)) |
||||
c.logNewLine() |
||||
c.Fail() |
||||
} |
||||
|
||||
// Fatal logs an error into the test error output, marks the test as failed, and
|
||||
// stops the test execution. The provided arguments are assembled together into
|
||||
// a string with fmt.Sprint.
|
||||
func (c *C) Fatal(args ...interface{}) { |
||||
c.logCaller(1) |
||||
c.logString(fmt.Sprint("Error: ", fmt.Sprint(args...))) |
||||
c.logNewLine() |
||||
c.FailNow() |
||||
} |
||||
|
||||
// Fatlaf logs an error into the test error output, marks the test as failed, and
|
||||
// stops the test execution. The provided arguments are assembled together into
|
||||
// a string with fmt.Sprintf.
|
||||
func (c *C) Fatalf(format string, args ...interface{}) { |
||||
c.logCaller(1) |
||||
c.logString(fmt.Sprint("Error: ", fmt.Sprintf(format, args...))) |
||||
c.logNewLine() |
||||
c.FailNow() |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Generic checks and assertions based on checkers.
|
||||
|
||||
// Check verifies if the first value matches the expected value according
|
||||
// to the provided checker. If they do not match, an error is logged, the
|
||||
// test is marked as failed, and the test execution continues.
|
||||
//
|
||||
// Some checkers may not need the expected argument (e.g. IsNil).
|
||||
//
|
||||
// Extra arguments provided to the function are logged next to the reported
|
||||
// problem when the matching fails.
|
||||
func (c *C) Check(obtained interface{}, checker Checker, args ...interface{}) bool { |
||||
return c.internalCheck("Check", obtained, checker, args...) |
||||
} |
||||
|
||||
// Assert ensures that the first value matches the expected value according
|
||||
// to the provided checker. If they do not match, an error is logged, the
|
||||
// test is marked as failed, and the test execution stops.
|
||||
//
|
||||
// Some checkers may not need the expected argument (e.g. IsNil).
|
||||
//
|
||||
// Extra arguments provided to the function are logged next to the reported
|
||||
// problem when the matching fails.
|
||||
func (c *C) Assert(obtained interface{}, checker Checker, args ...interface{}) { |
||||
if !c.internalCheck("Assert", obtained, checker, args...) { |
||||
c.stopNow() |
||||
} |
||||
} |
||||
|
||||
func (c *C) internalCheck(funcName string, obtained interface{}, checker Checker, args ...interface{}) bool { |
||||
if checker == nil { |
||||
c.logCaller(2) |
||||
c.logString(fmt.Sprintf("%s(obtained, nil!?, ...):", funcName)) |
||||
c.logString("Oops.. you've provided a nil checker!") |
||||
c.logNewLine() |
||||
c.Fail() |
||||
return false |
||||
} |
||||
|
||||
// If the last argument is a bug info, extract it out.
|
||||
var comment CommentInterface |
||||
if len(args) > 0 { |
||||
if c, ok := args[len(args)-1].(CommentInterface); ok { |
||||
comment = c |
||||
args = args[:len(args)-1] |
||||
} |
||||
} |
||||
|
||||
params := append([]interface{}{obtained}, args...) |
||||
info := checker.Info() |
||||
|
||||
if len(params) != len(info.Params) { |
||||
names := append([]string{info.Params[0], info.Name}, info.Params[1:]...) |
||||
c.logCaller(2) |
||||
c.logString(fmt.Sprintf("%s(%s):", funcName, strings.Join(names, ", "))) |
||||
c.logString(fmt.Sprintf("Wrong number of parameters for %s: want %d, got %d", info.Name, len(names), len(params)+1)) |
||||
c.logNewLine() |
||||
c.Fail() |
||||
return false |
||||
} |
||||
|
||||
// Copy since it may be mutated by Check.
|
||||
names := append([]string{}, info.Params...) |
||||
|
||||
// Do the actual check.
|
||||
result, error := checker.Check(params, names) |
||||
if !result || error != "" { |
||||
c.logCaller(2) |
||||
for i := 0; i != len(params); i++ { |
||||
c.logValue(names[i], params[i]) |
||||
} |
||||
if comment != nil { |
||||
c.logString(comment.CheckCommentString()) |
||||
} |
||||
if error != "" { |
||||
c.logString(error) |
||||
} |
||||
c.logNewLine() |
||||
c.Fail() |
||||
return false |
||||
} |
||||
return true |
||||
} |
@ -0,0 +1,519 @@ |
||||
// These tests verify the inner workings of the helper methods associated
|
||||
// with check.T.
|
||||
|
||||
package check_test |
||||
|
||||
import ( |
||||
"gopkg.in/check.v1" |
||||
"os" |
||||
"reflect" |
||||
"runtime" |
||||
"sync" |
||||
) |
||||
|
||||
var helpersS = check.Suite(&HelpersS{}) |
||||
|
||||
type HelpersS struct{} |
||||
|
||||
func (s *HelpersS) TestCountSuite(c *check.C) { |
||||
suitesRun += 1 |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Fake checker and bug info to verify the behavior of Assert() and Check().
|
||||
|
||||
type MyChecker struct { |
||||
info *check.CheckerInfo |
||||
params []interface{} |
||||
names []string |
||||
result bool |
||||
error string |
||||
} |
||||
|
||||
func (checker *MyChecker) Info() *check.CheckerInfo { |
||||
if checker.info == nil { |
||||
return &check.CheckerInfo{Name: "MyChecker", Params: []string{"myobtained", "myexpected"}} |
||||
} |
||||
return checker.info |
||||
} |
||||
|
||||
func (checker *MyChecker) Check(params []interface{}, names []string) (bool, string) { |
||||
rparams := checker.params |
||||
rnames := checker.names |
||||
checker.params = append([]interface{}{}, params...) |
||||
checker.names = append([]string{}, names...) |
||||
if rparams != nil { |
||||
copy(params, rparams) |
||||
} |
||||
if rnames != nil { |
||||
copy(names, rnames) |
||||
} |
||||
return checker.result, checker.error |
||||
} |
||||
|
||||
type myCommentType string |
||||
|
||||
func (c myCommentType) CheckCommentString() string { |
||||
return string(c) |
||||
} |
||||
|
||||
func myComment(s string) myCommentType { |
||||
return myCommentType(s) |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Ensure a real checker actually works fine.
|
||||
|
||||
func (s *HelpersS) TestCheckerInterface(c *check.C) { |
||||
testHelperSuccess(c, "Check(1, Equals, 1)", true, func() interface{} { |
||||
return c.Check(1, check.Equals, 1) |
||||
}) |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Tests for Check(), mostly the same as for Assert() following these.
|
||||
|
||||
func (s *HelpersS) TestCheckSucceedWithExpected(c *check.C) { |
||||
checker := &MyChecker{result: true} |
||||
testHelperSuccess(c, "Check(1, checker, 2)", true, func() interface{} { |
||||
return c.Check(1, checker, 2) |
||||
}) |
||||
if !reflect.DeepEqual(checker.params, []interface{}{1, 2}) { |
||||
c.Fatalf("Bad params for check: %#v", checker.params) |
||||
} |
||||
} |
||||
|
||||
func (s *HelpersS) TestCheckSucceedWithoutExpected(c *check.C) { |
||||
checker := &MyChecker{result: true, info: &check.CheckerInfo{Params: []string{"myvalue"}}} |
||||
testHelperSuccess(c, "Check(1, checker)", true, func() interface{} { |
||||
return c.Check(1, checker) |
||||
}) |
||||
if !reflect.DeepEqual(checker.params, []interface{}{1}) { |
||||
c.Fatalf("Bad params for check: %#v", checker.params) |
||||
} |
||||
} |
||||
|
||||
func (s *HelpersS) TestCheckFailWithExpected(c *check.C) { |
||||
checker := &MyChecker{result: false} |
||||
log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
||||
" return c\\.Check\\(1, checker, 2\\)\n" + |
||||
"\\.+ myobtained int = 1\n" + |
||||
"\\.+ myexpected int = 2\n\n" |
||||
testHelperFailure(c, "Check(1, checker, 2)", false, false, log, |
||||
func() interface{} { |
||||
return c.Check(1, checker, 2) |
||||
}) |
||||
} |
||||
|
||||
func (s *HelpersS) TestCheckFailWithExpectedAndComment(c *check.C) { |
||||
checker := &MyChecker{result: false} |
||||
log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
||||
" return c\\.Check\\(1, checker, 2, myComment\\(\"Hello world!\"\\)\\)\n" + |
||||
"\\.+ myobtained int = 1\n" + |
||||
"\\.+ myexpected int = 2\n" + |
||||
"\\.+ Hello world!\n\n" |
||||
testHelperFailure(c, "Check(1, checker, 2, msg)", false, false, log, |
||||
func() interface{} { |
||||
return c.Check(1, checker, 2, myComment("Hello world!")) |
||||
}) |
||||
} |
||||
|
||||
func (s *HelpersS) TestCheckFailWithExpectedAndStaticComment(c *check.C) { |
||||
checker := &MyChecker{result: false} |
||||
log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
||||
" // Nice leading comment\\.\n" + |
||||
" return c\\.Check\\(1, checker, 2\\) // Hello there\n" + |
||||
"\\.+ myobtained int = 1\n" + |
||||
"\\.+ myexpected int = 2\n\n" |
||||
testHelperFailure(c, "Check(1, checker, 2, msg)", false, false, log, |
||||
func() interface{} { |
||||
// Nice leading comment.
|
||||
return c.Check(1, checker, 2) // Hello there
|
||||
}) |
||||
} |
||||
|
||||
func (s *HelpersS) TestCheckFailWithoutExpected(c *check.C) { |
||||
checker := &MyChecker{result: false, info: &check.CheckerInfo{Params: []string{"myvalue"}}} |
||||
log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
||||
" return c\\.Check\\(1, checker\\)\n" + |
||||
"\\.+ myvalue int = 1\n\n" |
||||
testHelperFailure(c, "Check(1, checker)", false, false, log, |
||||
func() interface{} { |
||||
return c.Check(1, checker) |
||||
}) |
||||
} |
||||
|
||||
func (s *HelpersS) TestCheckFailWithoutExpectedAndMessage(c *check.C) { |
||||
checker := &MyChecker{result: false, info: &check.CheckerInfo{Params: []string{"myvalue"}}} |
||||
log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
||||
" return c\\.Check\\(1, checker, myComment\\(\"Hello world!\"\\)\\)\n" + |
||||
"\\.+ myvalue int = 1\n" + |
||||
"\\.+ Hello world!\n\n" |
||||
testHelperFailure(c, "Check(1, checker, msg)", false, false, log, |
||||
func() interface{} { |
||||
return c.Check(1, checker, myComment("Hello world!")) |
||||
}) |
||||
} |
||||
|
||||
func (s *HelpersS) TestCheckWithMissingExpected(c *check.C) { |
||||
checker := &MyChecker{result: true} |
||||
log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
||||
" return c\\.Check\\(1, checker\\)\n" + |
||||
"\\.+ Check\\(myobtained, MyChecker, myexpected\\):\n" + |
||||
"\\.+ Wrong number of parameters for MyChecker: " + |
||||
"want 3, got 2\n\n" |
||||
testHelperFailure(c, "Check(1, checker, !?)", false, false, log, |
||||
func() interface{} { |
||||
return c.Check(1, checker) |
||||
}) |
||||
} |
||||
|
||||
func (s *HelpersS) TestCheckWithTooManyExpected(c *check.C) { |
||||
checker := &MyChecker{result: true} |
||||
log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
||||
" return c\\.Check\\(1, checker, 2, 3\\)\n" + |
||||
"\\.+ Check\\(myobtained, MyChecker, myexpected\\):\n" + |
||||
"\\.+ Wrong number of parameters for MyChecker: " + |
||||
"want 3, got 4\n\n" |
||||
testHelperFailure(c, "Check(1, checker, 2, 3)", false, false, log, |
||||
func() interface{} { |
||||
return c.Check(1, checker, 2, 3) |
||||
}) |
||||
} |
||||
|
||||
func (s *HelpersS) TestCheckWithError(c *check.C) { |
||||
checker := &MyChecker{result: false, error: "Some not so cool data provided!"} |
||||
log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
||||
" return c\\.Check\\(1, checker, 2\\)\n" + |
||||
"\\.+ myobtained int = 1\n" + |
||||
"\\.+ myexpected int = 2\n" + |
||||
"\\.+ Some not so cool data provided!\n\n" |
||||
testHelperFailure(c, "Check(1, checker, 2)", false, false, log, |
||||
func() interface{} { |
||||
return c.Check(1, checker, 2) |
||||
}) |
||||
} |
||||
|
||||
func (s *HelpersS) TestCheckWithNilChecker(c *check.C) { |
||||
log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
||||
" return c\\.Check\\(1, nil\\)\n" + |
||||
"\\.+ Check\\(obtained, nil!\\?, \\.\\.\\.\\):\n" + |
||||
"\\.+ Oops\\.\\. you've provided a nil checker!\n\n" |
||||
testHelperFailure(c, "Check(obtained, nil)", false, false, log, |
||||
func() interface{} { |
||||
return c.Check(1, nil) |
||||
}) |
||||
} |
||||
|
||||
func (s *HelpersS) TestCheckWithParamsAndNamesMutation(c *check.C) { |
||||
checker := &MyChecker{result: false, params: []interface{}{3, 4}, names: []string{"newobtained", "newexpected"}} |
||||
log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
||||
" return c\\.Check\\(1, checker, 2\\)\n" + |
||||
"\\.+ newobtained int = 3\n" + |
||||
"\\.+ newexpected int = 4\n\n" |
||||
testHelperFailure(c, "Check(1, checker, 2) with mutation", false, false, log, |
||||
func() interface{} { |
||||
return c.Check(1, checker, 2) |
||||
}) |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Tests for Assert(), mostly the same as for Check() above.
|
||||
|
||||
func (s *HelpersS) TestAssertSucceedWithExpected(c *check.C) { |
||||
checker := &MyChecker{result: true} |
||||
testHelperSuccess(c, "Assert(1, checker, 2)", nil, func() interface{} { |
||||
c.Assert(1, checker, 2) |
||||
return nil |
||||
}) |
||||
if !reflect.DeepEqual(checker.params, []interface{}{1, 2}) { |
||||
c.Fatalf("Bad params for check: %#v", checker.params) |
||||
} |
||||
} |
||||
|
||||
func (s *HelpersS) TestAssertSucceedWithoutExpected(c *check.C) { |
||||
checker := &MyChecker{result: true, info: &check.CheckerInfo{Params: []string{"myvalue"}}} |
||||
testHelperSuccess(c, "Assert(1, checker)", nil, func() interface{} { |
||||
c.Assert(1, checker) |
||||
return nil |
||||
}) |
||||
if !reflect.DeepEqual(checker.params, []interface{}{1}) { |
||||
c.Fatalf("Bad params for check: %#v", checker.params) |
||||
} |
||||
} |
||||
|
||||
func (s *HelpersS) TestAssertFailWithExpected(c *check.C) { |
||||
checker := &MyChecker{result: false} |
||||
log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
||||
" c\\.Assert\\(1, checker, 2\\)\n" + |
||||
"\\.+ myobtained int = 1\n" + |
||||
"\\.+ myexpected int = 2\n\n" |
||||
testHelperFailure(c, "Assert(1, checker, 2)", nil, true, log, |
||||
func() interface{} { |
||||
c.Assert(1, checker, 2) |
||||
return nil |
||||
}) |
||||
} |
||||
|
||||
func (s *HelpersS) TestAssertFailWithExpectedAndMessage(c *check.C) { |
||||
checker := &MyChecker{result: false} |
||||
log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
||||
" c\\.Assert\\(1, checker, 2, myComment\\(\"Hello world!\"\\)\\)\n" + |
||||
"\\.+ myobtained int = 1\n" + |
||||
"\\.+ myexpected int = 2\n" + |
||||
"\\.+ Hello world!\n\n" |
||||
testHelperFailure(c, "Assert(1, checker, 2, msg)", nil, true, log, |
||||
func() interface{} { |
||||
c.Assert(1, checker, 2, myComment("Hello world!")) |
||||
return nil |
||||
}) |
||||
} |
||||
|
||||
func (s *HelpersS) TestAssertFailWithoutExpected(c *check.C) { |
||||
checker := &MyChecker{result: false, info: &check.CheckerInfo{Params: []string{"myvalue"}}} |
||||
log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
||||
" c\\.Assert\\(1, checker\\)\n" + |
||||
"\\.+ myvalue int = 1\n\n" |
||||
testHelperFailure(c, "Assert(1, checker)", nil, true, log, |
||||
func() interface{} { |
||||
c.Assert(1, checker) |
||||
return nil |
||||
}) |
||||
} |
||||
|
||||
func (s *HelpersS) TestAssertFailWithoutExpectedAndMessage(c *check.C) { |
||||
checker := &MyChecker{result: false, info: &check.CheckerInfo{Params: []string{"myvalue"}}} |
||||
log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
||||
" c\\.Assert\\(1, checker, myComment\\(\"Hello world!\"\\)\\)\n" + |
||||
"\\.+ myvalue int = 1\n" + |
||||
"\\.+ Hello world!\n\n" |
||||
testHelperFailure(c, "Assert(1, checker, msg)", nil, true, log, |
||||
func() interface{} { |
||||
c.Assert(1, checker, myComment("Hello world!")) |
||||
return nil |
||||
}) |
||||
} |
||||
|
||||
func (s *HelpersS) TestAssertWithMissingExpected(c *check.C) { |
||||
checker := &MyChecker{result: true} |
||||
log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
||||
" c\\.Assert\\(1, checker\\)\n" + |
||||
"\\.+ Assert\\(myobtained, MyChecker, myexpected\\):\n" + |
||||
"\\.+ Wrong number of parameters for MyChecker: " + |
||||
"want 3, got 2\n\n" |
||||
testHelperFailure(c, "Assert(1, checker, !?)", nil, true, log, |
||||
func() interface{} { |
||||
c.Assert(1, checker) |
||||
return nil |
||||
}) |
||||
} |
||||
|
||||
func (s *HelpersS) TestAssertWithError(c *check.C) { |
||||
checker := &MyChecker{result: false, error: "Some not so cool data provided!"} |
||||
log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
||||
" c\\.Assert\\(1, checker, 2\\)\n" + |
||||
"\\.+ myobtained int = 1\n" + |
||||
"\\.+ myexpected int = 2\n" + |
||||
"\\.+ Some not so cool data provided!\n\n" |
||||
testHelperFailure(c, "Assert(1, checker, 2)", nil, true, log, |
||||
func() interface{} { |
||||
c.Assert(1, checker, 2) |
||||
return nil |
||||
}) |
||||
} |
||||
|
||||
func (s *HelpersS) TestAssertWithNilChecker(c *check.C) { |
||||
log := "(?s)helpers_test\\.go:[0-9]+:.*\nhelpers_test\\.go:[0-9]+:\n" + |
||||
" c\\.Assert\\(1, nil\\)\n" + |
||||
"\\.+ Assert\\(obtained, nil!\\?, \\.\\.\\.\\):\n" + |
||||
"\\.+ Oops\\.\\. you've provided a nil checker!\n\n" |
||||
testHelperFailure(c, "Assert(obtained, nil)", nil, true, log, |
||||
func() interface{} { |
||||
c.Assert(1, nil) |
||||
return nil |
||||
}) |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Ensure that values logged work properly in some interesting cases.
|
||||
|
||||
func (s *HelpersS) TestValueLoggingWithArrays(c *check.C) { |
||||
checker := &MyChecker{result: false} |
||||
log := "(?s)helpers_test.go:[0-9]+:.*\nhelpers_test.go:[0-9]+:\n" + |
||||
" return c\\.Check\\(\\[\\]byte{1, 2}, checker, \\[\\]byte{1, 3}\\)\n" + |
||||
"\\.+ myobtained \\[\\]uint8 = \\[\\]byte{0x1, 0x2}\n" + |
||||
"\\.+ myexpected \\[\\]uint8 = \\[\\]byte{0x1, 0x3}\n\n" |
||||
testHelperFailure(c, "Check([]byte{1}, chk, []byte{3})", false, false, log, |
||||
func() interface{} { |
||||
return c.Check([]byte{1, 2}, checker, []byte{1, 3}) |
||||
}) |
||||
} |
||||
|
||||
func (s *HelpersS) TestValueLoggingWithMultiLine(c *check.C) { |
||||
checker := &MyChecker{result: false} |
||||
log := "(?s)helpers_test.go:[0-9]+:.*\nhelpers_test.go:[0-9]+:\n" + |
||||
" return c\\.Check\\(\"a\\\\nb\\\\n\", checker, \"a\\\\nb\\\\nc\"\\)\n" + |
||||
"\\.+ myobtained string = \"\" \\+\n" + |
||||
"\\.+ \"a\\\\n\" \\+\n" + |
||||
"\\.+ \"b\\\\n\"\n" + |
||||
"\\.+ myexpected string = \"\" \\+\n" + |
||||
"\\.+ \"a\\\\n\" \\+\n" + |
||||
"\\.+ \"b\\\\n\" \\+\n" + |
||||
"\\.+ \"c\"\n\n" |
||||
testHelperFailure(c, `Check("a\nb\n", chk, "a\nb\nc")`, false, false, log, |
||||
func() interface{} { |
||||
return c.Check("a\nb\n", checker, "a\nb\nc") |
||||
}) |
||||
} |
||||
|
||||
func (s *HelpersS) TestValueLoggingWithMultiLineException(c *check.C) { |
||||
// If the newline is at the end of the string, don't log as multi-line.
|
||||
checker := &MyChecker{result: false} |
||||
log := "(?s)helpers_test.go:[0-9]+:.*\nhelpers_test.go:[0-9]+:\n" + |
||||
" return c\\.Check\\(\"a b\\\\n\", checker, \"a\\\\nb\"\\)\n" + |
||||
"\\.+ myobtained string = \"a b\\\\n\"\n" + |
||||
"\\.+ myexpected string = \"\" \\+\n" + |
||||
"\\.+ \"a\\\\n\" \\+\n" + |
||||
"\\.+ \"b\"\n\n" |
||||
testHelperFailure(c, `Check("a b\n", chk, "a\nb")`, false, false, log, |
||||
func() interface{} { |
||||
return c.Check("a b\n", checker, "a\nb") |
||||
}) |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// MakeDir() tests.
|
||||
|
||||
type MkDirHelper struct { |
||||
path1 string |
||||
path2 string |
||||
isDir1 bool |
||||
isDir2 bool |
||||
isDir3 bool |
||||
isDir4 bool |
||||
} |
||||
|
||||
func (s *MkDirHelper) SetUpSuite(c *check.C) { |
||||
s.path1 = c.MkDir() |
||||
s.isDir1 = isDir(s.path1) |
||||
} |
||||
|
||||
func (s *MkDirHelper) Test(c *check.C) { |
||||
s.path2 = c.MkDir() |
||||
s.isDir2 = isDir(s.path2) |
||||
} |
||||
|
||||
func (s *MkDirHelper) TearDownSuite(c *check.C) { |
||||
s.isDir3 = isDir(s.path1) |
||||
s.isDir4 = isDir(s.path2) |
||||
} |
||||
|
||||
func (s *HelpersS) TestMkDir(c *check.C) { |
||||
helper := MkDirHelper{} |
||||
output := String{} |
||||
check.Run(&helper, &check.RunConf{Output: &output}) |
||||
c.Assert(output.value, check.Equals, "") |
||||
c.Check(helper.isDir1, check.Equals, true) |
||||
c.Check(helper.isDir2, check.Equals, true) |
||||
c.Check(helper.isDir3, check.Equals, true) |
||||
c.Check(helper.isDir4, check.Equals, true) |
||||
c.Check(helper.path1, check.Not(check.Equals), |
||||
helper.path2) |
||||
c.Check(isDir(helper.path1), check.Equals, false) |
||||
c.Check(isDir(helper.path2), check.Equals, false) |
||||
} |
||||
|
||||
func isDir(path string) bool { |
||||
if stat, err := os.Stat(path); err == nil { |
||||
return stat.IsDir() |
||||
} |
||||
return false |
||||
} |
||||
|
||||
// Concurrent logging should not corrupt the underling buffer.
|
||||
// Use go test -race to detect the race in this test.
|
||||
func (s *HelpersS) TestConcurrentLogging(c *check.C) { |
||||
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(runtime.NumCPU())) |
||||
var start, stop sync.WaitGroup |
||||
start.Add(1) |
||||
for i, n := 0, runtime.NumCPU()*2; i < n; i++ { |
||||
stop.Add(1) |
||||
go func(i int) { |
||||
start.Wait() |
||||
for j := 0; j < 30; j++ { |
||||
c.Logf("Worker %d: line %d", i, j) |
||||
} |
||||
stop.Done() |
||||
}(i) |
||||
} |
||||
start.Done() |
||||
stop.Wait() |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Test the TestName function
|
||||
|
||||
type TestNameHelper struct { |
||||
name1 string |
||||
name2 string |
||||
name3 string |
||||
name4 string |
||||
name5 string |
||||
} |
||||
|
||||
func (s *TestNameHelper) SetUpSuite(c *check.C) { s.name1 = c.TestName() } |
||||
func (s *TestNameHelper) SetUpTest(c *check.C) { s.name2 = c.TestName() } |
||||
func (s *TestNameHelper) Test(c *check.C) { s.name3 = c.TestName() } |
||||
func (s *TestNameHelper) TearDownTest(c *check.C) { s.name4 = c.TestName() } |
||||
func (s *TestNameHelper) TearDownSuite(c *check.C) { s.name5 = c.TestName() } |
||||
|
||||
func (s *HelpersS) TestTestName(c *check.C) { |
||||
helper := TestNameHelper{} |
||||
output := String{} |
||||
check.Run(&helper, &check.RunConf{Output: &output}) |
||||
c.Check(helper.name1, check.Equals, "") |
||||
c.Check(helper.name2, check.Equals, "TestNameHelper.Test") |
||||
c.Check(helper.name3, check.Equals, "TestNameHelper.Test") |
||||
c.Check(helper.name4, check.Equals, "TestNameHelper.Test") |
||||
c.Check(helper.name5, check.Equals, "") |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// A couple of helper functions to test helper functions. :-)
|
||||
|
||||
func testHelperSuccess(c *check.C, name string, expectedResult interface{}, closure func() interface{}) { |
||||
var result interface{} |
||||
defer (func() { |
||||
if err := recover(); err != nil { |
||||
panic(err) |
||||
} |
||||
checkState(c, result, |
||||
&expectedState{ |
||||
name: name, |
||||
result: expectedResult, |
||||
failed: false, |
||||
log: "", |
||||
}) |
||||
})() |
||||
result = closure() |
||||
} |
||||
|
||||
func testHelperFailure(c *check.C, name string, expectedResult interface{}, shouldStop bool, log string, closure func() interface{}) { |
||||
var result interface{} |
||||
defer (func() { |
||||
if err := recover(); err != nil { |
||||
panic(err) |
||||
} |
||||
checkState(c, result, |
||||
&expectedState{ |
||||
name: name, |
||||
result: expectedResult, |
||||
failed: true, |
||||
log: log, |
||||
}) |
||||
})() |
||||
result = closure() |
||||
if shouldStop { |
||||
c.Logf("%s didn't stop when it should", name) |
||||
} |
||||
} |
@ -0,0 +1,168 @@ |
||||
package check |
||||
|
||||
import ( |
||||
"bytes" |
||||
"go/ast" |
||||
"go/parser" |
||||
"go/printer" |
||||
"go/token" |
||||
"os" |
||||
) |
||||
|
||||
func indent(s, with string) (r string) { |
||||
eol := true |
||||
for i := 0; i != len(s); i++ { |
||||
c := s[i] |
||||
switch { |
||||
case eol && c == '\n' || c == '\r': |
||||
case c == '\n' || c == '\r': |
||||
eol = true |
||||
case eol: |
||||
eol = false |
||||
s = s[:i] + with + s[i:] |
||||
i += len(with) |
||||
} |
||||
} |
||||
return s |
||||
} |
||||
|
||||
func printLine(filename string, line int) (string, error) { |
||||
fset := token.NewFileSet() |
||||
file, err := os.Open(filename) |
||||
if err != nil { |
||||
return "", err |
||||
} |
||||
fnode, err := parser.ParseFile(fset, filename, file, parser.ParseComments) |
||||
if err != nil { |
||||
return "", err |
||||
} |
||||
config := &printer.Config{Mode: printer.UseSpaces, Tabwidth: 4} |
||||
lp := &linePrinter{fset: fset, fnode: fnode, line: line, config: config} |
||||
ast.Walk(lp, fnode) |
||||
result := lp.output.Bytes() |
||||
// Comments leave \n at the end.
|
||||
n := len(result) |
||||
for n > 0 && result[n-1] == '\n' { |
||||
n-- |
||||
} |
||||
return string(result[:n]), nil |
||||
} |
||||
|
||||
type linePrinter struct { |
||||
config *printer.Config |
||||
fset *token.FileSet |
||||
fnode *ast.File |
||||
line int |
||||
output bytes.Buffer |
||||
stmt ast.Stmt |
||||
} |
||||
|
||||
func (lp *linePrinter) emit() bool { |
||||
if lp.stmt != nil { |
||||
lp.trim(lp.stmt) |
||||
lp.printWithComments(lp.stmt) |
||||
lp.stmt = nil |
||||
return true |
||||
} |
||||
return false |
||||
} |
||||
|
||||
func (lp *linePrinter) printWithComments(n ast.Node) { |
||||
nfirst := lp.fset.Position(n.Pos()).Line |
||||
nlast := lp.fset.Position(n.End()).Line |
||||
for _, g := range lp.fnode.Comments { |
||||
cfirst := lp.fset.Position(g.Pos()).Line |
||||
clast := lp.fset.Position(g.End()).Line |
||||
if clast == nfirst-1 && lp.fset.Position(n.Pos()).Column == lp.fset.Position(g.Pos()).Column { |
||||
for _, c := range g.List { |
||||
lp.output.WriteString(c.Text) |
||||
lp.output.WriteByte('\n') |
||||
} |
||||
} |
||||
if cfirst >= nfirst && cfirst <= nlast && n.End() <= g.List[0].Slash { |
||||
// The printer will not include the comment if it starts past
|
||||
// the node itself. Trick it into printing by overlapping the
|
||||
// slash with the end of the statement.
|
||||
g.List[0].Slash = n.End() - 1 |
||||
} |
||||
} |
||||
node := &printer.CommentedNode{n, lp.fnode.Comments} |
||||
lp.config.Fprint(&lp.output, lp.fset, node) |
||||
} |
||||
|
||||
func (lp *linePrinter) Visit(n ast.Node) (w ast.Visitor) { |
||||
if n == nil { |
||||
if lp.output.Len() == 0 { |
||||
lp.emit() |
||||
} |
||||
return nil |
||||
} |
||||
first := lp.fset.Position(n.Pos()).Line |
||||
last := lp.fset.Position(n.End()).Line |
||||
if first <= lp.line && last >= lp.line { |
||||
// Print the innermost statement containing the line.
|
||||
if stmt, ok := n.(ast.Stmt); ok { |
||||
if _, ok := n.(*ast.BlockStmt); !ok { |
||||
lp.stmt = stmt |
||||
} |
||||
} |
||||
if first == lp.line && lp.emit() { |
||||
return nil |
||||
} |
||||
return lp |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
func (lp *linePrinter) trim(n ast.Node) bool { |
||||
stmt, ok := n.(ast.Stmt) |
||||
if !ok { |
||||
return true |
||||
} |
||||
line := lp.fset.Position(n.Pos()).Line |
||||
if line != lp.line { |
||||
return false |
||||
} |
||||
switch stmt := stmt.(type) { |
||||
case *ast.IfStmt: |
||||
stmt.Body = lp.trimBlock(stmt.Body) |
||||
case *ast.SwitchStmt: |
||||
stmt.Body = lp.trimBlock(stmt.Body) |
||||
case *ast.TypeSwitchStmt: |
||||
stmt.Body = lp.trimBlock(stmt.Body) |
||||
case *ast.CaseClause: |
||||
stmt.Body = lp.trimList(stmt.Body) |
||||
case *ast.CommClause: |
||||
stmt.Body = lp.trimList(stmt.Body) |
||||
case *ast.BlockStmt: |
||||
stmt.List = lp.trimList(stmt.List) |
||||
} |
||||
return true |
||||
} |
||||
|
||||
func (lp *linePrinter) trimBlock(stmt *ast.BlockStmt) *ast.BlockStmt { |
||||
if !lp.trim(stmt) { |
||||
return lp.emptyBlock(stmt) |
||||
} |
||||
stmt.Rbrace = stmt.Lbrace |
||||
return stmt |
||||
} |
||||
|
||||
func (lp *linePrinter) trimList(stmts []ast.Stmt) []ast.Stmt { |
||||
for i := 0; i != len(stmts); i++ { |
||||
if !lp.trim(stmts[i]) { |
||||
stmts[i] = lp.emptyStmt(stmts[i]) |
||||
break |
||||
} |
||||
} |
||||
return stmts |
||||
} |
||||
|
||||
func (lp *linePrinter) emptyStmt(n ast.Node) *ast.ExprStmt { |
||||
return &ast.ExprStmt{&ast.Ellipsis{n.Pos(), nil}} |
||||
} |
||||
|
||||
func (lp *linePrinter) emptyBlock(n ast.Node) *ast.BlockStmt { |
||||
p := n.Pos() |
||||
return &ast.BlockStmt{p, []ast.Stmt{lp.emptyStmt(n)}, p} |
||||
} |
@ -0,0 +1,104 @@ |
||||
package check_test |
||||
|
||||
import ( |
||||
. "gopkg.in/check.v1" |
||||
) |
||||
|
||||
var _ = Suite(&PrinterS{}) |
||||
|
||||
type PrinterS struct{} |
||||
|
||||
func (s *PrinterS) TestCountSuite(c *C) { |
||||
suitesRun += 1 |
||||
} |
||||
|
||||
var printTestFuncLine int |
||||
|
||||
func init() { |
||||
printTestFuncLine = getMyLine() + 3 |
||||
} |
||||
|
||||
func printTestFunc() { |
||||
println(1) // Comment1
|
||||
if 2 == 2 { // Comment2
|
||||
println(3) // Comment3
|
||||
} |
||||
switch 5 { |
||||
case 6: println(6) // Comment6
|
||||
println(7) |
||||
} |
||||
switch interface{}(9).(type) {// Comment9
|
||||
case int: println(10) |
||||
println(11) |
||||
} |
||||
select { |
||||
case <-(chan bool)(nil): println(14) |
||||
println(15) |
||||
default: println(16) |
||||
println(17) |
||||
} |
||||
println(19, |
||||
20) |
||||
_ = func() { println(21) |
||||
println(22) |
||||
} |
||||
println(24, func() { |
||||
println(25) |
||||
}) |
||||
// Leading comment
|
||||
// with multiple lines.
|
||||
println(29) // Comment29
|
||||
} |
||||
|
||||
var printLineTests = []struct { |
||||
line int |
||||
output string |
||||
}{ |
||||
{1, "println(1) // Comment1"}, |
||||
{2, "if 2 == 2 { // Comment2\n ...\n}"}, |
||||
{3, "println(3) // Comment3"}, |
||||
{5, "switch 5 {\n...\n}"}, |
||||
{6, "case 6:\n println(6) // Comment6\n ..."}, |
||||
{7, "println(7)"}, |
||||
{9, "switch interface{}(9).(type) { // Comment9\n...\n}"}, |
||||
{10, "case int:\n println(10)\n ..."}, |
||||
{14, "case <-(chan bool)(nil):\n println(14)\n ..."}, |
||||
{15, "println(15)"}, |
||||
{16, "default:\n println(16)\n ..."}, |
||||
{17, "println(17)"}, |
||||
{19, "println(19,\n 20)"}, |
||||
{20, "println(19,\n 20)"}, |
||||
{21, "_ = func() {\n println(21)\n println(22)\n}"}, |
||||
{22, "println(22)"}, |
||||
{24, "println(24, func() {\n println(25)\n})"}, |
||||
{25, "println(25)"}, |
||||
{26, "println(24, func() {\n println(25)\n})"}, |
||||
{29, "// Leading comment\n// with multiple lines.\nprintln(29) // Comment29"}, |
||||
} |
||||
|
||||
func (s *PrinterS) TestPrintLine(c *C) { |
||||
for _, test := range printLineTests { |
||||
output, err := PrintLine("printer_test.go", printTestFuncLine+test.line) |
||||
c.Assert(err, IsNil) |
||||
c.Assert(output, Equals, test.output) |
||||
} |
||||
} |
||||
|
||||
var indentTests = []struct { |
||||
in, out string |
||||
}{ |
||||
{"", ""}, |
||||
{"\n", "\n"}, |
||||
{"a", ">>>a"}, |
||||
{"a\n", ">>>a\n"}, |
||||
{"a\nb", ">>>a\n>>>b"}, |
||||
{" ", ">>> "}, |
||||
} |
||||
|
||||
func (s *PrinterS) TestIndent(c *C) { |
||||
for _, test := range indentTests { |
||||
out := Indent(test.in, ">>>") |
||||
c.Assert(out, Equals, test.out) |
||||
} |
||||
|
||||
} |
@ -0,0 +1,175 @@ |
||||
package check |
||||
|
||||
import ( |
||||
"bufio" |
||||
"flag" |
||||
"fmt" |
||||
"os" |
||||
"testing" |
||||
"time" |
||||
) |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Test suite registry.
|
||||
|
||||
var allSuites []interface{} |
||||
|
||||
// Suite registers the given value as a test suite to be run. Any methods
|
||||
// starting with the Test prefix in the given value will be considered as
|
||||
// a test method.
|
||||
func Suite(suite interface{}) interface{} { |
||||
allSuites = append(allSuites, suite) |
||||
return suite |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Public running interface.
|
||||
|
||||
var ( |
||||
oldFilterFlag = flag.String("gocheck.f", "", "Regular expression selecting which tests and/or suites to run") |
||||
oldVerboseFlag = flag.Bool("gocheck.v", false, "Verbose mode") |
||||
oldStreamFlag = flag.Bool("gocheck.vv", false, "Super verbose mode (disables output caching)") |
||||
oldBenchFlag = flag.Bool("gocheck.b", false, "Run benchmarks") |
||||
oldBenchTime = flag.Duration("gocheck.btime", 1*time.Second, "approximate run time for each benchmark") |
||||
oldListFlag = flag.Bool("gocheck.list", false, "List the names of all tests that will be run") |
||||
oldWorkFlag = flag.Bool("gocheck.work", false, "Display and do not remove the test working directory") |
||||
|
||||
newFilterFlag = flag.String("check.f", "", "Regular expression selecting which tests and/or suites to run") |
||||
newVerboseFlag = flag.Bool("check.v", false, "Verbose mode") |
||||
newStreamFlag = flag.Bool("check.vv", false, "Super verbose mode (disables output caching)") |
||||
newBenchFlag = flag.Bool("check.b", false, "Run benchmarks") |
||||
newBenchTime = flag.Duration("check.btime", 1*time.Second, "approximate run time for each benchmark") |
||||
newBenchMem = flag.Bool("check.bmem", false, "Report memory benchmarks") |
||||
newListFlag = flag.Bool("check.list", false, "List the names of all tests that will be run") |
||||
newWorkFlag = flag.Bool("check.work", false, "Display and do not remove the test working directory") |
||||
) |
||||
|
||||
// TestingT runs all test suites registered with the Suite function,
|
||||
// printing results to stdout, and reporting any failures back to
|
||||
// the "testing" package.
|
||||
func TestingT(testingT *testing.T) { |
||||
benchTime := *newBenchTime |
||||
if benchTime == 1*time.Second { |
||||
benchTime = *oldBenchTime |
||||
} |
||||
conf := &RunConf{ |
||||
Filter: *oldFilterFlag + *newFilterFlag, |
||||
Verbose: *oldVerboseFlag || *newVerboseFlag, |
||||
Stream: *oldStreamFlag || *newStreamFlag, |
||||
Benchmark: *oldBenchFlag || *newBenchFlag, |
||||
BenchmarkTime: benchTime, |
||||
BenchmarkMem: *newBenchMem, |
||||
KeepWorkDir: *oldWorkFlag || *newWorkFlag, |
||||
} |
||||
if *oldListFlag || *newListFlag { |
||||
w := bufio.NewWriter(os.Stdout) |
||||
for _, name := range ListAll(conf) { |
||||
fmt.Fprintln(w, name) |
||||
} |
||||
w.Flush() |
||||
return |
||||
} |
||||
result := RunAll(conf) |
||||
println(result.String()) |
||||
if !result.Passed() { |
||||
testingT.Fail() |
||||
} |
||||
} |
||||
|
||||
// RunAll runs all test suites registered with the Suite function, using the
|
||||
// provided run configuration.
|
||||
func RunAll(runConf *RunConf) *Result { |
||||
result := Result{} |
||||
for _, suite := range allSuites { |
||||
result.Add(Run(suite, runConf)) |
||||
} |
||||
return &result |
||||
} |
||||
|
||||
// Run runs the provided test suite using the provided run configuration.
|
||||
func Run(suite interface{}, runConf *RunConf) *Result { |
||||
runner := newSuiteRunner(suite, runConf) |
||||
return runner.run() |
||||
} |
||||
|
||||
// ListAll returns the names of all the test functions registered with the
|
||||
// Suite function that will be run with the provided run configuration.
|
||||
func ListAll(runConf *RunConf) []string { |
||||
var names []string |
||||
for _, suite := range allSuites { |
||||
names = append(names, List(suite, runConf)...) |
||||
} |
||||
return names |
||||
} |
||||
|
||||
// List returns the names of the test functions in the given
|
||||
// suite that will be run with the provided run configuration.
|
||||
func List(suite interface{}, runConf *RunConf) []string { |
||||
var names []string |
||||
runner := newSuiteRunner(suite, runConf) |
||||
for _, t := range runner.tests { |
||||
names = append(names, t.String()) |
||||
} |
||||
return names |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Result methods.
|
||||
|
||||
func (r *Result) Add(other *Result) { |
||||
r.Succeeded += other.Succeeded |
||||
r.Skipped += other.Skipped |
||||
r.Failed += other.Failed |
||||
r.Panicked += other.Panicked |
||||
r.FixturePanicked += other.FixturePanicked |
||||
r.ExpectedFailures += other.ExpectedFailures |
||||
r.Missed += other.Missed |
||||
if r.WorkDir != "" && other.WorkDir != "" { |
||||
r.WorkDir += ":" + other.WorkDir |
||||
} else if other.WorkDir != "" { |
||||
r.WorkDir = other.WorkDir |
||||
} |
||||
} |
||||
|
||||
func (r *Result) Passed() bool { |
||||
return (r.Failed == 0 && r.Panicked == 0 && |
||||
r.FixturePanicked == 0 && r.Missed == 0 && |
||||
r.RunError == nil) |
||||
} |
||||
|
||||
func (r *Result) String() string { |
||||
if r.RunError != nil { |
||||
return "ERROR: " + r.RunError.Error() |
||||
} |
||||
|
||||
var value string |
||||
if r.Failed == 0 && r.Panicked == 0 && r.FixturePanicked == 0 && |
||||
r.Missed == 0 { |
||||
value = "OK: " |
||||
} else { |
||||
value = "OOPS: " |
||||
} |
||||
value += fmt.Sprintf("%d passed", r.Succeeded) |
||||
if r.Skipped != 0 { |
||||
value += fmt.Sprintf(", %d skipped", r.Skipped) |
||||
} |
||||
if r.ExpectedFailures != 0 { |
||||
value += fmt.Sprintf(", %d expected failures", r.ExpectedFailures) |
||||
} |
||||
if r.Failed != 0 { |
||||
value += fmt.Sprintf(", %d FAILED", r.Failed) |
||||
} |
||||
if r.Panicked != 0 { |
||||
value += fmt.Sprintf(", %d PANICKED", r.Panicked) |
||||
} |
||||
if r.FixturePanicked != 0 { |
||||
value += fmt.Sprintf(", %d FIXTURE-PANICKED", r.FixturePanicked) |
||||
} |
||||
if r.Missed != 0 { |
||||
value += fmt.Sprintf(", %d MISSED", r.Missed) |
||||
} |
||||
if r.WorkDir != "" { |
||||
value += "\nWORK=" + r.WorkDir |
||||
} |
||||
return value |
||||
} |
@ -0,0 +1,419 @@ |
||||
// These tests verify the test running logic.
|
||||
|
||||
package check_test |
||||
|
||||
import ( |
||||
"errors" |
||||
. "gopkg.in/check.v1" |
||||
"os" |
||||
"sync" |
||||
) |
||||
|
||||
var runnerS = Suite(&RunS{}) |
||||
|
||||
type RunS struct{} |
||||
|
||||
func (s *RunS) TestCountSuite(c *C) { |
||||
suitesRun += 1 |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Tests ensuring result counting works properly.
|
||||
|
||||
func (s *RunS) TestSuccess(c *C) { |
||||
output := String{} |
||||
result := Run(&SuccessHelper{}, &RunConf{Output: &output}) |
||||
c.Check(result.Succeeded, Equals, 1) |
||||
c.Check(result.Failed, Equals, 0) |
||||
c.Check(result.Skipped, Equals, 0) |
||||
c.Check(result.Panicked, Equals, 0) |
||||
c.Check(result.FixturePanicked, Equals, 0) |
||||
c.Check(result.Missed, Equals, 0) |
||||
c.Check(result.RunError, IsNil) |
||||
} |
||||
|
||||
func (s *RunS) TestFailure(c *C) { |
||||
output := String{} |
||||
result := Run(&FailHelper{}, &RunConf{Output: &output}) |
||||
c.Check(result.Succeeded, Equals, 0) |
||||
c.Check(result.Failed, Equals, 1) |
||||
c.Check(result.Skipped, Equals, 0) |
||||
c.Check(result.Panicked, Equals, 0) |
||||
c.Check(result.FixturePanicked, Equals, 0) |
||||
c.Check(result.Missed, Equals, 0) |
||||
c.Check(result.RunError, IsNil) |
||||
} |
||||
|
||||
func (s *RunS) TestFixture(c *C) { |
||||
output := String{} |
||||
result := Run(&FixtureHelper{}, &RunConf{Output: &output}) |
||||
c.Check(result.Succeeded, Equals, 2) |
||||
c.Check(result.Failed, Equals, 0) |
||||
c.Check(result.Skipped, Equals, 0) |
||||
c.Check(result.Panicked, Equals, 0) |
||||
c.Check(result.FixturePanicked, Equals, 0) |
||||
c.Check(result.Missed, Equals, 0) |
||||
c.Check(result.RunError, IsNil) |
||||
} |
||||
|
||||
func (s *RunS) TestPanicOnTest(c *C) { |
||||
output := String{} |
||||
helper := &FixtureHelper{panicOn: "Test1"} |
||||
result := Run(helper, &RunConf{Output: &output}) |
||||
c.Check(result.Succeeded, Equals, 1) |
||||
c.Check(result.Failed, Equals, 0) |
||||
c.Check(result.Skipped, Equals, 0) |
||||
c.Check(result.Panicked, Equals, 1) |
||||
c.Check(result.FixturePanicked, Equals, 0) |
||||
c.Check(result.Missed, Equals, 0) |
||||
c.Check(result.RunError, IsNil) |
||||
} |
||||
|
||||
func (s *RunS) TestPanicOnSetUpTest(c *C) { |
||||
output := String{} |
||||
helper := &FixtureHelper{panicOn: "SetUpTest"} |
||||
result := Run(helper, &RunConf{Output: &output}) |
||||
c.Check(result.Succeeded, Equals, 0) |
||||
c.Check(result.Failed, Equals, 0) |
||||
c.Check(result.Skipped, Equals, 0) |
||||
c.Check(result.Panicked, Equals, 0) |
||||
c.Check(result.FixturePanicked, Equals, 1) |
||||
c.Check(result.Missed, Equals, 2) |
||||
c.Check(result.RunError, IsNil) |
||||
} |
||||
|
||||
func (s *RunS) TestPanicOnSetUpSuite(c *C) { |
||||
output := String{} |
||||
helper := &FixtureHelper{panicOn: "SetUpSuite"} |
||||
result := Run(helper, &RunConf{Output: &output}) |
||||
c.Check(result.Succeeded, Equals, 0) |
||||
c.Check(result.Failed, Equals, 0) |
||||
c.Check(result.Skipped, Equals, 0) |
||||
c.Check(result.Panicked, Equals, 0) |
||||
c.Check(result.FixturePanicked, Equals, 1) |
||||
c.Check(result.Missed, Equals, 2) |
||||
c.Check(result.RunError, IsNil) |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Check result aggregation.
|
||||
|
||||
func (s *RunS) TestAdd(c *C) { |
||||
result := &Result{ |
||||
Succeeded: 1, |
||||
Skipped: 2, |
||||
Failed: 3, |
||||
Panicked: 4, |
||||
FixturePanicked: 5, |
||||
Missed: 6, |
||||
ExpectedFailures: 7, |
||||
} |
||||
result.Add(&Result{ |
||||
Succeeded: 10, |
||||
Skipped: 20, |
||||
Failed: 30, |
||||
Panicked: 40, |
||||
FixturePanicked: 50, |
||||
Missed: 60, |
||||
ExpectedFailures: 70, |
||||
}) |
||||
c.Check(result.Succeeded, Equals, 11) |
||||
c.Check(result.Skipped, Equals, 22) |
||||
c.Check(result.Failed, Equals, 33) |
||||
c.Check(result.Panicked, Equals, 44) |
||||
c.Check(result.FixturePanicked, Equals, 55) |
||||
c.Check(result.Missed, Equals, 66) |
||||
c.Check(result.ExpectedFailures, Equals, 77) |
||||
c.Check(result.RunError, IsNil) |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Check the Passed() method.
|
||||
|
||||
func (s *RunS) TestPassed(c *C) { |
||||
c.Assert((&Result{}).Passed(), Equals, true) |
||||
c.Assert((&Result{Succeeded: 1}).Passed(), Equals, true) |
||||
c.Assert((&Result{Skipped: 1}).Passed(), Equals, true) |
||||
c.Assert((&Result{Failed: 1}).Passed(), Equals, false) |
||||
c.Assert((&Result{Panicked: 1}).Passed(), Equals, false) |
||||
c.Assert((&Result{FixturePanicked: 1}).Passed(), Equals, false) |
||||
c.Assert((&Result{Missed: 1}).Passed(), Equals, false) |
||||
c.Assert((&Result{RunError: errors.New("!")}).Passed(), Equals, false) |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Check that result printing is working correctly.
|
||||
|
||||
func (s *RunS) TestPrintSuccess(c *C) { |
||||
result := &Result{Succeeded: 5} |
||||
c.Check(result.String(), Equals, "OK: 5 passed") |
||||
} |
||||
|
||||
func (s *RunS) TestPrintFailure(c *C) { |
||||
result := &Result{Failed: 5} |
||||
c.Check(result.String(), Equals, "OOPS: 0 passed, 5 FAILED") |
||||
} |
||||
|
||||
func (s *RunS) TestPrintSkipped(c *C) { |
||||
result := &Result{Skipped: 5} |
||||
c.Check(result.String(), Equals, "OK: 0 passed, 5 skipped") |
||||
} |
||||
|
||||
func (s *RunS) TestPrintExpectedFailures(c *C) { |
||||
result := &Result{ExpectedFailures: 5} |
||||
c.Check(result.String(), Equals, "OK: 0 passed, 5 expected failures") |
||||
} |
||||
|
||||
func (s *RunS) TestPrintPanicked(c *C) { |
||||
result := &Result{Panicked: 5} |
||||
c.Check(result.String(), Equals, "OOPS: 0 passed, 5 PANICKED") |
||||
} |
||||
|
||||
func (s *RunS) TestPrintFixturePanicked(c *C) { |
||||
result := &Result{FixturePanicked: 5} |
||||
c.Check(result.String(), Equals, "OOPS: 0 passed, 5 FIXTURE-PANICKED") |
||||
} |
||||
|
||||
func (s *RunS) TestPrintMissed(c *C) { |
||||
result := &Result{Missed: 5} |
||||
c.Check(result.String(), Equals, "OOPS: 0 passed, 5 MISSED") |
||||
} |
||||
|
||||
func (s *RunS) TestPrintAll(c *C) { |
||||
result := &Result{Succeeded: 1, Skipped: 2, ExpectedFailures: 3, |
||||
Panicked: 4, FixturePanicked: 5, Missed: 6} |
||||
c.Check(result.String(), Equals, |
||||
"OOPS: 1 passed, 2 skipped, 3 expected failures, 4 PANICKED, "+ |
||||
"5 FIXTURE-PANICKED, 6 MISSED") |
||||
} |
||||
|
||||
func (s *RunS) TestPrintRunError(c *C) { |
||||
result := &Result{Succeeded: 1, Failed: 1, |
||||
RunError: errors.New("Kaboom!")} |
||||
c.Check(result.String(), Equals, "ERROR: Kaboom!") |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Verify that the method pattern flag works correctly.
|
||||
|
||||
func (s *RunS) TestFilterTestName(c *C) { |
||||
helper := FixtureHelper{} |
||||
output := String{} |
||||
runConf := RunConf{Output: &output, Filter: "Test[91]"} |
||||
Run(&helper, &runConf) |
||||
c.Check(helper.calls[0], Equals, "SetUpSuite") |
||||
c.Check(helper.calls[1], Equals, "SetUpTest") |
||||
c.Check(helper.calls[2], Equals, "Test1") |
||||
c.Check(helper.calls[3], Equals, "TearDownTest") |
||||
c.Check(helper.calls[4], Equals, "TearDownSuite") |
||||
c.Check(len(helper.calls), Equals, 5) |
||||
} |
||||
|
||||
func (s *RunS) TestFilterTestNameWithAll(c *C) { |
||||
helper := FixtureHelper{} |
||||
output := String{} |
||||
runConf := RunConf{Output: &output, Filter: ".*"} |
||||
Run(&helper, &runConf) |
||||
c.Check(helper.calls[0], Equals, "SetUpSuite") |
||||
c.Check(helper.calls[1], Equals, "SetUpTest") |
||||
c.Check(helper.calls[2], Equals, "Test1") |
||||
c.Check(helper.calls[3], Equals, "TearDownTest") |
||||
c.Check(helper.calls[4], Equals, "SetUpTest") |
||||
c.Check(helper.calls[5], Equals, "Test2") |
||||
c.Check(helper.calls[6], Equals, "TearDownTest") |
||||
c.Check(helper.calls[7], Equals, "TearDownSuite") |
||||
c.Check(len(helper.calls), Equals, 8) |
||||
} |
||||
|
||||
func (s *RunS) TestFilterSuiteName(c *C) { |
||||
helper := FixtureHelper{} |
||||
output := String{} |
||||
runConf := RunConf{Output: &output, Filter: "FixtureHelper"} |
||||
Run(&helper, &runConf) |
||||
c.Check(helper.calls[0], Equals, "SetUpSuite") |
||||
c.Check(helper.calls[1], Equals, "SetUpTest") |
||||
c.Check(helper.calls[2], Equals, "Test1") |
||||
c.Check(helper.calls[3], Equals, "TearDownTest") |
||||
c.Check(helper.calls[4], Equals, "SetUpTest") |
||||
c.Check(helper.calls[5], Equals, "Test2") |
||||
c.Check(helper.calls[6], Equals, "TearDownTest") |
||||
c.Check(helper.calls[7], Equals, "TearDownSuite") |
||||
c.Check(len(helper.calls), Equals, 8) |
||||
} |
||||
|
||||
func (s *RunS) TestFilterSuiteNameAndTestName(c *C) { |
||||
helper := FixtureHelper{} |
||||
output := String{} |
||||
runConf := RunConf{Output: &output, Filter: "FixtureHelper\\.Test2"} |
||||
Run(&helper, &runConf) |
||||
c.Check(helper.calls[0], Equals, "SetUpSuite") |
||||
c.Check(helper.calls[1], Equals, "SetUpTest") |
||||
c.Check(helper.calls[2], Equals, "Test2") |
||||
c.Check(helper.calls[3], Equals, "TearDownTest") |
||||
c.Check(helper.calls[4], Equals, "TearDownSuite") |
||||
c.Check(len(helper.calls), Equals, 5) |
||||
} |
||||
|
||||
func (s *RunS) TestFilterAllOut(c *C) { |
||||
helper := FixtureHelper{} |
||||
output := String{} |
||||
runConf := RunConf{Output: &output, Filter: "NotFound"} |
||||
Run(&helper, &runConf) |
||||
c.Check(len(helper.calls), Equals, 0) |
||||
} |
||||
|
||||
func (s *RunS) TestRequirePartialMatch(c *C) { |
||||
helper := FixtureHelper{} |
||||
output := String{} |
||||
runConf := RunConf{Output: &output, Filter: "est"} |
||||
Run(&helper, &runConf) |
||||
c.Check(len(helper.calls), Equals, 8) |
||||
} |
||||
|
||||
func (s *RunS) TestFilterError(c *C) { |
||||
helper := FixtureHelper{} |
||||
output := String{} |
||||
runConf := RunConf{Output: &output, Filter: "]["} |
||||
result := Run(&helper, &runConf) |
||||
c.Check(result.String(), Equals, |
||||
"ERROR: Bad filter expression: error parsing regexp: missing closing ]: `[`") |
||||
c.Check(len(helper.calls), Equals, 0) |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Verify that List works correctly.
|
||||
|
||||
func (s *RunS) TestListFiltered(c *C) { |
||||
names := List(&FixtureHelper{}, &RunConf{Filter: "1"}) |
||||
c.Assert(names, DeepEquals, []string{ |
||||
"FixtureHelper.Test1", |
||||
}) |
||||
} |
||||
|
||||
func (s *RunS) TestList(c *C) { |
||||
names := List(&FixtureHelper{}, &RunConf{}) |
||||
c.Assert(names, DeepEquals, []string{ |
||||
"FixtureHelper.Test1", |
||||
"FixtureHelper.Test2", |
||||
}) |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Verify that verbose mode prints tests which pass as well.
|
||||
|
||||
func (s *RunS) TestVerboseMode(c *C) { |
||||
helper := FixtureHelper{} |
||||
output := String{} |
||||
runConf := RunConf{Output: &output, Verbose: true} |
||||
Run(&helper, &runConf) |
||||
|
||||
expected := "PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Test1\t *[.0-9]+s\n" + |
||||
"PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Test2\t *[.0-9]+s\n" |
||||
|
||||
c.Assert(output.value, Matches, expected) |
||||
} |
||||
|
||||
func (s *RunS) TestVerboseModeWithFailBeforePass(c *C) { |
||||
helper := FixtureHelper{panicOn: "Test1"} |
||||
output := String{} |
||||
runConf := RunConf{Output: &output, Verbose: true} |
||||
Run(&helper, &runConf) |
||||
|
||||
expected := "(?s).*PANIC.*\n-+\n" + // Should have an extra line.
|
||||
"PASS: check_test\\.go:[0-9]+: FixtureHelper\\.Test2\t *[.0-9]+s\n" |
||||
|
||||
c.Assert(output.value, Matches, expected) |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Verify the stream output mode. In this mode there's no output caching.
|
||||
|
||||
type StreamHelper struct { |
||||
l2 sync.Mutex |
||||
l3 sync.Mutex |
||||
} |
||||
|
||||
func (s *StreamHelper) SetUpSuite(c *C) { |
||||
c.Log("0") |
||||
} |
||||
|
||||
func (s *StreamHelper) Test1(c *C) { |
||||
c.Log("1") |
||||
s.l2.Lock() |
||||
s.l3.Lock() |
||||
go func() { |
||||
s.l2.Lock() // Wait for "2".
|
||||
c.Log("3") |
||||
s.l3.Unlock() |
||||
}() |
||||
} |
||||
|
||||
func (s *StreamHelper) Test2(c *C) { |
||||
c.Log("2") |
||||
s.l2.Unlock() |
||||
s.l3.Lock() // Wait for "3".
|
||||
c.Fail() |
||||
c.Log("4") |
||||
} |
||||
|
||||
func (s *RunS) TestStreamMode(c *C) { |
||||
helper := &StreamHelper{} |
||||
output := String{} |
||||
runConf := RunConf{Output: &output, Stream: true} |
||||
Run(helper, &runConf) |
||||
|
||||
expected := "START: run_test\\.go:[0-9]+: StreamHelper\\.SetUpSuite\n0\n" + |
||||
"PASS: run_test\\.go:[0-9]+: StreamHelper\\.SetUpSuite\t *[.0-9]+s\n\n" + |
||||
"START: run_test\\.go:[0-9]+: StreamHelper\\.Test1\n1\n" + |
||||
"PASS: run_test\\.go:[0-9]+: StreamHelper\\.Test1\t *[.0-9]+s\n\n" + |
||||
"START: run_test\\.go:[0-9]+: StreamHelper\\.Test2\n2\n3\n4\n" + |
||||
"FAIL: run_test\\.go:[0-9]+: StreamHelper\\.Test2\n\n" |
||||
|
||||
c.Assert(output.value, Matches, expected) |
||||
} |
||||
|
||||
type StreamMissHelper struct{} |
||||
|
||||
func (s *StreamMissHelper) SetUpSuite(c *C) { |
||||
c.Log("0") |
||||
c.Fail() |
||||
} |
||||
|
||||
func (s *StreamMissHelper) Test1(c *C) { |
||||
c.Log("1") |
||||
} |
||||
|
||||
func (s *RunS) TestStreamModeWithMiss(c *C) { |
||||
helper := &StreamMissHelper{} |
||||
output := String{} |
||||
runConf := RunConf{Output: &output, Stream: true} |
||||
Run(helper, &runConf) |
||||
|
||||
expected := "START: run_test\\.go:[0-9]+: StreamMissHelper\\.SetUpSuite\n0\n" + |
||||
"FAIL: run_test\\.go:[0-9]+: StreamMissHelper\\.SetUpSuite\n\n" + |
||||
"START: run_test\\.go:[0-9]+: StreamMissHelper\\.Test1\n" + |
||||
"MISS: run_test\\.go:[0-9]+: StreamMissHelper\\.Test1\n\n" |
||||
|
||||
c.Assert(output.value, Matches, expected) |
||||
} |
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Verify that that the keep work dir request indeed does so.
|
||||
|
||||
type WorkDirSuite struct {} |
||||
|
||||
func (s *WorkDirSuite) Test(c *C) { |
||||
c.MkDir() |
||||
} |
||||
|
||||
func (s *RunS) TestKeepWorkDir(c *C) { |
||||
output := String{} |
||||
runConf := RunConf{Output: &output, Verbose: true, KeepWorkDir: true} |
||||
result := Run(&WorkDirSuite{}, &runConf) |
||||
|
||||
c.Assert(result.String(), Matches, ".*\nWORK=" + result.WorkDir) |
||||
|
||||
stat, err := os.Stat(result.WorkDir) |
||||
c.Assert(err, IsNil) |
||||
c.Assert(stat.IsDir(), Equals, true) |
||||
} |
Loading…
Reference in new issue