tinygo/src/testing/testing.go
2021-09-27 20:02:02 +02:00

308 строки
7,3 КиБ
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"
"os"
"strings"
)
// Testing flags.
var (
flagVerbose bool
flagShort bool
)
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")
}
// common holds the elements common between T and B and
// captures common methods such as Errorf.
type common struct {
output bytes.Buffer
indent string
failed bool // Test or benchmark has failed.
skipped bool // Test of benchmark has been skipped.
finished bool // Test function has completed.
name string // Name of test or benchmark.
}
// 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()
}
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.
}
// Run runs a subtest of f 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 + "/" + name,
indent: t.indent + " ",
},
}
// Run the test.
if flagVerbose {
fmt.Fprintf(&t.output, "=== RUN %s\n", sub.name)
}
f(&sub)
// Process the result (pass or fail).
if sub.failed {
t.failed = true
fmt.Fprintf(&t.output, sub.indent+"--- FAIL: %s\n", sub.name)
t.output.Write(sub.output.Bytes())
} else {
if flagVerbose {
fmt.Fprintf(&t.output, sub.indent+"--- PASS: %s\n", sub.name)
t.output.Write(sub.output.Bytes())
}
}
return !sub.failed
}
// InternalTest is a reference to a test that should be called during a test suite run.
type InternalTest struct {
Name string
F func(*T)
}
// M is a test suite.
type M struct {
// tests is a list of the test names to execute
Tests []InternalTest
}
// Run the test suite.
func (m *M) Run() int {
if len(m.Tests) == 0 {
fmt.Fprintln(os.Stderr, "testing: warning: no tests to run")
}
if !flag.Parsed() {
flag.Parse()
}
failures := 0
for _, test := range m.Tests {
t := &T{
common: common{
name: test.Name,
},
}
if flagVerbose {
fmt.Printf("=== RUN %s\n", test.Name)
}
test.F(t)
if t.failed {
fmt.Printf("--- FAIL: %s\n", test.Name)
os.Stdout.Write(t.output.Bytes())
} else {
if flagVerbose {
fmt.Printf("--- PASS: %s\n", test.Name)
os.Stdout.Write(t.output.Bytes())
}
}
if t.failed {
failures++
}
}
if failures > 0 {
fmt.Println("FAIL")
} else {
fmt.Println("PASS")
}
return failures
}
// Short reports whether the -test.short flag is set.
func Short() bool {
return flagShort
}
func TestMain(m *M) {
os.Exit(m.Run())
}
func MainStart(deps interface{}, tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) *M {
Init()
return &M{
Tests: tests,
}
}
type InternalExample struct {
Name string
F func()
Output string
Unordered bool
}