testing: switch to upstream version

This commit is contained in:
Ayke van Laethem
2025-08-04 11:34:38 +02:00
parent aedaf7d925
commit 91b0bbaae6
16 changed files with 25 additions and 2299 deletions
+1 -2
View File
@@ -363,7 +363,6 @@ TEST_PACKAGES_FAST = \
path \
reflect \
sync \
testing \
testing/iotest \
text/scanner \
unicode \
@@ -480,7 +479,7 @@ TEST_PACKAGES_HOST := $(TEST_PACKAGES_FAST) $(TEST_PACKAGES_WINDOWS)
TEST_IOFS := false
endif
TEST_SKIP_FLAG := -skip='TestExtraMethods|TestParseAndBytesRoundTrip/P256/Generic'
TEST_SKIP_FLAG := -skip='TestExtraMethods|TestParseAndBytesRoundTrip/P256/Generic|^Fuzz'
# Test known-working standard library packages.
# TODO: parallelize, and only show failing tests (no implied -v flag).
-1
View File
@@ -256,7 +256,6 @@ func pathsToOverride(goMinor int, needsSyscallPackage bool) map[string]bool {
"reflect/": false,
"runtime/": false,
"sync/": true,
"testing/": true,
"tinygo/": false,
"unique/": false,
}
+3 -1
View File
@@ -30,7 +30,9 @@ func SetPanicOnFault(enabled bool) bool {
func WriteHeapDump(fd uintptr)
func SetTraceback(level string)
// Unimplemented.
func SetTraceback(level string) {
}
func SetMemoryLimit(limit int64) int64 {
return limit
+5
View File
@@ -1,6 +1,11 @@
package runtime
func Callers(skip int, pc []uintptr) int {
if len(pc) > 0 {
// The testing package expects at least one caller in all cases.
pc[0] = 0
return 1
}
return 0
}
+16
View File
@@ -181,3 +181,19 @@ func getAuxv() []uintptr {
func cgo_errno() uintptr {
return uintptr(*libc_errno_location())
}
// Unimplemented.
var MemProfileRate int = 0
// Unimplemented.
func SetBlockProfileRate(rate int) {
}
var mutexProfileFraction int
// Unimplemented.
func SetMutexProfileFraction(rate int) int {
previous := mutexProfileFraction
mutexProfileFraction = rate
return previous
}
-521
View File
@@ -1,521 +0,0 @@
// 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.
package testing
import (
"flag"
"fmt"
"io"
"math"
"os"
"runtime"
"strconv"
"strings"
"time"
)
func initBenchmarkFlags() {
matchBenchmarks = flag.String("test.bench", "", "run only benchmarks matching `regexp`")
benchmarkMemory = flag.Bool("test.benchmem", false, "print memory allocations for benchmarks")
flag.Var(&benchTime, "test.benchtime", "run each benchmark for duration `d`")
}
var (
matchBenchmarks *string
benchmarkMemory *bool
benchTime = benchTimeFlag{d: 1 * time.Second} // changed during test of testing package
)
type benchTimeFlag struct {
d time.Duration
n int
}
func (f *benchTimeFlag) String() string {
if f.n > 0 {
return fmt.Sprintf("%dx", f.n)
}
return time.Duration(f.d).String()
}
func (f *benchTimeFlag) Set(s string) error {
if strings.HasSuffix(s, "x") {
n, err := strconv.ParseInt(s[:len(s)-1], 10, 0)
if err != nil || n <= 0 {
return fmt.Errorf("invalid count")
}
*f = benchTimeFlag{n: int(n)}
return nil
}
d, err := time.ParseDuration(s)
if err != nil || d <= 0 {
return fmt.Errorf("invalid duration")
}
*f = benchTimeFlag{d: d}
return nil
}
// InternalBenchmark is an internal type but exported because it is cross-package;
// it is part of the implementation of the "go test" command.
type InternalBenchmark struct {
Name string
F func(b *B)
}
// B is a type passed to Benchmark functions to manage benchmark
// timing and to specify the number of iterations to run.
//
// A benchmark ends when its Benchmark function returns or calls any of the methods
// FailNow, Fatal, Fatalf, SkipNow, Skip, or Skipf. Those methods must be called
// only from the goroutine running the Benchmark function.
// The other reporting methods, such as the variations of Log and Error,
// may be called simultaneously from multiple goroutines.
//
// Like in tests, benchmark logs are accumulated during execution
// and dumped to standard output when done. Unlike in tests, benchmark logs
// are always printed, so as not to hide output whose existence may be
// affecting benchmark results.
type B struct {
common
context *benchContext
N int
benchFunc func(b *B)
bytes int64
missingBytes bool // one of the subbenchmarks does not have bytes set.
benchTime benchTimeFlag
timerOn bool
result BenchmarkResult
// report memory statistics
showAllocResult bool
// initial state of MemStats.Mallocs and MemStats.TotalAlloc
startAllocs uint64
startBytes uint64
// net total after running benchmar
netAllocs uint64
netBytes uint64
}
// StartTimer starts timing a test. This function is called automatically
// before a benchmark starts, but it can also be used to resume timing after
// a call to StopTimer.
func (b *B) StartTimer() {
if !b.timerOn {
b.start = time.Now()
b.timerOn = true
var mstats runtime.MemStats
runtime.ReadMemStats(&mstats)
b.startAllocs = mstats.Mallocs
b.startBytes = mstats.TotalAlloc
}
}
// StopTimer stops timing a test. This can be used to pause the timer
// while performing complex initialization that you don't
// want to measure.
func (b *B) StopTimer() {
if b.timerOn {
b.duration += time.Since(b.start)
b.timerOn = false
var mstats runtime.MemStats
runtime.ReadMemStats(&mstats)
b.netAllocs += mstats.Mallocs - b.startAllocs
b.netBytes += mstats.TotalAlloc - b.startBytes
}
}
// ResetTimer zeroes the elapsed benchmark time and memory allocation counters
// and deletes user-reported metrics.
func (b *B) ResetTimer() {
if b.timerOn {
b.start = time.Now()
var mstats runtime.MemStats
runtime.ReadMemStats(&mstats)
b.startAllocs = mstats.Mallocs
b.startBytes = mstats.TotalAlloc
}
b.duration = 0
b.netAllocs = 0
b.netBytes = 0
}
// SetBytes records the number of bytes processed in a single operation.
// If this is called, the benchmark will report ns/op and MB/s.
func (b *B) SetBytes(n int64) { b.bytes = n }
// ReportAllocs enables malloc statistics for this benchmark.
// It is equivalent to setting -test.benchmem, but it only affects the
// benchmark function that calls ReportAllocs.
func (b *B) ReportAllocs() {
b.showAllocResult = true
}
// runN runs a single benchmark for the specified number of iterations.
func (b *B) runN(n int) {
b.N = n
runtime.GC()
b.ResetTimer()
b.StartTimer()
b.benchFunc(b)
b.StopTimer()
}
func min(x, y int64) int64 {
if x > y {
return y
}
return x
}
func max(x, y int64) int64 {
if x < y {
return y
}
return x
}
// run1 runs the first iteration of benchFunc. It reports whether more
// iterations of this benchmarks should be run.
func (b *B) run1() bool {
if ctx := b.context; ctx != nil {
// Extend maxLen, if needed.
if n := len(b.name); n > ctx.maxLen {
ctx.maxLen = n + 8 // Add additional slack to avoid too many jumps in size.
}
}
b.runN(1)
return !b.hasSub
}
// run executes the benchmark.
func (b *B) run() {
if b.context != nil {
// Running go test --test.bench
b.processBench(b.context) // calls doBench and prints results
} else {
// Running func Benchmark.
b.doBench()
}
}
func (b *B) doBench() BenchmarkResult {
// in upstream, this uses a goroutine
b.launch()
return b.result
}
// launch launches the benchmark function. It gradually increases the number
// of benchmark iterations until the benchmark runs for the requested benchtime.
// run1 must have been called on b.
func (b *B) launch() {
// Run the benchmark for at least the specified amount of time.
if b.benchTime.n > 0 {
b.runN(b.benchTime.n)
} else {
d := b.benchTime.d
b.failed = false
b.duration = 0
for n := int64(1); !b.failed && b.duration < d && n < 1e9; {
last := n
// Predict required iterations.
goalns := d.Nanoseconds()
prevIters := int64(b.N)
prevns := b.duration.Nanoseconds()
if prevns <= 0 {
// Round up, to avoid div by zero.
prevns = 1
}
// Order of operations matters.
// For very fast benchmarks, prevIters ~= prevns.
// If you divide first, you get 0 or 1,
// which can hide an order of magnitude in execution time.
// So multiply first, then divide.
n = goalns * prevIters / prevns
// Run more iterations than we think we'll need (1.2x).
n += n / 5
// Don't grow too fast in case we had timing errors previously.
n = min(n, 100*last)
// Be sure to run at least one more than last time.
n = max(n, last+1)
// Don't run more than 1e9 times. (This also keeps n in int range on 32 bit platforms.)
n = min(n, 1e9)
b.runN(int(n))
}
}
b.result = BenchmarkResult{b.N, b.duration, b.bytes, b.netAllocs, b.netBytes}
}
// BenchmarkResult contains the results of a benchmark run.
type BenchmarkResult struct {
N int // The number of iterations.
T time.Duration // The total time taken.
Bytes int64 // Bytes processed in one iteration.
MemAllocs uint64 // The total number of memory allocations.
MemBytes uint64 // The total number of bytes allocated.
}
// NsPerOp returns the "ns/op" metric.
func (r BenchmarkResult) NsPerOp() int64 {
if r.N <= 0 {
return 0
}
return r.T.Nanoseconds() / int64(r.N)
}
// mbPerSec returns the "MB/s" metric.
func (r BenchmarkResult) mbPerSec() float64 {
if r.Bytes <= 0 || r.T <= 0 || r.N <= 0 {
return 0
}
return (float64(r.Bytes) * float64(r.N) / 1e6) / r.T.Seconds()
}
// AllocsPerOp returns the "allocs/op" metric,
// which is calculated as r.MemAllocs / r.N.
func (r BenchmarkResult) AllocsPerOp() int64 {
if r.N <= 0 {
return 0
}
return int64(r.MemAllocs) / int64(r.N)
}
// AllocedBytesPerOp returns the "B/op" metric,
// which is calculated as r.MemBytes / r.N.
func (r BenchmarkResult) AllocedBytesPerOp() int64 {
if r.N <= 0 {
return 0
}
return int64(r.MemBytes) / int64(r.N)
}
// String returns a summary of the benchmark results.
// It follows the benchmark result line format from
// https://golang.org/design/14313-benchmark-format, not including the
// benchmark name.
// Extra metrics override built-in metrics of the same name.
// String does not include allocs/op or B/op, since those are reported
// by MemString.
func (r BenchmarkResult) String() string {
buf := new(strings.Builder)
fmt.Fprintf(buf, "%8d", r.N)
// Get ns/op as a float.
ns := float64(r.T.Nanoseconds()) / float64(r.N)
if ns != 0 {
buf.WriteByte('\t')
prettyPrint(buf, ns, "ns/op")
}
if mbs := r.mbPerSec(); mbs != 0 {
fmt.Fprintf(buf, "\t%7.2f MB/s", mbs)
}
return buf.String()
}
// MemString returns r.AllocedBytesPerOp and r.AllocsPerOp in the same format as 'go test'.
func (r BenchmarkResult) MemString() string {
return fmt.Sprintf("%8d B/op\t%8d allocs/op",
r.AllocedBytesPerOp(), r.AllocsPerOp())
}
func prettyPrint(w io.Writer, x float64, unit string) {
// Print all numbers with 10 places before the decimal point
// and small numbers with four sig figs. Field widths are
// chosen to fit the whole part in 10 places while aligning
// the decimal point of all fractional formats.
var format string
switch y := math.Abs(x); {
case y == 0 || y >= 999.95:
format = "%10.0f %s"
case y >= 99.995:
format = "%12.1f %s"
case y >= 9.9995:
format = "%13.2f %s"
case y >= 0.99995:
format = "%14.3f %s"
case y >= 0.099995:
format = "%15.4f %s"
case y >= 0.0099995:
format = "%16.5f %s"
case y >= 0.00099995:
format = "%17.6f %s"
default:
format = "%18.7f %s"
}
fmt.Fprintf(w, format, x, unit)
}
type benchContext struct {
match *matcher
maxLen int // The largest recorded benchmark name.
}
func runBenchmarks(matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark) bool {
// If no flag was specified, don't run benchmarks.
if len(*matchBenchmarks) == 0 {
return true
}
ctx := &benchContext{
match: newMatcher(matchString, *matchBenchmarks, "-test.bench", flagSkipRegexp),
}
var bs []InternalBenchmark
for _, Benchmark := range benchmarks {
if _, matched, _ := ctx.match.fullName(nil, Benchmark.Name); matched {
bs = append(bs, Benchmark)
benchName := Benchmark.Name
if l := len(benchName); l > ctx.maxLen {
ctx.maxLen = l
}
}
}
main := &B{
common: common{
output: &logger{},
name: "Main",
},
benchTime: benchTime,
benchFunc: func(b *B) {
for _, Benchmark := range bs {
b.Run(Benchmark.Name, Benchmark.F)
}
},
context: ctx,
}
main.runN(1)
return true
}
// processBench runs bench b and prints the results.
func (b *B) processBench(ctx *benchContext) {
benchName := b.name
for i := 0; i < flagCount; i++ {
if ctx != nil {
fmt.Printf("%-*s\t", ctx.maxLen, benchName)
}
r := b.doBench()
if b.failed {
// The output could be very long here, but probably isn't.
// We print it all, regardless, because we don't want to trim the reason
// the benchmark failed.
fmt.Printf("--- FAIL: %s\n%s", benchName, "") // b.output)
return
}
if ctx != nil {
results := r.String()
if *benchmarkMemory || b.showAllocResult {
results += "\t" + r.MemString()
}
fmt.Println(results)
// Print any benchmark output
if b.output.Len() > 0 {
fmt.Printf("--- BENCH: %s\n", benchName)
b.output.WriteTo(os.Stdout)
}
}
}
}
// Run benchmarks f as a subbenchmark with the given name. It reports
// true if the subbenchmark succeeded.
//
// A subbenchmark is like any other benchmark. A benchmark that calls Run at
// least once will not be measured itself and will be called once with N=1.
func (b *B) Run(name string, f func(b *B)) bool {
benchName, ok, partial := b.name, true, false
if b.context != nil {
benchName, ok, partial = b.context.match.fullName(&b.common, name)
}
if !ok {
return true
}
b.hasSub = true
sub := &B{
common: common{
output: &logger{},
name: benchName,
level: b.level + 1,
},
benchFunc: f,
benchTime: b.benchTime,
context: b.context,
}
if partial {
// Partial name match, like -bench=X/Y matching BenchmarkX.
// Only process sub-benchmarks, if any.
sub.hasSub = true
}
if sub.run1() {
sub.run()
}
b.add(sub.result)
return !sub.failed
}
// add simulates running benchmarks in sequence in a single iteration. It is
// used to give some meaningful results in case func Benchmark is used in
// combination with Run.
func (b *B) add(other BenchmarkResult) {
r := &b.result
// The aggregated BenchmarkResults resemble running all subbenchmarks as
// in sequence in a single benchmark.
r.N = 1
r.T += time.Duration(other.NsPerOp())
if other.Bytes == 0 {
// Summing Bytes is meaningless in aggregate if not all subbenchmarks
// set it.
b.missingBytes = true
r.Bytes = 0
}
if !b.missingBytes {
r.Bytes += other.Bytes
}
}
// A PB is used by RunParallel for running parallel benchmarks.
type PB struct {
}
// Next reports whether there are more iterations to execute.
func (pb *PB) Next() bool {
return false
}
// RunParallel runs a benchmark in parallel.
//
// Not implemented
func (b *B) RunParallel(body func(*PB)) {
return
}
func (b *B) Loop() bool {
panic("unimplemented: testing.B.Loop")
}
// Benchmark benchmarks a single function. It is useful for creating
// custom benchmarks that do not use the "go test" command.
//
// If f calls Run, the result will be an estimate of running all its
// subbenchmarks that don't call Run in sequence in a single benchmark.
func Benchmark(f func(b *B)) BenchmarkResult {
b := &B{
benchFunc: f,
benchTime: benchTime,
}
if b.run1() {
b.run()
}
return b.result
}
-55
View File
@@ -1,55 +0,0 @@
// Copyright 2011 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.
package testing_test
import (
"testing"
)
var buf = make([]byte, 13579)
func NonASCII(b []byte, i int, offset int) int {
for i = offset; i < len(b)+offset; i++ {
if b[i%len(b)] >= 0x80 {
break
}
}
return i
}
func BenchmarkFastNonASCII(b *testing.B) {
var val int
for i := 0; i < b.N; i++ {
val += NonASCII(buf, 0, 0)
}
}
func BenchmarkSlowNonASCII(b *testing.B) {
var val int
for i := 0; i < b.N; i++ {
val += NonASCII(buf, 0, 0)
val += NonASCII(buf, 0, 1)
}
}
// TestBenchmark simply uses Benchmark twice and makes sure it does not crash.
func TestBenchmark(t *testing.T) {
// FIXME: reduce runtime from the current 3 seconds.
rslow := testing.Benchmark(BenchmarkSlowNonASCII)
rfast := testing.Benchmark(BenchmarkFastNonASCII)
tslow := rslow.NsPerOp()
tfast := rfast.NsPerOp()
// Be exceedingly forgiving; do not fail even if system gets busy.
speedup := float64(tslow) / float64(tfast)
if speedup < 0.3 {
t.Errorf("Expected speedup >= 0.3, got %f", speedup)
}
}
func BenchmarkSub(b *testing.B) {
b.Run("Fast", func(b *testing.B) { BenchmarkFastNonASCII(b) })
b.Run("Slow", func(b *testing.B) { BenchmarkSlowNonASCII(b) })
}
-6
View File
@@ -1,6 +0,0 @@
package testing
/*
This is a sad stub of the upstream testing package because it doesn't compile
with tinygo right now.
*/
-143
View File
@@ -1,143 +0,0 @@
package testing
import (
"errors"
"fmt"
"reflect"
"time"
)
// InternalFuzzTarget is an internal type but exported because it is
// cross-package; it is part of the implementation of the "go test" command.
type InternalFuzzTarget struct {
Name string
Fn func(f *F)
}
// F is a type passed to fuzz tests.
//
// Fuzz tests run generated inputs against a provided fuzz target, which can
// find and report potential bugs in the code being tested.
//
// A fuzz test runs the seed corpus by default, which includes entries provided
// by (*F).Add and entries in the testdata/fuzz/<FuzzTestName> directory. After
// any necessary setup and calls to (*F).Add, the fuzz test must then call
// (*F).Fuzz to provide the fuzz target. See the testing package documentation
// for an example, and see the F.Fuzz and F.Add method documentation for
// details.
//
// *F methods can only be called before (*F).Fuzz. Once the test is
// executing the fuzz target, only (*T) methods can be used. The only *F methods
// that are allowed in the (*F).Fuzz function are (*F).Failed and (*F).Name.
type F struct {
common
fuzzContext *fuzzContext
testContext *testContext
// inFuzzFn is true when the fuzz function is running. Most F methods cannot
// be called when inFuzzFn is true.
inFuzzFn bool
// corpus is a set of seed corpus entries, added with F.Add and loaded
// from testdata.
corpus []corpusEntry
result fuzzResult
fuzzCalled bool
}
// corpusEntry is an alias to the same type as internal/fuzz.CorpusEntry.
// We use a type alias because we don't want to export this type, and we can't
// import internal/fuzz from testing.
type corpusEntry = struct {
Parent string
Path string
Data []byte
Values []interface{}
Generation int
IsSeed bool
}
// Add will add the arguments to the seed corpus for the fuzz test. This will be
// a no-op if called after or within the fuzz target, and args must match the
// arguments for the fuzz target.
func (f *F) Add(args ...interface{}) {
var values []interface{}
for i := range args {
if t := reflect.TypeOf(args[i]); !supportedTypes[t] {
panic(fmt.Sprintf("testing: unsupported type to Add %v", t))
}
values = append(values, args[i])
}
f.corpus = append(f.corpus, corpusEntry{Values: values, IsSeed: true, Path: fmt.Sprintf("seed#%d", len(f.corpus))})
}
// supportedTypes represents all of the supported types which can be fuzzed.
var supportedTypes = map[reflect.Type]bool{
reflect.TypeOf(([]byte)("")): true,
reflect.TypeOf((string)("")): true,
reflect.TypeOf((bool)(false)): true,
reflect.TypeOf((byte)(0)): true,
reflect.TypeOf((rune)(0)): true,
reflect.TypeOf((float32)(0)): true,
reflect.TypeOf((float64)(0)): true,
reflect.TypeOf((int)(0)): true,
reflect.TypeOf((int8)(0)): true,
reflect.TypeOf((int16)(0)): true,
reflect.TypeOf((int32)(0)): true,
reflect.TypeOf((int64)(0)): true,
reflect.TypeOf((uint)(0)): true,
reflect.TypeOf((uint8)(0)): true,
reflect.TypeOf((uint16)(0)): true,
reflect.TypeOf((uint32)(0)): true,
reflect.TypeOf((uint64)(0)): true,
}
// Fuzz runs the fuzz function, ff, for fuzz testing. If ff fails for a set of
// arguments, those arguments will be added to the seed corpus.
//
// ff must be a function with no return value whose first argument is *T and
// whose remaining arguments are the types to be fuzzed.
// For example:
//
// f.Fuzz(func(t *testing.T, b []byte, i int) { ... })
//
// The following types are allowed: []byte, string, bool, byte, rune, float32,
// float64, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64.
// More types may be supported in the future.
//
// ff must not call any *F methods, e.g. (*F).Log, (*F).Error, (*F).Skip. Use
// the corresponding *T method instead. The only *F methods that are allowed in
// the (*F).Fuzz function are (*F).Failed and (*F).Name.
//
// This function should be fast and deterministic, and its behavior should not
// depend on shared state. No mutatable input arguments, or pointers to them,
// should be retained between executions of the fuzz function, as the memory
// backing them may be mutated during a subsequent invocation. ff must not
// modify the underlying data of the arguments provided by the fuzzing engine.
//
// When fuzzing, F.Fuzz does not return until a problem is found, time runs out
// (set with -fuzztime), or the test process is interrupted by a signal. F.Fuzz
// should be called exactly once, unless F.Skip or F.Fail is called beforehand.
func (f *F) Fuzz(ff interface{}) {
f.failed = true
f.result.N = 0
f.result.T = 0
f.result.Error = errors.New("operation not implemented")
return
}
// fuzzContext holds fields common to all fuzz tests.
type fuzzContext struct {
deps testDeps
mode fuzzMode
}
type fuzzMode uint8
// fuzzResult contains the results of a fuzz run.
type fuzzResult struct {
N int // The number of iterations.
T time.Duration // The total time taken.
Error error // Error is the error from the failing input
}
-9
View File
@@ -1,9 +0,0 @@
// Copyright 2016 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.
//
//go:build baremetal
package testing
const isBaremetal = true
-9
View File
@@ -1,9 +0,0 @@
// Copyright 2016 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.
//
//go:build !baremetal
package testing
const isBaremetal = false
-323
View File
@@ -1,323 +0,0 @@
// Copyright 2015 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.
package testing
import (
"fmt"
"os"
"strconv"
"strings"
"sync"
)
// matcher sanitizes, uniques, and filters names of subtests and subbenchmarks.
type matcher struct {
filter filterMatch
skip filterMatch
matchFunc func(pat, str string) (bool, error)
mu sync.Mutex
// subNames is used to deduplicate subtest names.
// Each key is the subtest name joined to the deduplicated name of the parent test.
// Each value is the count of the number of occurrences of the given subtest name
// already seen.
subNames map[string]int32
}
type filterMatch interface {
// matches checks the name against the receiver's pattern strings using the
// given match function.
matches(name []string, matchString func(pat, str string) (bool, error)) (ok, partial bool)
// verify checks that the receiver's pattern strings are valid filters by
// calling the given match function.
verify(name string, matchString func(pat, str string) (bool, error)) error
}
// simpleMatch matches a test name if all of the pattern strings match in
// sequence.
type simpleMatch []string
// alternationMatch matches a test name if one of the alternations match.
type alternationMatch []filterMatch
// TODO: fix test_main to avoid race and improve caching, also allowing to
// eliminate this Mutex.
var matchMutex sync.Mutex
func allMatcher() *matcher {
return newMatcher(nil, "", "", "")
}
func newMatcher(matchString func(pat, str string) (bool, error), patterns, name, skips string) *matcher {
if isBaremetal {
matchString = fakeMatchString
}
var filter, skip filterMatch
if patterns == "" {
filter = simpleMatch{} // always partial true
} else {
filter = splitRegexp(patterns)
if err := filter.verify(name, matchString); err != nil {
fmt.Fprintf(os.Stderr, "testing: invalid regexp for %s\n", err)
os.Exit(1)
}
}
if skips == "" {
skip = alternationMatch{} // always false
} else {
skip = splitRegexp(skips)
if err := skip.verify("-test.skip", matchString); err != nil {
fmt.Fprintf(os.Stderr, "testing: invalid regexp for %v\n", err)
os.Exit(1)
}
}
return &matcher{
filter: filter,
skip: skip,
matchFunc: matchString,
subNames: map[string]int32{},
}
}
func (m *matcher) fullName(c *common, subname string) (name string, ok, partial bool) {
name = subname
m.mu.Lock()
defer m.mu.Unlock()
if c != nil && c.level > 0 {
name = m.unique(c.name, rewrite(subname))
}
matchMutex.Lock()
defer matchMutex.Unlock()
// We check the full array of paths each time to allow for the case that a pattern contains a '/'.
elem := strings.Split(name, "/")
// filter must match.
// accept partial match that may produce full match later.
ok, partial = m.filter.matches(elem, m.matchFunc)
if !ok {
return name, false, false
}
// skip must not match.
// ignore partial match so we can get to more precise match later.
skip, partialSkip := m.skip.matches(elem, m.matchFunc)
if skip && !partialSkip {
return name, false, false
}
return name, ok, partial
}
// clearSubNames clears the matcher's internal state, potentially freeing
// memory. After this is called, T.Name may return the same strings as it did
// for earlier subtests.
func (m *matcher) clearSubNames() {
m.mu.Lock()
defer m.mu.Unlock()
for key := range m.subNames {
delete(m.subNames, key)
}
}
func (m simpleMatch) matches(name []string, matchString func(pat, str string) (bool, error)) (ok, partial bool) {
for i, s := range name {
if i >= len(m) {
break
}
if ok, _ := matchString(m[i], s); !ok {
return false, false
}
}
return true, len(name) < len(m)
}
func (m simpleMatch) verify(name string, matchString func(pat, str string) (bool, error)) error {
for i, s := range m {
m[i] = rewrite(s)
}
// Verify filters before doing any processing.
for i, s := range m {
if _, err := matchString(s, "non-empty"); err != nil {
return fmt.Errorf("element %d of %s (%q): %s", i, name, s, err)
}
}
return nil
}
func (m alternationMatch) matches(name []string, matchString func(pat, str string) (bool, error)) (ok, partial bool) {
for _, m := range m {
if ok, partial = m.matches(name, matchString); ok {
return ok, partial
}
}
return false, false
}
func (m alternationMatch) verify(name string, matchString func(pat, str string) (bool, error)) error {
for i, m := range m {
if err := m.verify(name, matchString); err != nil {
return fmt.Errorf("alternation %d of %s", i, err)
}
}
return nil
}
func splitRegexp(s string) filterMatch {
a := make(simpleMatch, 0, strings.Count(s, "/"))
b := make(alternationMatch, 0, strings.Count(s, "|"))
cs := 0
cp := 0
for i := 0; i < len(s); {
switch s[i] {
case '[':
cs++
case ']':
if cs--; cs < 0 { // An unmatched ']' is legal.
cs = 0
}
case '(':
if cs == 0 {
cp++
}
case ')':
if cs == 0 {
cp--
}
case '\\':
i++
case '/':
if cs == 0 && cp == 0 {
a = append(a, s[:i])
s = s[i+1:]
i = 0
continue
}
case '|':
if cs == 0 && cp == 0 {
a = append(a, s[:i])
s = s[i+1:]
i = 0
b = append(b, a)
a = make(simpleMatch, 0, len(a))
continue
}
}
i++
}
a = append(a, s)
if len(b) == 0 {
return a
}
return append(b, a)
}
// unique creates a unique name for the given parent and subname by affixing it
// with one or more counts, if necessary.
func (m *matcher) unique(parent, subname string) string {
base := parent + "/" + subname
for {
n := m.subNames[base]
if n < 0 {
panic("subtest count overflow")
}
m.subNames[base] = n + 1
if n == 0 && subname != "" {
prefix, nn := parseSubtestNumber(base)
if len(prefix) < len(base) && nn < m.subNames[prefix] {
// This test is explicitly named like "parent/subname#NN",
// and #NN was already used for the NNth occurrence of "parent/subname".
// Loop to add a disambiguating suffix.
continue
}
return base
}
name := fmt.Sprintf("%s#%02d", base, n)
if m.subNames[name] != 0 {
// This is the nth occurrence of base, but the name "parent/subname#NN"
// collides with the first occurrence of a subtest *explicitly* named
// "parent/subname#NN". Try the next number.
continue
}
return name
}
}
// parseSubtestNumber splits a subtest name into a "#%02d"-formatted int32
// suffix (if present), and a prefix preceding that suffix (always).
func parseSubtestNumber(s string) (prefix string, nn int32) {
i := strings.LastIndex(s, "#")
if i < 0 {
return s, 0
}
prefix, suffix := s[:i], s[i+1:]
if len(suffix) < 2 || (len(suffix) > 2 && suffix[0] == '0') {
// Even if suffix is numeric, it is not a possible output of a "%02" format
// string: it has either too few digits or too many leading zeroes.
return s, 0
}
if suffix == "00" {
if !strings.HasSuffix(prefix, "/") {
// We only use "#00" as a suffix for subtests named with the empty
// string — it isn't a valid suffix if the subtest name is non-empty.
return s, 0
}
}
n, err := strconv.ParseInt(suffix, 10, 32)
if err != nil || n < 0 {
return s, 0
}
return prefix, int32(n)
}
// rewrite rewrites a subname to having only printable characters and no white
// space.
func rewrite(s string) string {
b := []byte{}
for _, r := range s {
switch {
case isSpace(r):
b = append(b, '_')
case !strconv.IsPrint(r):
s := strconv.QuoteRune(r)
b = append(b, s[1:len(s)-1]...)
default:
b = append(b, string(r)...)
}
}
return string(b)
}
func isSpace(r rune) bool {
if r < 0x2000 {
switch r {
// Note: not the same as Unicode Z class.
case '\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xA0, 0x1680:
return true
}
} else {
if r <= 0x200a {
return true
}
switch r {
case 0x2028, 0x2029, 0x202f, 0x205f, 0x3000:
return true
}
}
return false
}
-259
View File
@@ -1,259 +0,0 @@
// Copyright 2015 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.
package testing
import (
"fmt"
"reflect"
"regexp"
"strings"
"unicode"
)
// Verify that our IsSpace agrees with unicode.IsSpace.
func TestIsSpace(t *T) {
n := 0
for r := rune(0); r <= unicode.MaxRune; r++ {
if isSpace(r) != unicode.IsSpace(r) {
t.Errorf("IsSpace(%U)=%t incorrect", r, isSpace(r))
n++
if n > 10 {
return
}
}
}
}
func TestSplitRegexp(t *T) {
res := func(s ...string) filterMatch { return simpleMatch(s) }
alt := func(m ...filterMatch) filterMatch { return alternationMatch(m) }
testCases := []struct {
pattern string
result filterMatch
}{
// Correct patterns
// If a regexp pattern is correct, all split regexps need to be correct
// as well.
{"", res("")},
{"/", res("", "")},
{"//", res("", "", "")},
{"A", res("A")},
{"A/B", res("A", "B")},
{"A/B/", res("A", "B", "")},
{"/A/B/", res("", "A", "B", "")},
{"[A]/(B)", res("[A]", "(B)")},
{"[/]/[/]", res("[/]", "[/]")},
{"[/]/[:/]", res("[/]", "[:/]")},
{"/]", res("", "]")},
{"]/", res("]", "")},
{"]/[/]", res("]", "[/]")},
{`([)/][(])`, res(`([)/][(])`)},
{"[(]/[)]", res("[(]", "[)]")},
{"A/B|C/D", alt(res("A", "B"), res("C", "D"))},
// Faulty patterns
// Errors in original should produce at least one faulty regexp in results.
{")/", res(")/")},
{")/(/)", res(")/(", ")")},
{"a[/)b", res("a[/)b")},
{"(/]", res("(/]")},
{"(/", res("(/")},
{"[/]/[/", res("[/]", "[/")},
{`\p{/}`, res(`\p{`, "}")},
{`\p/`, res(`\p`, "")},
{`[[:/:]]`, res(`[[:/:]]`)},
}
for _, tc := range testCases {
a := splitRegexp(tc.pattern)
if !reflect.DeepEqual(a, tc.result) {
t.Errorf("splitRegexp(%q) = %#v; want %#v", tc.pattern, a, tc.result)
}
// If there is any error in the pattern, one of the returned subpatterns
// needs to have an error as well.
if _, err := regexp.Compile(tc.pattern); err != nil {
ok := true
if err := a.verify("", regexp.MatchString); err != nil {
ok = false
}
if ok {
t.Errorf("%s: expected error in any of %q", tc.pattern, a)
}
}
}
}
func TestMatcher(t *T) {
testCases := []struct {
pattern string
skip string
parent, sub string
ok bool
partial bool
}{
// Behavior without subtests.
{"", "", "", "TestFoo", true, false},
{"TestFoo", "", "", "TestFoo", true, false},
{"TestFoo/", "", "", "TestFoo", true, true},
{"TestFoo/bar/baz", "", "", "TestFoo", true, true},
{"TestFoo", "", "", "TestBar", false, false},
{"TestFoo/", "", "", "TestBar", false, false},
{"TestFoo/bar/baz", "", "", "TestBar/bar/baz", false, false},
{"", "TestBar", "", "TestFoo", true, false},
{"", "TestBar", "", "TestBar", false, false},
// Skipping a non-existent test doesn't change anything.
{"", "TestFoo/skipped", "", "TestFoo", true, false},
{"TestFoo", "TestFoo/skipped", "", "TestFoo", true, false},
{"TestFoo/", "TestFoo/skipped", "", "TestFoo", true, true},
{"TestFoo/bar/baz", "TestFoo/skipped", "", "TestFoo", true, true},
{"TestFoo", "TestFoo/skipped", "", "TestBar", false, false},
{"TestFoo/", "TestFoo/skipped", "", "TestBar", false, false},
{"TestFoo/bar/baz", "TestFoo/skipped", "", "TestBar/bar/baz", false, false},
// with subtests
{"", "", "TestFoo", "x", true, false},
{"TestFoo", "", "TestFoo", "x", true, false},
{"TestFoo/", "", "TestFoo", "x", true, false},
{"TestFoo/bar/baz", "", "TestFoo", "bar", true, true},
{"", "TestFoo/skipped", "TestFoo", "x", true, false},
{"TestFoo", "TestFoo/skipped", "TestFoo", "x", true, false},
{"TestFoo", "TestFoo/skipped", "TestFoo", "skipped", false, false},
{"TestFoo/", "TestFoo/skipped", "TestFoo", "x", true, false},
{"TestFoo/bar/baz", "TestFoo/skipped", "TestFoo", "bar", true, true},
// Subtest with a '/' in its name still allows for copy and pasted names
// to match.
{"TestFoo/bar/baz", "", "TestFoo", "bar/baz", true, false},
{"TestFoo/bar/baz", "TestFoo/bar/baz", "TestFoo", "bar/baz", false, false},
{"TestFoo/bar/baz", "TestFoo/bar/baz/skip", "TestFoo", "bar/baz", true, false},
{"TestFoo/bar/baz", "", "TestFoo/bar", "baz", true, false},
{"TestFoo/bar/baz", "", "TestFoo", "x", false, false},
{"TestFoo", "", "TestBar", "x", false, false},
{"TestFoo/", "", "TestBar", "x", false, false},
{"TestFoo/bar/baz", "", "TestBar", "x/bar/baz", false, false},
{"A/B|C/D", "", "TestA", "B", true, false},
{"A/B|C/D", "", "TestC", "D", true, false},
{"A/B|C/D", "", "TestA", "C", false, false},
// subtests only
{"", "", "TestFoo", "x", true, false},
{"/", "", "TestFoo", "x", true, false},
{"./", "", "TestFoo", "x", true, false},
{"./.", "", "TestFoo", "x", true, false},
{"/bar/baz", "", "TestFoo", "bar", true, true},
{"/bar/baz", "", "TestFoo", "bar/baz", true, false},
{"//baz", "", "TestFoo", "bar/baz", true, false},
{"//", "", "TestFoo", "bar/baz", true, false},
{"/bar/baz", "", "TestFoo/bar", "baz", true, false},
{"//foo", "", "TestFoo", "bar/baz", false, false},
{"/bar/baz", "", "TestFoo", "x", false, false},
{"/bar/baz", "", "TestBar", "x/bar/baz", false, false},
}
for _, tc := range testCases {
m := newMatcher(regexp.MatchString, tc.pattern, "-test.run", tc.skip)
parent := &common{name: tc.parent}
if tc.parent != "" {
parent.level = 1
}
if n, ok, partial := m.fullName(parent, tc.sub); ok != tc.ok || partial != tc.partial {
t.Errorf("for pattern %q, fullName(parent=%q, sub=%q) = %q, ok %v partial %v; want ok %v partial %v",
tc.pattern, tc.parent, tc.sub, n, ok, partial, tc.ok, tc.partial)
}
}
}
var namingTestCases = []struct{ name, want string }{
// Uniqueness
{"", "x/#00"},
{"", "x/#01"},
{"#0", "x/#0"}, // Doesn't conflict with #00 because the number of digits differs.
{"#00", "x/#00#01"}, // Conflicts with implicit #00 (used above), so add a suffix.
{"#", "x/#"},
{"#", "x/##01"},
{"t", "x/t"},
{"t", "x/t#01"},
{"t", "x/t#02"},
{"t#00", "x/t#00"}, // Explicit "#00" doesn't conflict with the unsuffixed first subtest.
{"a#01", "x/a#01"}, // user has subtest with this name.
{"a", "x/a"}, // doesn't conflict with this name.
{"a", "x/a#02"}, // This string is claimed now, so resume
{"a", "x/a#03"}, // with counting.
{"a#02", "x/a#02#01"}, // We already used a#02 once, so add a suffix.
{"b#00", "x/b#00"},
{"b", "x/b"}, // Implicit 0 doesn't conflict with explicit "#00".
{"b", "x/b#01"},
{"b#9223372036854775807", "x/b#9223372036854775807"}, // MaxInt64
{"b", "x/b#02"},
{"b", "x/b#03"},
// Sanitizing
{"A:1 B:2", "x/A:1_B:2"},
{"s\t\r\u00a0", "x/s___"},
{"\x01", `x/\x01`},
{"\U0010ffff", `x/\U0010ffff`},
}
func TestNaming(t *T) {
m := newMatcher(regexp.MatchString, "", "", "")
parent := &common{name: "x", level: 1} // top-level test.
for i, tc := range namingTestCases {
if got, _, _ := m.fullName(parent, tc.name); got != tc.want {
t.Errorf("%d:%s: got %q; want %q", i, tc.name, got, tc.want)
}
}
}
func FuzzNaming(f *F) {
for _, tc := range namingTestCases {
f.Add(tc.name)
}
parent := &common{name: "x", level: 1}
var m *matcher
var seen map[string]string
reset := func() {
m = allMatcher()
seen = make(map[string]string)
}
reset()
f.Fuzz(func(t *T, subname string) {
if len(subname) > 10 {
// Long names attract the OOM killer.
t.Skip()
}
name := m.unique(parent.name, subname)
if !strings.Contains(name, "/"+subname) {
t.Errorf("name %q does not contain subname %q", name, subname)
}
if prev, ok := seen[name]; ok {
t.Errorf("name %q generated by both %q and %q", name, prev, subname)
}
if len(seen) > 1e6 {
// Free up memory.
reset()
}
seen[name] = subname
})
}
// GoString returns a string that is more readable than the default, which makes
// it easier to read test errors.
func (m alternationMatch) GoString() string {
s := make([]string, len(m))
for i, m := range m {
s[i] = fmt.Sprintf("%#v", m)
}
return fmt.Sprintf("(%s)", strings.Join(s, " | "))
}
-80
View File
@@ -1,80 +0,0 @@
// Copyright 2016 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.
package testing
import (
"reflect"
)
func TestCleanup(t *T) {
var cleanups []int
t.Run("test", func(t *T) {
t.Cleanup(func() { cleanups = append(cleanups, 1) })
t.Cleanup(func() { cleanups = append(cleanups, 2) })
})
if got, want := cleanups, []int{2, 1}; !reflect.DeepEqual(got, want) {
t.Errorf("unexpected cleanup record; got %v want %v", got, want)
}
}
func TestRunCleanup(t *T) {
outerCleanup := 0
innerCleanup := 0
t.Run("test", func(t *T) {
t.Cleanup(func() { outerCleanup++ })
t.Run("x", func(t *T) {
t.Cleanup(func() { innerCleanup++ })
})
})
if innerCleanup != 1 {
t.Errorf("unexpected inner cleanup count; got %d want 1", innerCleanup)
}
if outerCleanup != 1 {
t.Errorf("unexpected outer cleanup count; got %d want 1", outerCleanup) // wrong upstream!
}
}
func TestCleanupParallelSubtests(t *T) {
ranCleanup := 0
t.Run("test", func(t *T) {
t.Cleanup(func() { ranCleanup++ })
t.Run("x", func(t *T) {
t.Parallel()
if ranCleanup > 0 {
t.Error("outer cleanup ran before parallel subtest")
}
})
})
if ranCleanup != 1 {
t.Errorf("unexpected cleanup count; got %d want 1", ranCleanup)
}
}
func TestNestedCleanup(t *T) {
ranCleanup := 0
t.Run("test", func(t *T) {
t.Cleanup(func() {
if ranCleanup != 2 {
t.Errorf("unexpected cleanup count in first cleanup: got %d want 2", ranCleanup)
}
ranCleanup++
})
t.Cleanup(func() {
if ranCleanup != 0 {
t.Errorf("unexpected cleanup count in second cleanup: got %d want 0", ranCleanup)
}
ranCleanup++
t.Cleanup(func() {
if ranCleanup != 1 {
t.Errorf("unexpected cleanup count in nested cleanup: got %d want 1", ranCleanup)
}
ranCleanup++
})
})
})
if ranCleanup != 3 {
t.Errorf("unexpected cleanup count: got %d want 3", ranCleanup)
}
}
-686
View File
@@ -1,686 +0,0 @@
// 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"
"errors"
"flag"
"fmt"
"io"
"io/fs"
"math/rand"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
"time"
"unicode"
"unicode/utf8"
)
// Testing flags.
var (
flagVerbose bool
flagShort bool
flagRunRegexp string
flagSkipRegexp string
flagShuffle string
flagCount int
)
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")
flag.StringVar(&flagSkipRegexp, "test.skip", "", "skip: regexp of tests to run")
flag.StringVar(&flagShuffle, "test.shuffle", "off", "shuffle: off, on, <numeric-seed>")
flag.IntVar(&flagCount, "test.count", 1, "run each test or benchmark `count` times")
initBenchmarkFlags()
}
// common holds the elements common between T and B and
// captures common methods such as Errorf.
type common struct {
output *logger
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.
hasSub bool // TODO: should be atomic
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
tempDir string
tempDirErr error
tempDirSeq int32
}
type logger struct {
logToStdout bool
b bytes.Buffer
}
func (l *logger) Write(p []byte) (int, error) {
if l.logToStdout {
return os.Stdout.Write(p)
}
return l.b.Write(p)
}
func (l *logger) WriteTo(w io.Writer) (int64, error) {
if l.logToStdout {
// We've already been logging to stdout; nothing to do.
return 0, nil
}
return l.b.WriteTo(w)
}
func (l *logger) Len() int {
return l.b.Len()
}
// 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
}
// String constant that is being set when running a test.
var testBinary string
// Testing returns whether the program was compiled as a test, using "tinygo
// test". It returns false when built using "tinygo build", "tinygo flash", etc.
func Testing() bool {
return testBinary == "1"
}
// flushToParent writes c.output to the parent after first writing the header
// with the given format and arguments.
func (c *common) flushToParent(testName, format string, args ...interface{}) {
if c.parent == nil {
// The fake top-level test doesn't want a FAIL or PASS banner.
// Not quite sure how this works upstream.
c.output.WriteTo(os.Stdout)
} else {
fmt.Fprintf(c.parent.output, format, args...)
c.output.WriteTo(c.parent.output)
}
}
// 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 {
Cleanup(func())
Error(args ...interface{})
Errorf(format string, args ...interface{})
Fail()
FailNow()
Failed() bool
Fatal(args ...interface{})
Fatalf(format string, args ...interface{})
Helper()
Log(args ...interface{})
Logf(format string, args ...interface{})
Name() string
Setenv(key, value string)
Skip(args ...interface{})
SkipNow()
Skipf(format string, args ...interface{})
Skipped() bool
TempDir() string
}
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
context *testContext // For running tests and subtests.
}
// Name returns the name of the running test or benchmark.
func (c *common) Name() string {
return c.name
}
func (c *common) setRan() {
if c.parent != nil {
c.parent.setRan()
}
c.ran = true
}
// 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.
fmt.Fprintf(c.output, "%s %s\n", c.indent, lines[0])
// More lines.
for _, line := range lines[1:] {
fmt.Fprintf(c.output, "%s %s\n", c.indent, line)
}
}
// 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)
}
// TempDir returns a temporary directory for the test to use.
// The directory is automatically removed by Cleanup when the test and
// all its subtests complete.
// Each subsequent call to t.TempDir returns a unique directory;
// if the directory creation fails, TempDir terminates the test by calling Fatal.
func (c *common) TempDir() string {
// Use a single parent directory for all the temporary directories
// created by a test, each numbered sequentially.
var nonExistent bool
if c.tempDir == "" { // Usually the case with js/wasm
nonExistent = true
} else {
_, err := os.Stat(c.tempDir)
nonExistent = errors.Is(err, fs.ErrNotExist)
if err != nil && !nonExistent {
c.Fatalf("TempDir: %v", err)
}
}
if nonExistent {
c.Helper()
// Drop unusual characters (such as path separators or
// characters interacting with globs) from the directory name to
// avoid surprising os.MkdirTemp behavior.
mapper := func(r rune) rune {
if r < utf8.RuneSelf {
const allowed = "!#$%&()+,-.=@^_{}~ "
if '0' <= r && r <= '9' ||
'a' <= r && r <= 'z' ||
'A' <= r && r <= 'Z' {
return r
}
if strings.ContainsRune(allowed, r) {
return r
}
} else if unicode.IsLetter(r) || unicode.IsNumber(r) {
return r
}
return -1
}
pattern := strings.Map(mapper, c.Name())
c.tempDir, c.tempDirErr = os.MkdirTemp("", pattern)
if c.tempDirErr == nil {
c.Cleanup(func() {
if err := os.RemoveAll(c.tempDir); err != nil {
c.Errorf("TempDir RemoveAll cleanup: %v", err)
}
})
}
}
if c.tempDirErr != nil {
c.Fatalf("TempDir: %v", c.tempDirErr)
}
seq := c.tempDirSeq
c.tempDirSeq++
dir := fmt.Sprintf("%s%c%03d", c.tempDir, os.PathSeparator, seq)
if err := os.Mkdir(dir, 0777); err != nil {
c.Fatalf("TempDir: %v", err)
}
return dir
}
// Setenv calls os.Setenv(key, value) and uses Cleanup to
// restore the environment variable to its original value
// after the test.
func (c *common) Setenv(key, value string) {
prevValue, ok := os.LookupEnv(key)
if err := os.Setenv(key, value); err != nil {
c.Fatalf("cannot set environment variable: %v", err)
}
if ok {
c.Cleanup(func() {
os.Setenv(key, prevValue)
})
} else {
c.Cleanup(func() {
os.Unsetenv(key)
})
}
}
// Chdir calls os.Chdir(dir) and uses Cleanup to restore the current
// working directory to its original value after the test. On Unix, it
// also sets PWD environment variable for the duration of the test.
//
// Because Chdir affects the whole process, it cannot be used
// in parallel tests or tests with parallel ancestors.
func (c *common) Chdir(dir string) {
// Note: function copied from the Go 1.24.0 source tree.
oldwd, err := os.Open(".")
if err != nil {
c.Fatal(err)
}
if err := os.Chdir(dir); err != nil {
c.Fatal(err)
}
// On POSIX platforms, PWD represents “an absolute pathname of the
// current working directory.” Since we are changing the working
// directory, we should also set or update PWD to reflect that.
switch runtime.GOOS {
case "windows", "plan9":
// Windows and Plan 9 do not use the PWD variable.
default:
if !filepath.IsAbs(dir) {
dir, err = os.Getwd()
if err != nil {
c.Fatal(err)
}
}
c.Setenv("PWD", dir)
}
c.Cleanup(func() {
err := oldwd.Chdir()
oldwd.Close()
if err != nil {
// It's not safe to continue with tests if we can't
// get back to the original working directory. Since
// we are holding a dirfd, this is highly unlikely.
panic("testing.Chdir: " + err.Error())
}
})
}
// 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 (t *T) 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.
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.
if t.parent != nil && !t.hasSub {
t.setRan()
}
}
// 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 {
t.hasSub = true
testName, ok, _ := t.context.match.fullName(&t.common, name)
if !ok {
return true
}
// Create a subtest.
sub := T{
common: common{
output: &logger{logToStdout: flagVerbose},
name: testName,
parent: &t.common,
level: t.level + 1,
},
context: t.context,
}
if t.level > 0 {
sub.indent = sub.indent + " "
}
if flagVerbose {
fmt.Fprintf(t.output, "=== RUN %s\n", sub.name)
}
tRunner(&sub, f)
return !sub.failed
}
// Deadline reports the time at which the test binary will have
// exceeded the timeout specified by the -timeout flag.
//
// The ok result is false if the -timeout flag indicates “no timeout” (0).
// For now tinygo always return 0, false.
//
// Not Implemented.
func (t *T) Deadline() (deadline time.Time, ok bool) {
deadline = t.context.deadline
return deadline, !deadline.IsZero()
}
// testContext holds all fields that are common to all tests. This includes
// synchronization primitives to run at most *parallel tests.
type testContext struct {
match *matcher
deadline time.Time
}
func newTestContext(m *matcher) *testContext {
return &testContext{
match: m,
}
}
// 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 (m *M) shuffle() error {
var n int64
if flagShuffle == "on" {
n = time.Now().UnixNano()
} else {
var err error
n, err = strconv.ParseInt(flagShuffle, 10, 64)
if err != nil {
m.exitCode = 2
return fmt.Errorf(`testing: -shuffle should be "off", "on", or a valid integer: %v`, err)
}
}
fmt.Println("-test.shuffle", n)
rng := rand.New(rand.NewSource(n))
rng.Shuffle(len(m.Tests), func(i, j int) { m.Tests[i], m.Tests[j] = m.Tests[j], m.Tests[i] })
rng.Shuffle(len(m.Benchmarks), func(i, j int) { m.Benchmarks[i], m.Benchmarks[j] = m.Benchmarks[j], m.Benchmarks[i] })
return nil
}
// 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()
}
if flagShuffle != "off" {
if err := m.shuffle(); err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
}
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 {
fmt.Println("PASS")
m.exitCode = 0
}
return
}
func runTests(matchString func(pat, str string) (bool, error), tests []InternalTest) (ran, ok bool) {
ok = true
ctx := newTestContext(newMatcher(matchString, flagRunRegexp, "-test.run", flagSkipRegexp))
t := &T{
common: common{
output: &logger{logToStdout: flagVerbose},
},
context: ctx,
}
for i := 0; i < flagCount; i++ {
tRunner(t, func(t *T) {
for _, test := range tests {
t.Run(test.Name, test.F)
ok = ok && !t.Failed()
}
})
}
return t.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
}
t.flushToParent(t.name, format, "FAIL", t.name, dstr)
} else if flagVerbose {
if t.Skipped() {
t.flushToParent(t.name, format, "SKIP", t.name, dstr)
} else {
t.flushToParent(t.name, format, "PASS", t.name, dstr)
}
}
}
// 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
}
// MainStart is meant for use by tests generated by 'go test'.
// It is not meant to be called directly and is not subject to the Go 1 compatibility document.
// It may change signature from release to release.
func MainStart(deps interface{}, tests []InternalTest, benchmarks []InternalBenchmark, fuzzTargets []InternalFuzzTarget, examples []InternalExample) *M {
Init()
return &M{
Tests: tests,
Benchmarks: benchmarks,
deps: deps.(testDeps),
}
}
// A fake regexp matcher.
// Inflexible, but saves 50KB of flash and 50KB of RAM per -size full,
// and lets tests pass on cortex-m.
func fakeMatchString(pat, str string) (bool, error) {
if pat == ".*" {
return true, nil
}
matched := strings.Contains(str, pat)
return matched, nil
}
-204
View File
@@ -1,204 +0,0 @@
//go:build !windows
// TODO: implement readdir for windows, then enable this file
// Copyright 2014 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.
package testing_test
import (
"errors"
"io/fs"
"os"
"path/filepath"
"runtime"
"testing"
)
// This is exactly what a test would do without a TestMain.
// It's here only so that there is at least one package in the
// standard library with a TestMain, so that code is executed.
func TestMain(m *testing.M) {
os.Exit(m.Run())
}
func TestTempDirInCleanup(t *testing.T) {
if runtime.GOOS == "wasip1" || runtime.GOOS == "wasip2" {
t.Log("Skipping. TODO: implement RemoveAll for wasi")
return
}
var dir string
t.Run("test", func(t *testing.T) {
t.Cleanup(func() {
dir = t.TempDir()
})
_ = t.TempDir()
})
fi, err := os.Stat(dir)
if fi != nil {
t.Fatalf("Directory %q from user Cleanup still exists", dir)
}
if !errors.Is(err, fs.ErrNotExist) {
t.Fatalf("Unexpected error: %v", err)
}
}
func TestTempDirInBenchmark(t *testing.T) {
testing.Benchmark(func(b *testing.B) {
if !b.Run("test", func(b *testing.B) {
// Add a loop so that the test won't fail. See issue 38677.
for i := 0; i < b.N; i++ {
_ = b.TempDir()
}
}) {
t.Fatal("Sub test failure in a benchmark")
}
})
}
func TestTempDir(t *testing.T) {
if runtime.GOOS == "wasip1" || runtime.GOOS == "wasip2" {
t.Log("Skipping. TODO: implement RemoveAll for wasi")
return
}
testTempDir(t)
t.Run("InSubtest", testTempDir)
t.Run("test/subtest", testTempDir)
t.Run("test\\subtest", testTempDir)
t.Run("test:subtest", testTempDir)
t.Run("test/..", testTempDir)
t.Run("../test", testTempDir)
t.Run("test[]", testTempDir)
t.Run("test*", testTempDir)
t.Run("äöüéè", testTempDir)
}
func testTempDir(t *testing.T) {
dirCh := make(chan string, 1)
t.Cleanup(func() {
// Verify directory has been removed.
select {
case dir := <-dirCh:
fi, err := os.Stat(dir)
if errors.Is(err, fs.ErrNotExist) {
// All good
return
}
if err != nil {
t.Fatal(err)
}
t.Errorf("directory %q still exists: %v, isDir=%v", dir, fi, fi.IsDir())
default:
if !t.Failed() {
t.Fatal("never received dir channel")
}
}
})
dir := t.TempDir()
if dir == "" {
t.Fatal("expected dir")
}
dir2 := t.TempDir()
if dir == dir2 {
t.Fatal("subsequent calls to TempDir returned the same directory")
}
if filepath.Dir(dir) != filepath.Dir(dir2) {
t.Fatalf("calls to TempDir do not share a parent; got %q, %q", dir, dir2)
}
dirCh <- dir
fi, err := os.Stat(dir)
if err != nil {
t.Fatal(err)
}
if !fi.IsDir() {
t.Errorf("dir %q is not a dir", dir)
}
files, err := os.ReadDir(dir)
if err != nil {
t.Fatal(err)
}
if len(files) > 0 {
t.Errorf("unexpected %d files in TempDir: %v", len(files), files)
}
glob := filepath.Join(dir, "*.txt")
if _, err := filepath.Glob(glob); err != nil {
t.Error(err)
}
err = os.Remove(dir)
if err != nil {
t.Errorf("unexpected files in TempDir")
}
}
func TestSetenv(t *testing.T) {
tests := []struct {
name string
key string
initialValueExists bool
initialValue string
newValue string
}{
{
name: "initial value exists",
key: "GO_TEST_KEY_1",
initialValueExists: true,
initialValue: "111",
newValue: "222",
},
{
name: "initial value exists but empty",
key: "GO_TEST_KEY_2",
initialValueExists: true,
initialValue: "",
newValue: "222",
},
{
name: "initial value is not exists",
key: "GO_TEST_KEY_3",
initialValueExists: false,
initialValue: "",
newValue: "222",
},
}
for _, test := range tests {
if test.initialValueExists {
if err := os.Setenv(test.key, test.initialValue); err != nil {
t.Fatalf("unable to set env: got %v", err)
}
} else {
os.Unsetenv(test.key)
}
t.Run(test.name, func(t *testing.T) {
t.Setenv(test.key, test.newValue)
if os.Getenv(test.key) != test.newValue {
t.Fatalf("unexpected value after t.Setenv: got %s, want %s", os.Getenv(test.key), test.newValue)
}
})
got, exists := os.LookupEnv(test.key)
if got != test.initialValue {
t.Fatalf("unexpected value after t.Setenv cleanup: got %s, want %s", got, test.initialValue)
}
if exists != test.initialValueExists {
t.Fatalf("unexpected value after t.Setenv cleanup: got %t, want %t", exists, test.initialValueExists)
}
}
}
func TestTesting(t *testing.T) {
if !testing.Testing() {
t.Error("Expected testing.Testing() to return true while in a test")
}
}