// 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 }