diff --git a/GNUmakefile b/GNUmakefile index 962b2e2613..5447e51ebc 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -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). diff --git a/loader/goroot.go b/loader/goroot.go index 00a7124d80..3241ade1a2 100644 --- a/loader/goroot.go +++ b/loader/goroot.go @@ -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, } diff --git a/src/runtime/debug/garbage.go b/src/runtime/debug/garbage.go index cf6cac0493..bf40bbc442 100644 --- a/src/runtime/debug/garbage.go +++ b/src/runtime/debug/garbage.go @@ -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 diff --git a/src/runtime/extern.go b/src/runtime/extern.go index f4e7c002c4..ec0223c46b 100644 --- a/src/runtime/extern.go +++ b/src/runtime/extern.go @@ -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 } diff --git a/src/runtime/runtime.go b/src/runtime/runtime.go index c9b0959384..c4598ca1a9 100644 --- a/src/runtime/runtime.go +++ b/src/runtime/runtime.go @@ -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 +} diff --git a/src/testing/benchmark.go b/src/testing/benchmark.go deleted file mode 100644 index 2dc59b72fd..0000000000 --- a/src/testing/benchmark.go +++ /dev/null @@ -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 -} diff --git a/src/testing/benchmark_test.go b/src/testing/benchmark_test.go deleted file mode 100644 index 7fbd2cf121..0000000000 --- a/src/testing/benchmark_test.go +++ /dev/null @@ -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) }) -} diff --git a/src/testing/doc.go b/src/testing/doc.go deleted file mode 100644 index b5026d8062..0000000000 --- a/src/testing/doc.go +++ /dev/null @@ -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. -*/ diff --git a/src/testing/fuzz.go b/src/testing/fuzz.go deleted file mode 100644 index b6eaad409f..0000000000 --- a/src/testing/fuzz.go +++ /dev/null @@ -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/ 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 -} diff --git a/src/testing/is_baremetal.go b/src/testing/is_baremetal.go deleted file mode 100644 index a9a2625a49..0000000000 --- a/src/testing/is_baremetal.go +++ /dev/null @@ -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 diff --git a/src/testing/is_not_baremetal.go b/src/testing/is_not_baremetal.go deleted file mode 100644 index 8f487ec992..0000000000 --- a/src/testing/is_not_baremetal.go +++ /dev/null @@ -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 diff --git a/src/testing/match.go b/src/testing/match.go deleted file mode 100644 index 1762b26584..0000000000 --- a/src/testing/match.go +++ /dev/null @@ -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 -} diff --git a/src/testing/match_test.go b/src/testing/match_test.go deleted file mode 100644 index 9ef5ceb34f..0000000000 --- a/src/testing/match_test.go +++ /dev/null @@ -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, " | ")) -} diff --git a/src/testing/sub_test.go b/src/testing/sub_test.go deleted file mode 100644 index 7ac1ea47d4..0000000000 --- a/src/testing/sub_test.go +++ /dev/null @@ -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) - } -} diff --git a/src/testing/testing.go b/src/testing/testing.go deleted file mode 100644 index c4449cbb0a..0000000000 --- a/src/testing/testing.go +++ /dev/null @@ -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, ") - - 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 -} diff --git a/src/testing/testing_test.go b/src/testing/testing_test.go deleted file mode 100644 index eecba519af..0000000000 --- a/src/testing/testing_test.go +++ /dev/null @@ -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") - } -}