
Only trivial functional changes: - gets rid of mistaken extra "no tests" warning (whoops) - matches upstream's exit code better In preparation for switching to fancy test filtering.
429 строки
11 КиБ
Go
429 строки
11 КиБ
Go
// Copyright 2009 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
//
|
|
// This file has been modified for use by the TinyGo compiler.
|
|
// src: https://github.com/golang/go/blob/61bb56ad/src/testing/testing.go
|
|
|
|
// Package testing provides support for automated testing of Go packages.
|
|
package testing
|
|
|
|
import (
|
|
"bytes"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// Testing flags.
|
|
var (
|
|
flagVerbose bool
|
|
flagShort bool
|
|
flagRunRegexp string
|
|
)
|
|
|
|
var initRan bool
|
|
|
|
// Init registers testing flags. It has no effect if it has already run.
|
|
func Init() {
|
|
if initRan {
|
|
return
|
|
}
|
|
initRan = true
|
|
|
|
flag.BoolVar(&flagVerbose, "test.v", false, "verbose: print additional output")
|
|
flag.BoolVar(&flagShort, "test.short", false, "short: run smaller test suite to save time")
|
|
flag.StringVar(&flagRunRegexp, "test.run", "", "run: regexp of tests to run")
|
|
|
|
initBenchmarkFlags()
|
|
}
|
|
|
|
// common holds the elements common between T and B and
|
|
// captures common methods such as Errorf.
|
|
type common struct {
|
|
output bytes.Buffer
|
|
w io.Writer // either &output, or at top level, os.Stdout
|
|
indent string
|
|
ran bool // Test or benchmark (or one of its subtests) was executed.
|
|
failed bool // Test or benchmark has failed.
|
|
skipped bool // Test of benchmark has been skipped.
|
|
cleanups []func() // optional functions to be called at the end of the test
|
|
finished bool // Test function has completed.
|
|
|
|
parent *common
|
|
level int // Nesting depth of test or benchmark.
|
|
name string // Name of test or benchmark.
|
|
start time.Time // Time test or benchmark started
|
|
duration time.Duration
|
|
}
|
|
|
|
// Short reports whether the -test.short flag is set.
|
|
func Short() bool {
|
|
return flagShort
|
|
}
|
|
|
|
// CoverMode reports what the test coverage mode is set to.
|
|
//
|
|
// Test coverage is not supported; this returns the empty string.
|
|
func CoverMode() string {
|
|
return ""
|
|
}
|
|
|
|
// Verbose reports whether the -test.v flag is set.
|
|
func Verbose() bool {
|
|
return flagVerbose
|
|
}
|
|
|
|
// fmtDuration returns a string representing d in the form "87.00s".
|
|
func fmtDuration(d time.Duration) string {
|
|
return fmt.Sprintf("%.2fs", d.Seconds())
|
|
}
|
|
|
|
// TB is the interface common to T and B.
|
|
type TB interface {
|
|
Error(args ...interface{})
|
|
Errorf(format string, args ...interface{})
|
|
Fail()
|
|
FailNow()
|
|
Failed() bool
|
|
Fatal(args ...interface{})
|
|
Fatalf(format string, args ...interface{})
|
|
Log(args ...interface{})
|
|
Logf(format string, args ...interface{})
|
|
Name() string
|
|
Skip(args ...interface{})
|
|
SkipNow()
|
|
Skipf(format string, args ...interface{})
|
|
Skipped() bool
|
|
Helper()
|
|
Parallel()
|
|
}
|
|
|
|
var _ TB = (*T)(nil)
|
|
var _ TB = (*B)(nil)
|
|
|
|
// T is a type passed to Test functions to manage test state and support formatted test logs.
|
|
// Logs are accumulated during execution and dumped to standard output when done.
|
|
//
|
|
type T struct {
|
|
common
|
|
}
|
|
|
|
// Name returns the name of the running test or benchmark.
|
|
func (c *common) Name() string {
|
|
return c.name
|
|
}
|
|
|
|
// Fail marks the function as having failed but continues execution.
|
|
func (c *common) Fail() {
|
|
c.failed = true
|
|
}
|
|
|
|
// Failed reports whether the function has failed.
|
|
func (c *common) Failed() bool {
|
|
failed := c.failed
|
|
return failed
|
|
}
|
|
|
|
// FailNow marks the function as having failed and stops its execution
|
|
// by calling runtime.Goexit (which then runs all deferred calls in the
|
|
// current goroutine).
|
|
func (c *common) FailNow() {
|
|
c.Fail()
|
|
|
|
c.finished = true
|
|
c.Error("FailNow is incomplete, requires runtime.Goexit()")
|
|
}
|
|
|
|
// log generates the output.
|
|
func (c *common) log(s string) {
|
|
// This doesn't print the same as in upstream go, but works for now.
|
|
if len(s) != 0 && s[len(s)-1] == '\n' {
|
|
s = s[:len(s)-1]
|
|
}
|
|
lines := strings.Split(s, "\n")
|
|
// First line.
|
|
c.output.WriteString(c.indent)
|
|
c.output.WriteString(" ") // 4 spaces
|
|
c.output.WriteString(lines[0])
|
|
c.output.WriteByte('\n')
|
|
// More lines.
|
|
for _, line := range lines[1:] {
|
|
c.output.WriteString(c.indent)
|
|
c.output.WriteString(" ") // 8 spaces
|
|
c.output.WriteString(line)
|
|
c.output.WriteByte('\n')
|
|
}
|
|
}
|
|
|
|
// Log formats its arguments using default formatting, analogous to Println,
|
|
// and records the text in the error log. For tests, the text will be printed only if
|
|
// the test fails or the -test.v flag is set. For benchmarks, the text is always
|
|
// printed to avoid having performance depend on the value of the -test.v flag.
|
|
func (c *common) Log(args ...interface{}) { c.log(fmt.Sprintln(args...)) }
|
|
|
|
// Logf formats its arguments according to the format, analogous to Printf, and
|
|
// records the text in the error log. A final newline is added if not provided. For
|
|
// tests, the text will be printed only if the test fails or the -test.v flag is
|
|
// set. For benchmarks, the text is always printed to avoid having performance
|
|
// depend on the value of the -test.v flag.
|
|
func (c *common) Logf(format string, args ...interface{}) { c.log(fmt.Sprintf(format, args...)) }
|
|
|
|
// Error is equivalent to Log followed by Fail.
|
|
func (c *common) Error(args ...interface{}) {
|
|
c.log(fmt.Sprintln(args...))
|
|
c.Fail()
|
|
}
|
|
|
|
// Errorf is equivalent to Logf followed by Fail.
|
|
func (c *common) Errorf(format string, args ...interface{}) {
|
|
c.log(fmt.Sprintf(format, args...))
|
|
c.Fail()
|
|
}
|
|
|
|
// Fatal is equivalent to Log followed by FailNow.
|
|
func (c *common) Fatal(args ...interface{}) {
|
|
c.log(fmt.Sprintln(args...))
|
|
c.FailNow()
|
|
}
|
|
|
|
// Fatalf is equivalent to Logf followed by FailNow.
|
|
func (c *common) Fatalf(format string, args ...interface{}) {
|
|
c.log(fmt.Sprintf(format, args...))
|
|
c.FailNow()
|
|
}
|
|
|
|
// Skip is equivalent to Log followed by SkipNow.
|
|
func (c *common) Skip(args ...interface{}) {
|
|
c.log(fmt.Sprintln(args...))
|
|
c.SkipNow()
|
|
}
|
|
|
|
// Skipf is equivalent to Logf followed by SkipNow.
|
|
func (c *common) Skipf(format string, args ...interface{}) {
|
|
c.log(fmt.Sprintf(format, args...))
|
|
c.SkipNow()
|
|
}
|
|
|
|
// SkipNow marks the test as having been skipped and stops its execution
|
|
// by calling runtime.Goexit.
|
|
func (c *common) SkipNow() {
|
|
c.skip()
|
|
c.finished = true
|
|
c.Error("SkipNow is incomplete, requires runtime.Goexit()")
|
|
}
|
|
|
|
func (c *common) skip() {
|
|
c.skipped = true
|
|
}
|
|
|
|
// Skipped reports whether the test was skipped.
|
|
func (c *common) Skipped() bool {
|
|
return c.skipped
|
|
}
|
|
|
|
// Helper is not implemented, it is only provided for compatibility.
|
|
func (c *common) Helper() {
|
|
// Unimplemented.
|
|
}
|
|
|
|
// Cleanup registers a function to be called when the test (or subtest) and all its
|
|
// subtests complete. Cleanup functions will be called in last added,
|
|
// first called order.
|
|
func (c *common) Cleanup(f func()) {
|
|
c.cleanups = append(c.cleanups, f)
|
|
}
|
|
|
|
// runCleanup is called at the end of the test.
|
|
func (c *common) runCleanup() {
|
|
for {
|
|
var cleanup func()
|
|
if len(c.cleanups) > 0 {
|
|
last := len(c.cleanups) - 1
|
|
cleanup = c.cleanups[last]
|
|
c.cleanups = c.cleanups[:last]
|
|
}
|
|
if cleanup == nil {
|
|
return
|
|
}
|
|
cleanup()
|
|
}
|
|
}
|
|
|
|
// Parallel is not implemented, it is only provided for compatibility.
|
|
func (c *common) Parallel() {
|
|
// Unimplemented.
|
|
}
|
|
|
|
// InternalTest is a reference to a test that should be called during a test suite run.
|
|
type InternalTest struct {
|
|
Name string
|
|
F func(*T)
|
|
}
|
|
|
|
func tRunner(t *T, fn func(t *T)) {
|
|
defer func() {
|
|
t.runCleanup()
|
|
}()
|
|
|
|
// Run the test.
|
|
if flagVerbose {
|
|
fmt.Fprintf(t.w, "=== RUN %s\n", t.name)
|
|
}
|
|
|
|
t.start = time.Now()
|
|
fn(t)
|
|
t.duration += time.Since(t.start) // TODO: capture cleanup time, too.
|
|
|
|
t.report() // Report after all subtests have finished.
|
|
t.ran = true
|
|
}
|
|
|
|
// Run runs f as a subtest of t called name. It waits until the subtest is finished
|
|
// and returns whether the subtest succeeded.
|
|
func (t *T) Run(name string, f func(t *T)) bool {
|
|
// Create a subtest.
|
|
sub := T{
|
|
common: common{
|
|
name: t.name + "/" + rewrite(name),
|
|
indent: t.indent + " ",
|
|
w: &t.output,
|
|
parent: &t.common,
|
|
},
|
|
}
|
|
|
|
tRunner(&sub, f)
|
|
return !sub.failed
|
|
}
|
|
|
|
// M is a test suite.
|
|
type M struct {
|
|
// tests is a list of the test names to execute
|
|
Tests []InternalTest
|
|
Benchmarks []InternalBenchmark
|
|
|
|
deps testDeps
|
|
|
|
// value to pass to os.Exit, the outer test func main
|
|
// harness calls os.Exit with this code. See #34129.
|
|
exitCode int
|
|
}
|
|
|
|
type testDeps interface {
|
|
MatchString(pat, str string) (bool, error)
|
|
}
|
|
|
|
func MainStart(deps interface{}, tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) *M {
|
|
Init()
|
|
return &M{
|
|
Tests: tests,
|
|
Benchmarks: benchmarks,
|
|
deps: deps.(testDeps),
|
|
}
|
|
}
|
|
|
|
// Run runs the tests. It returns an exit code to pass to os.Exit.
|
|
func (m *M) Run() (code int) {
|
|
defer func() {
|
|
code = m.exitCode
|
|
}()
|
|
|
|
if !flag.Parsed() {
|
|
flag.Parse()
|
|
}
|
|
|
|
testRan, testOk := runTests(m.deps.MatchString, m.Tests)
|
|
if !testRan && *matchBenchmarks == "" {
|
|
fmt.Fprintln(os.Stderr, "testing: warning: no tests to run")
|
|
}
|
|
if !testOk || !runBenchmarks(m.deps.MatchString, m.Benchmarks) {
|
|
fmt.Println("FAIL")
|
|
m.exitCode = 1
|
|
} else {
|
|
if flagVerbose {
|
|
fmt.Println("PASS")
|
|
}
|
|
m.exitCode = 0
|
|
}
|
|
return
|
|
}
|
|
|
|
func runTests(matchString func(pat, str string) (bool, error), tests []InternalTest) (ran, ok bool) {
|
|
ok = true
|
|
if flagRunRegexp != "" {
|
|
var filtered []InternalTest
|
|
|
|
// pre-test the regexp; we don't want to bother logging one failure for every test name if the regexp is broken
|
|
if _, err := matchString(flagRunRegexp, "some-test-name"); err != nil {
|
|
fmt.Println("testing: invalid regexp for -test.run:", err.Error())
|
|
return false, false
|
|
}
|
|
|
|
// filter the list of tests before we try to run them
|
|
for _, test := range tests {
|
|
// ignore the error; we already tested that the regexp compiles fine above
|
|
if match, _ := matchString(flagRunRegexp, test.Name); match {
|
|
filtered = append(filtered, test)
|
|
}
|
|
}
|
|
|
|
tests = filtered
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t := &T{
|
|
common: common{
|
|
name: test.Name,
|
|
w: os.Stdout,
|
|
},
|
|
}
|
|
|
|
tRunner(t, test.F)
|
|
|
|
ok = ok && !t.Failed()
|
|
ran = ran || t.ran
|
|
}
|
|
return ran, ok
|
|
}
|
|
|
|
func (t *T) report() {
|
|
dstr := fmtDuration(t.duration)
|
|
format := t.indent + "--- %s: %s (%s)\n"
|
|
if t.Failed() {
|
|
if t.parent != nil {
|
|
t.parent.failed = true
|
|
}
|
|
fmt.Fprintf(t.w, format, "FAIL", t.name, dstr)
|
|
t.w.Write(t.output.Bytes())
|
|
} else if flagVerbose {
|
|
if t.Skipped() {
|
|
fmt.Fprintf(t.w, format, "SKIP", t.name, dstr)
|
|
} else {
|
|
fmt.Fprintf(t.w, format, "PASS", t.name, dstr)
|
|
}
|
|
t.w.Write(t.output.Bytes())
|
|
}
|
|
}
|
|
|
|
// AllocsPerRun returns the average number of allocations during calls to f.
|
|
// Although the return value has type float64, it will always be an integral
|
|
// value.
|
|
//
|
|
// Not implemented.
|
|
func AllocsPerRun(runs int, f func()) (avg float64) {
|
|
f()
|
|
for i := 0; i < runs; i++ {
|
|
f()
|
|
}
|
|
return 0
|
|
}
|
|
|
|
type InternalExample struct {
|
|
Name string
|
|
F func()
|
|
Output string
|
|
Unordered bool
|
|
}
|