~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/gopkg.in/check.v1/check.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

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