launchpad.net/gocheck@v0.0.0-20140225173054-000000000087/gocheck.go (about)

     1  package gocheck
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"math/rand"
     9  	"os"
    10  	"path"
    11  	"path/filepath"
    12  	"reflect"
    13  	"regexp"
    14  	"runtime"
    15  	"strconv"
    16  	"strings"
    17  	"sync"
    18  	"time"
    19  )
    20  
    21  // -----------------------------------------------------------------------
    22  // Internal type which deals with suite method calling.
    23  
    24  const (
    25  	fixtureKd = iota
    26  	testKd
    27  )
    28  
    29  type funcKind int
    30  
    31  const (
    32  	succeededSt = iota
    33  	failedSt
    34  	skippedSt
    35  	panickedSt
    36  	fixturePanickedSt
    37  	missedSt
    38  )
    39  
    40  type funcStatus int
    41  
    42  // A method value can't reach its own Method structure.
    43  type methodType struct {
    44  	reflect.Value
    45  	Info reflect.Method
    46  }
    47  
    48  func newMethod(receiver reflect.Value, i int) *methodType {
    49  	return &methodType{receiver.Method(i), receiver.Type().Method(i)}
    50  }
    51  
    52  func (method *methodType) PC() uintptr {
    53  	return method.Info.Func.Pointer()
    54  }
    55  
    56  func (method *methodType) suiteName() string {
    57  	t := method.Info.Type.In(0)
    58  	if t.Kind() == reflect.Ptr {
    59  		t = t.Elem()
    60  	}
    61  	return t.Name()
    62  }
    63  
    64  func (method *methodType) String() string {
    65  	return method.suiteName() + "." + method.Info.Name
    66  }
    67  
    68  func (method *methodType) matches(re *regexp.Regexp) bool {
    69  	return (re.MatchString(method.Info.Name) ||
    70  		re.MatchString(method.suiteName()) ||
    71  		re.MatchString(method.String()))
    72  }
    73  
    74  type C struct {
    75  	method    *methodType
    76  	kind      funcKind
    77  	status    funcStatus
    78  	logb      *logger
    79  	logw      io.Writer
    80  	done      chan *C
    81  	reason    string
    82  	mustFail  bool
    83  	tempDir   *tempDir
    84  	startTime time.Time
    85  	timer
    86  }
    87  
    88  func (c *C) stopNow() {
    89  	runtime.Goexit()
    90  }
    91  
    92  // logger is a concurrency safe byte.Buffer
    93  type logger struct {
    94  	sync.Mutex
    95  	writer bytes.Buffer
    96  }
    97  
    98  func (l *logger) Write(buf []byte) (int, error) {
    99  	l.Lock()
   100  	defer l.Unlock()
   101  	return l.writer.Write(buf)
   102  }
   103  
   104  func (l *logger) WriteTo(w io.Writer) (int64, error) {
   105  	l.Lock()
   106  	defer l.Unlock()
   107  	return l.writer.WriteTo(w)
   108  }
   109  
   110  func (l *logger) String() string {
   111  	l.Lock()
   112  	defer l.Unlock()
   113  	return l.writer.String()
   114  }
   115  
   116  // -----------------------------------------------------------------------
   117  // Handling of temporary files and directories.
   118  
   119  type tempDir struct {
   120  	sync.Mutex
   121  	_path    string
   122  	_counter int
   123  }
   124  
   125  func (td *tempDir) newPath() string {
   126  	td.Lock()
   127  	defer td.Unlock()
   128  	if td._path == "" {
   129  		var err error
   130  		for i := 0; i != 100; i++ {
   131  			path := fmt.Sprintf("%s/gocheck-%d", os.TempDir(), rand.Int())
   132  			if err = os.Mkdir(path, 0700); err == nil {
   133  				td._path = path
   134  				break
   135  			}
   136  		}
   137  		if td._path == "" {
   138  			panic("Couldn't create temporary directory: " + err.Error())
   139  		}
   140  	}
   141  	result := path.Join(td._path, strconv.Itoa(td._counter))
   142  	td._counter += 1
   143  	return result
   144  }
   145  
   146  func (td *tempDir) removeAll() {
   147  	td.Lock()
   148  	defer td.Unlock()
   149  	if td._path != "" {
   150  		err := os.RemoveAll(td._path)
   151  		if err != nil {
   152  			fmt.Fprintf(os.Stderr, "WARNING: Error cleaning up temporaries: "+err.Error())
   153  		}
   154  	}
   155  }
   156  
   157  // Create a new temporary directory which is automatically removed after
   158  // the suite finishes running.
   159  func (c *C) MkDir() string {
   160  	path := c.tempDir.newPath()
   161  	if err := os.Mkdir(path, 0700); err != nil {
   162  		panic(fmt.Sprintf("Couldn't create temporary directory %s: %s", path, err.Error()))
   163  	}
   164  	return path
   165  }
   166  
   167  // -----------------------------------------------------------------------
   168  // Low-level logging functions.
   169  
   170  func (c *C) log(args ...interface{}) {
   171  	c.writeLog([]byte(fmt.Sprint(args...) + "\n"))
   172  }
   173  
   174  func (c *C) logf(format string, args ...interface{}) {
   175  	c.writeLog([]byte(fmt.Sprintf(format+"\n", args...)))
   176  }
   177  
   178  func (c *C) logNewLine() {
   179  	c.writeLog([]byte{'\n'})
   180  }
   181  
   182  func (c *C) writeLog(buf []byte) {
   183  	c.logb.Write(buf)
   184  	if c.logw != nil {
   185  		c.logw.Write(buf)
   186  	}
   187  }
   188  
   189  func hasStringOrError(x interface{}) (ok bool) {
   190  	_, ok = x.(fmt.Stringer)
   191  	if ok {
   192  		return
   193  	}
   194  	_, ok = x.(error)
   195  	return
   196  }
   197  
   198  func (c *C) logValue(label string, value interface{}) {
   199  	if label == "" {
   200  		if hasStringOrError(value) {
   201  			c.logf("... %#v (%q)", value, value)
   202  		} else {
   203  			c.logf("... %#v", value)
   204  		}
   205  	} else if value == nil {
   206  		c.logf("... %s = nil", label)
   207  	} else {
   208  		if hasStringOrError(value) {
   209  			fv := fmt.Sprintf("%#v", value)
   210  			qv := fmt.Sprintf("%q", value)
   211  			if fv != qv {
   212  				c.logf("... %s %s = %s (%s)", label, reflect.TypeOf(value), fv, qv)
   213  				return
   214  			}
   215  		}
   216  		if s, ok := value.(string); ok && isMultiLine(s) {
   217  			c.logf(`... %s %s = "" +`, label, reflect.TypeOf(value))
   218  			c.logMultiLine(s)
   219  		} else {
   220  			c.logf("... %s %s = %#v", label, reflect.TypeOf(value), value)
   221  		}
   222  	}
   223  }
   224  
   225  func (c *C) logMultiLine(s string) {
   226  	b := make([]byte, 0, len(s)*2)
   227  	i := 0
   228  	n := len(s)
   229  	for i < n {
   230  		j := i + 1
   231  		for j < n && s[j-1] != '\n' {
   232  			j++
   233  		}
   234  		b = append(b, "...     "...)
   235  		b = strconv.AppendQuote(b, s[i:j])
   236  		if j < n {
   237  			b = append(b, " +"...)
   238  		}
   239  		b = append(b, '\n')
   240  		i = j
   241  	}
   242  	c.writeLog(b)
   243  }
   244  
   245  func isMultiLine(s string) bool {
   246  	for i := 0; i+1 < len(s); i++ {
   247  		if s[i] == '\n' {
   248  			return true
   249  		}
   250  	}
   251  	return false
   252  }
   253  
   254  func (c *C) logString(issue string) {
   255  	c.log("... ", issue)
   256  }
   257  
   258  func (c *C) logCaller(skip int) {
   259  	// This is a bit heavier than it ought to be.
   260  	skip += 1 // Our own frame.
   261  	pc, callerFile, callerLine, ok := runtime.Caller(skip)
   262  	if !ok {
   263  		return
   264  	}
   265  	var testFile string
   266  	var testLine int
   267  	testFunc := runtime.FuncForPC(c.method.PC())
   268  	if runtime.FuncForPC(pc) != testFunc {
   269  		for {
   270  			skip += 1
   271  			if pc, file, line, ok := runtime.Caller(skip); ok {
   272  				// Note that the test line may be different on
   273  				// distinct calls for the same test.  Showing
   274  				// the "internal" line is helpful when debugging.
   275  				if runtime.FuncForPC(pc) == testFunc {
   276  					testFile, testLine = file, line
   277  					break
   278  				}
   279  			} else {
   280  				break
   281  			}
   282  		}
   283  	}
   284  	if testFile != "" && (testFile != callerFile || testLine != callerLine) {
   285  		c.logCode(testFile, testLine)
   286  	}
   287  	c.logCode(callerFile, callerLine)
   288  }
   289  
   290  func (c *C) logCode(path string, line int) {
   291  	c.logf("%s:%d:", nicePath(path), line)
   292  	code, err := printLine(path, line)
   293  	if code == "" {
   294  		code = "..." // XXX Open the file and take the raw line.
   295  		if err != nil {
   296  			code += err.Error()
   297  		}
   298  	}
   299  	c.log(indent(code, "    "))
   300  }
   301  
   302  var valueGo = filepath.Join("reflect", "value.go")
   303  
   304  func (c *C) logPanic(skip int, value interface{}) {
   305  	skip += 1 // Our own frame.
   306  	initialSkip := skip
   307  	for {
   308  		if pc, file, line, ok := runtime.Caller(skip); ok {
   309  			if skip == initialSkip {
   310  				c.logf("... Panic: %s (PC=0x%X)\n", value, pc)
   311  			}
   312  			name := niceFuncName(pc)
   313  			path := nicePath(file)
   314  			if name == "Value.call" && strings.HasSuffix(path, valueGo) {
   315  				break
   316  			}
   317  			c.logf("%s:%d\n  in %s", nicePath(file), line, name)
   318  		} else {
   319  			break
   320  		}
   321  		skip += 1
   322  	}
   323  }
   324  
   325  func (c *C) logSoftPanic(issue string) {
   326  	c.log("... Panic: ", issue)
   327  }
   328  
   329  func (c *C) logArgPanic(method *methodType, expectedType string) {
   330  	c.logf("... Panic: %s argument should be %s",
   331  		niceFuncName(method.PC()), expectedType)
   332  }
   333  
   334  // -----------------------------------------------------------------------
   335  // Some simple formatting helpers.
   336  
   337  var initWD, initWDErr = os.Getwd()
   338  
   339  func init() {
   340  	if initWDErr == nil {
   341  		initWD = strings.Replace(initWD, "\\", "/", -1) + "/"
   342  	}
   343  }
   344  
   345  func nicePath(path string) string {
   346  	if initWDErr == nil {
   347  		if strings.HasPrefix(path, initWD) {
   348  			return path[len(initWD):]
   349  		}
   350  	}
   351  	return path
   352  }
   353  
   354  func niceFuncPath(pc uintptr) string {
   355  	function := runtime.FuncForPC(pc)
   356  	if function != nil {
   357  		filename, line := function.FileLine(pc)
   358  		return fmt.Sprintf("%s:%d", nicePath(filename), line)
   359  	}
   360  	return "<unknown path>"
   361  }
   362  
   363  func niceFuncName(pc uintptr) string {
   364  	function := runtime.FuncForPC(pc)
   365  	if function != nil {
   366  		name := path.Base(function.Name())
   367  		if i := strings.Index(name, "."); i > 0 {
   368  			name = name[i+1:]
   369  		}
   370  		if strings.HasPrefix(name, "(*") {
   371  			if i := strings.Index(name, ")"); i > 0 {
   372  				name = name[2:i] + name[i+1:]
   373  			}
   374  		}
   375  		if i := strings.LastIndex(name, ".*"); i != -1 {
   376  			name = name[:i] + "." + name[i+2:]
   377  		}
   378  		if i := strings.LastIndex(name, "ยท"); i != -1 {
   379  			name = name[:i] + "." + name[i+2:]
   380  		}
   381  		return name
   382  	}
   383  	return "<unknown function>"
   384  }
   385  
   386  // -----------------------------------------------------------------------
   387  // Result tracker to aggregate call results.
   388  
   389  type Result struct {
   390  	Succeeded        int
   391  	Failed           int
   392  	Skipped          int
   393  	Panicked         int
   394  	FixturePanicked  int
   395  	ExpectedFailures int
   396  	Missed           int   // Not even tried to run, related to a panic in the fixture.
   397  	RunError         error // Houston, we've got a problem.
   398  }
   399  
   400  type resultTracker struct {
   401  	result          Result
   402  	_lastWasProblem bool
   403  	_waiting        int
   404  	_missed         int
   405  	_expectChan     chan *C
   406  	_doneChan       chan *C
   407  	_stopChan       chan bool
   408  }
   409  
   410  func newResultTracker() *resultTracker {
   411  	return &resultTracker{_expectChan: make(chan *C), // Synchronous
   412  		_doneChan: make(chan *C, 32), // Asynchronous
   413  		_stopChan: make(chan bool)}   // Synchronous
   414  }
   415  
   416  func (tracker *resultTracker) start() {
   417  	go tracker._loopRoutine()
   418  }
   419  
   420  func (tracker *resultTracker) waitAndStop() {
   421  	<-tracker._stopChan
   422  }
   423  
   424  func (tracker *resultTracker) expectCall(c *C) {
   425  	tracker._expectChan <- c
   426  }
   427  
   428  func (tracker *resultTracker) callDone(c *C) {
   429  	tracker._doneChan <- c
   430  }
   431  
   432  func (tracker *resultTracker) _loopRoutine() {
   433  	for {
   434  		var c *C
   435  		if tracker._waiting > 0 {
   436  			// Calls still running. Can't stop.
   437  			select {
   438  			// XXX Reindent this (not now to make diff clear)
   439  			case c = <-tracker._expectChan:
   440  				tracker._waiting += 1
   441  			case c = <-tracker._doneChan:
   442  				tracker._waiting -= 1
   443  				switch c.status {
   444  				case succeededSt:
   445  					if c.kind == testKd {
   446  						if c.mustFail {
   447  							tracker.result.ExpectedFailures++
   448  						} else {
   449  							tracker.result.Succeeded++
   450  						}
   451  					}
   452  				case failedSt:
   453  					tracker.result.Failed++
   454  				case panickedSt:
   455  					if c.kind == fixtureKd {
   456  						tracker.result.FixturePanicked++
   457  					} else {
   458  						tracker.result.Panicked++
   459  					}
   460  				case fixturePanickedSt:
   461  					// Track it as missed, since the panic
   462  					// was on the fixture, not on the test.
   463  					tracker.result.Missed++
   464  				case missedSt:
   465  					tracker.result.Missed++
   466  				case skippedSt:
   467  					if c.kind == testKd {
   468  						tracker.result.Skipped++
   469  					}
   470  				}
   471  			}
   472  		} else {
   473  			// No calls.  Can stop, but no done calls here.
   474  			select {
   475  			case tracker._stopChan <- true:
   476  				return
   477  			case c = <-tracker._expectChan:
   478  				tracker._waiting += 1
   479  			case c = <-tracker._doneChan:
   480  				panic("Tracker got an unexpected done call.")
   481  			}
   482  		}
   483  	}
   484  }
   485  
   486  // -----------------------------------------------------------------------
   487  // The underlying suite runner.
   488  
   489  type suiteRunner struct {
   490  	suite                     interface{}
   491  	setUpSuite, tearDownSuite *methodType
   492  	setUpTest, tearDownTest   *methodType
   493  	tests                     []*methodType
   494  	tracker                   *resultTracker
   495  	tempDir                   *tempDir
   496  	output                    *outputWriter
   497  	reportedProblemLast       bool
   498  	benchTime                 time.Duration
   499  }
   500  
   501  type RunConf struct {
   502  	Output        io.Writer
   503  	Stream        bool
   504  	Verbose       bool
   505  	Filter        string
   506  	Benchmark     bool
   507  	BenchmarkTime time.Duration // Defaults to 1 second
   508  }
   509  
   510  // Create a new suiteRunner able to run all methods in the given suite.
   511  func newSuiteRunner(suite interface{}, runConf *RunConf) *suiteRunner {
   512  	var conf RunConf
   513  	if runConf != nil {
   514  		conf = *runConf
   515  	}
   516  	if conf.Output == nil {
   517  		conf.Output = os.Stdout
   518  	}
   519  	if conf.Benchmark {
   520  		conf.Verbose = true
   521  	}
   522  
   523  	suiteType := reflect.TypeOf(suite)
   524  	suiteNumMethods := suiteType.NumMethod()
   525  	suiteValue := reflect.ValueOf(suite)
   526  
   527  	runner := &suiteRunner{
   528  		suite:     suite,
   529  		output:    newOutputWriter(conf.Output, conf.Stream, conf.Verbose),
   530  		tracker:   newResultTracker(),
   531  		benchTime: conf.BenchmarkTime,
   532  	}
   533  	runner.tests = make([]*methodType, 0, suiteNumMethods)
   534  	runner.tempDir = new(tempDir)
   535  	if runner.benchTime == 0 {
   536  		runner.benchTime = 1 * time.Second
   537  	}
   538  
   539  	var filterRegexp *regexp.Regexp
   540  	if conf.Filter != "" {
   541  		if regexp, err := regexp.Compile(conf.Filter); err != nil {
   542  			msg := "Bad filter expression: " + err.Error()
   543  			runner.tracker.result.RunError = errors.New(msg)
   544  			return runner
   545  		} else {
   546  			filterRegexp = regexp
   547  		}
   548  	}
   549  
   550  	for i := 0; i != suiteNumMethods; i++ {
   551  		method := newMethod(suiteValue, i)
   552  		switch method.Info.Name {
   553  		case "SetUpSuite":
   554  			runner.setUpSuite = method
   555  		case "TearDownSuite":
   556  			runner.tearDownSuite = method
   557  		case "SetUpTest":
   558  			runner.setUpTest = method
   559  		case "TearDownTest":
   560  			runner.tearDownTest = method
   561  		default:
   562  			prefix := "Test"
   563  			if conf.Benchmark {
   564  				prefix = "Benchmark"
   565  			}
   566  			if !strings.HasPrefix(method.Info.Name, prefix) {
   567  				continue
   568  			}
   569  			if filterRegexp == nil || method.matches(filterRegexp) {
   570  				runner.tests = append(runner.tests, method)
   571  			}
   572  		}
   573  	}
   574  	return runner
   575  }
   576  
   577  // Run all methods in the given suite.
   578  func (runner *suiteRunner) run() *Result {
   579  	if runner.tracker.result.RunError == nil && len(runner.tests) > 0 {
   580  		runner.tracker.start()
   581  		if runner.checkFixtureArgs() {
   582  			c := runner.runFixture(runner.setUpSuite, nil)
   583  			if c == nil || c.status == succeededSt {
   584  				for i := 0; i != len(runner.tests); i++ {
   585  					c := runner.runTest(runner.tests[i])
   586  					if c.status == fixturePanickedSt {
   587  						runner.skipTests(missedSt, runner.tests[i+1:])
   588  						break
   589  					}
   590  				}
   591  			} else if c != nil && c.status == skippedSt {
   592  				runner.skipTests(skippedSt, runner.tests)
   593  			} else {
   594  				runner.skipTests(missedSt, runner.tests)
   595  			}
   596  			runner.runFixture(runner.tearDownSuite, nil)
   597  		} else {
   598  			runner.skipTests(missedSt, runner.tests)
   599  		}
   600  		runner.tracker.waitAndStop()
   601  		runner.tempDir.removeAll()
   602  	}
   603  	return &runner.tracker.result
   604  }
   605  
   606  // Create a call object with the given suite method, and fork a
   607  // goroutine with the provided dispatcher for running it.
   608  func (runner *suiteRunner) forkCall(method *methodType, kind funcKind, logb *logger, dispatcher func(c *C)) *C {
   609  	var logw io.Writer
   610  	if runner.output.Stream {
   611  		logw = runner.output
   612  	}
   613  	if logb == nil {
   614  		logb = new(logger)
   615  	}
   616  	c := &C{
   617  		method:    method,
   618  		kind:      kind,
   619  		logb:      logb,
   620  		logw:      logw,
   621  		tempDir:   runner.tempDir,
   622  		done:      make(chan *C, 1),
   623  		timer:     timer{benchTime: runner.benchTime},
   624  		startTime: time.Now(),
   625  	}
   626  	runner.tracker.expectCall(c)
   627  	go (func() {
   628  		runner.reportCallStarted(c)
   629  		defer runner.callDone(c)
   630  		dispatcher(c)
   631  	})()
   632  	return c
   633  }
   634  
   635  // Same as forkCall(), but wait for call to finish before returning.
   636  func (runner *suiteRunner) runFunc(method *methodType, kind funcKind, logb *logger, dispatcher func(c *C)) *C {
   637  	c := runner.forkCall(method, kind, logb, dispatcher)
   638  	<-c.done
   639  	return c
   640  }
   641  
   642  // Handle a finished call.  If there were any panics, update the call status
   643  // accordingly.  Then, mark the call as done and report to the tracker.
   644  func (runner *suiteRunner) callDone(c *C) {
   645  	value := recover()
   646  	if value != nil {
   647  		switch v := value.(type) {
   648  		case *fixturePanic:
   649  			if v.status == skippedSt {
   650  				c.status = skippedSt
   651  			} else {
   652  				c.logSoftPanic("Fixture has panicked (see related PANIC)")
   653  				c.status = fixturePanickedSt
   654  			}
   655  		default:
   656  			c.logPanic(1, value)
   657  			c.status = panickedSt
   658  		}
   659  	}
   660  	if c.mustFail {
   661  		switch c.status {
   662  		case failedSt:
   663  			c.status = succeededSt
   664  		case succeededSt:
   665  			c.status = failedSt
   666  			c.logString("Error: Test succeeded, but was expected to fail")
   667  			c.logString("Reason: " + c.reason)
   668  		}
   669  	}
   670  
   671  	runner.reportCallDone(c)
   672  	c.done <- c
   673  }
   674  
   675  // Runs a fixture call synchronously.  The fixture will still be run in a
   676  // goroutine like all suite methods, but this method will not return
   677  // while the fixture goroutine is not done, because the fixture must be
   678  // run in a desired order.
   679  func (runner *suiteRunner) runFixture(method *methodType, logb *logger) *C {
   680  	if method != nil {
   681  		c := runner.runFunc(method, fixtureKd, logb, func(c *C) {
   682  			c.ResetTimer()
   683  			c.StartTimer()
   684  			defer c.StopTimer()
   685  			c.method.Call([]reflect.Value{reflect.ValueOf(c)})
   686  		})
   687  		return c
   688  	}
   689  	return nil
   690  }
   691  
   692  // Run the fixture method with runFixture(), but panic with a fixturePanic{}
   693  // in case the fixture method panics.  This makes it easier to track the
   694  // fixture panic together with other call panics within forkTest().
   695  func (runner *suiteRunner) runFixtureWithPanic(method *methodType, logb *logger, skipped *bool) *C {
   696  	if skipped != nil && *skipped {
   697  		return nil
   698  	}
   699  	c := runner.runFixture(method, logb)
   700  	if c != nil && c.status != succeededSt {
   701  		if skipped != nil {
   702  			*skipped = c.status == skippedSt
   703  		}
   704  		panic(&fixturePanic{c.status, method})
   705  	}
   706  	return c
   707  }
   708  
   709  type fixturePanic struct {
   710  	status funcStatus
   711  	method *methodType
   712  }
   713  
   714  // Run the suite test method, together with the test-specific fixture,
   715  // asynchronously.
   716  func (runner *suiteRunner) forkTest(method *methodType) *C {
   717  	return runner.forkCall(method, testKd, nil, func(c *C) {
   718  		var skipped bool
   719  		defer runner.runFixtureWithPanic(runner.tearDownTest, nil, &skipped)
   720  		defer c.StopTimer()
   721  		benchN := 1
   722  		for {
   723  			runner.runFixtureWithPanic(runner.setUpTest, c.logb, &skipped)
   724  			mt := c.method.Type()
   725  			if mt.NumIn() != 1 || mt.In(0) != reflect.TypeOf(c) {
   726  				// Rather than a plain panic, provide a more helpful message when
   727  				// the argument type is incorrect.
   728  				c.status = panickedSt
   729  				c.logArgPanic(c.method, "*gocheck.C")
   730  				return
   731  			}
   732  			if strings.HasPrefix(c.method.Info.Name, "Test") {
   733  				c.ResetTimer()
   734  				c.StartTimer()
   735  				c.method.Call([]reflect.Value{reflect.ValueOf(c)})
   736  				return
   737  			}
   738  			if !strings.HasPrefix(c.method.Info.Name, "Benchmark") {
   739  				panic("unexpected method prefix: " + c.method.Info.Name)
   740  			}
   741  
   742  			runtime.GC()
   743  			c.N = benchN
   744  			c.ResetTimer()
   745  			c.StartTimer()
   746  			c.method.Call([]reflect.Value{reflect.ValueOf(c)})
   747  			c.StopTimer()
   748  			if c.status != succeededSt || c.duration >= c.benchTime || benchN >= 1e9 {
   749  				return
   750  			}
   751  			perOpN := int(1e9)
   752  			if c.nsPerOp() != 0 {
   753  				perOpN = int(c.benchTime.Nanoseconds() / c.nsPerOp())
   754  			}
   755  
   756  			// Logic taken from the stock testing package:
   757  			// - Run more iterations than we think we'll need for a second (1.5x).
   758  			// - Don't grow too fast in case we had timing errors previously.
   759  			// - Be sure to run at least one more than last time.
   760  			benchN = max(min(perOpN+perOpN/2, 100*benchN), benchN+1)
   761  			benchN = roundUp(benchN)
   762  
   763  			skipped = true // Don't run the deferred one if this panics.
   764  			runner.runFixtureWithPanic(runner.tearDownTest, nil, nil)
   765  			skipped = false
   766  		}
   767  	})
   768  }
   769  
   770  // Same as forkTest(), but wait for the test to finish before returning.
   771  func (runner *suiteRunner) runTest(method *methodType) *C {
   772  	c := runner.forkTest(method)
   773  	<-c.done
   774  	return c
   775  }
   776  
   777  // Helper to mark tests as skipped or missed.  A bit heavy for what
   778  // it does, but it enables homogeneous handling of tracking, including
   779  // nice verbose output.
   780  func (runner *suiteRunner) skipTests(status funcStatus, methods []*methodType) {
   781  	for _, method := range methods {
   782  		runner.runFunc(method, testKd, nil, func(c *C) {
   783  			c.status = status
   784  		})
   785  	}
   786  }
   787  
   788  // Verify if the fixture arguments are *gocheck.C.  In case of errors,
   789  // log the error as a panic in the fixture method call, and return false.
   790  func (runner *suiteRunner) checkFixtureArgs() bool {
   791  	succeeded := true
   792  	argType := reflect.TypeOf(&C{})
   793  	for _, method := range []*methodType{runner.setUpSuite, runner.tearDownSuite, runner.setUpTest, runner.tearDownTest} {
   794  		if method != nil {
   795  			mt := method.Type()
   796  			if mt.NumIn() != 1 || mt.In(0) != argType {
   797  				succeeded = false
   798  				runner.runFunc(method, fixtureKd, nil, func(c *C) {
   799  					c.logArgPanic(method, "*gocheck.C")
   800  					c.status = panickedSt
   801  				})
   802  			}
   803  		}
   804  	}
   805  	return succeeded
   806  }
   807  
   808  func (runner *suiteRunner) reportCallStarted(c *C) {
   809  	runner.output.WriteCallStarted("START", c)
   810  }
   811  
   812  func (runner *suiteRunner) reportCallDone(c *C) {
   813  	runner.tracker.callDone(c)
   814  	switch c.status {
   815  	case succeededSt:
   816  		if c.mustFail {
   817  			runner.output.WriteCallSuccess("FAIL EXPECTED", c)
   818  		} else {
   819  			runner.output.WriteCallSuccess("PASS", c)
   820  		}
   821  	case skippedSt:
   822  		runner.output.WriteCallSuccess("SKIP", c)
   823  	case failedSt:
   824  		runner.output.WriteCallProblem("FAIL", c)
   825  	case panickedSt:
   826  		runner.output.WriteCallProblem("PANIC", c)
   827  	case fixturePanickedSt:
   828  		// That's a testKd call reporting that its fixture
   829  		// has panicked. The fixture call which caused the
   830  		// panic itself was tracked above. We'll report to
   831  		// aid debugging.
   832  		runner.output.WriteCallProblem("PANIC", c)
   833  	case missedSt:
   834  		runner.output.WriteCallSuccess("MISS", c)
   835  	}
   836  }
   837  
   838  // -----------------------------------------------------------------------
   839  // Output writer manages atomic output writing according to settings.
   840  
   841  type outputWriter struct {
   842  	m                    sync.Mutex
   843  	writer               io.Writer
   844  	wroteCallProblemLast bool
   845  	Stream               bool
   846  	Verbose              bool
   847  }
   848  
   849  func newOutputWriter(writer io.Writer, stream, verbose bool) *outputWriter {
   850  	return &outputWriter{writer: writer, Stream: stream, Verbose: verbose}
   851  }
   852  
   853  func (ow *outputWriter) Write(content []byte) (n int, err error) {
   854  	ow.m.Lock()
   855  	n, err = ow.writer.Write(content)
   856  	ow.m.Unlock()
   857  	return
   858  }
   859  
   860  func (ow *outputWriter) WriteCallStarted(label string, c *C) {
   861  	if ow.Stream {
   862  		header := renderCallHeader(label, c, "", "\n")
   863  		ow.m.Lock()
   864  		ow.writer.Write([]byte(header))
   865  		ow.m.Unlock()
   866  	}
   867  }
   868  
   869  func (ow *outputWriter) WriteCallProblem(label string, c *C) {
   870  	var prefix string
   871  	if !ow.Stream {
   872  		prefix = "\n-----------------------------------" +
   873  			"-----------------------------------\n"
   874  	}
   875  	header := renderCallHeader(label, c, prefix, "\n\n")
   876  	ow.m.Lock()
   877  	ow.wroteCallProblemLast = true
   878  	ow.writer.Write([]byte(header))
   879  	if !ow.Stream {
   880  		c.logb.WriteTo(ow.writer)
   881  	}
   882  	ow.m.Unlock()
   883  }
   884  
   885  func (ow *outputWriter) WriteCallSuccess(label string, c *C) {
   886  	if ow.Stream || (ow.Verbose && c.kind == testKd) {
   887  		// TODO Use a buffer here.
   888  		var suffix string
   889  		if c.reason != "" {
   890  			suffix = " (" + c.reason + ")"
   891  		}
   892  		if c.status == succeededSt {
   893  			suffix += "\t" + c.timerString()
   894  		}
   895  		suffix += "\n"
   896  		if ow.Stream {
   897  			suffix += "\n"
   898  		}
   899  		header := renderCallHeader(label, c, "", suffix)
   900  		ow.m.Lock()
   901  		// Resist temptation of using line as prefix above due to race.
   902  		if !ow.Stream && ow.wroteCallProblemLast {
   903  			header = "\n-----------------------------------" +
   904  				"-----------------------------------\n" +
   905  				header
   906  		}
   907  		ow.wroteCallProblemLast = false
   908  		ow.writer.Write([]byte(header))
   909  		ow.m.Unlock()
   910  	}
   911  }
   912  
   913  func renderCallHeader(label string, c *C, prefix, suffix string) string {
   914  	pc := c.method.PC()
   915  	return fmt.Sprintf("%s%s: %s: %s%s", prefix, label, niceFuncPath(pc),
   916  		niceFuncName(pc), suffix)
   917  }