tests: Remove dependency on check.v1 (#5034)
This PR addresses a long standing dependency on `gopkg.in/check.v1` project used for our tests. All tests are re-written to use the go default testing framework instead. There was no reason for us to use an external package where Go tools are sufficient for this.master
parent
d28b3d8801
commit
4deefa3695
@ -1,26 +0,0 @@ |
||||
/* |
||||
* Minio Cloud Storage, (C) 2015 Minio, Inc. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package cmd |
||||
|
||||
import ( |
||||
"testing" |
||||
|
||||
. "gopkg.in/check.v1" |
||||
) |
||||
|
||||
// Hook up gocheck into the "go test" runner.
|
||||
func Test(t *testing.T) { TestingT(t) } |
File diff suppressed because it is too large
Load Diff
@ -1,25 +0,0 @@ |
||||
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. |
@ -1,20 +0,0 @@ |
||||
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 |
@ -1,2 +0,0 @@ |
||||
- Assert(slice, Contains, item) |
||||
- Parallel test support |
@ -1,187 +0,0 @@ |
||||
// Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * 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.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// 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.
|
||||
|
||||
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 |
||||
} |
@ -1,954 +0,0 @@ |
||||
// 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" |
||||
"sync/atomic" |
||||
"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 uint32 |
||||
|
||||
// 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) status() funcStatus { |
||||
return funcStatus(atomic.LoadUint32((*uint32)(&c._status))) |
||||
} |
||||
|
||||
func (c *C) setStatus(s funcStatus) { |
||||
atomic.StoreUint32((*uint32)(&c._status), uint32(s)) |
||||
} |
||||
|
||||
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" || name == "call32") && 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.setStatus(skippedSt) |
||||
} else { |
||||
c.logSoftPanic("Fixture has panicked (see related PANIC)") |
||||
c.setStatus(fixturePanickedSt) |
||||
} |
||||
default: |
||||
c.logPanic(1, value) |
||||
c.setStatus(panickedSt) |
||||
} |
||||
} |
||||
if c.mustFail { |
||||
switch c.status() { |
||||
case failedSt: |
||||
c.setStatus(succeededSt) |
||||
case succeededSt: |
||||
c.setStatus(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.setStatus(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.setStatus(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.setStatus(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) |
||||
} |
@ -1,458 +0,0 @@ |
||||
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()), "" |
||||
} |
@ -1,231 +0,0 @@ |
||||
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.setStatus(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.setStatus(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.setStatus(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 |
||||
} |
@ -1,168 +0,0 @@ |
||||
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} |
||||
} |
@ -1,175 +0,0 @@ |
||||
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 |
||||
} |
Loading…
Reference in new issue