diff --git a/main.go b/main.go index 481e92ba..463c25d4 100644 --- a/main.go +++ b/main.go @@ -216,7 +216,7 @@ func Build(pkgName, outpath string, options *compileopts.Options) error { // Test runs the tests in the given package. Returns whether the test passed and // possibly an error if the test failed to run. -func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options, testCompileOnly, testVerbose, testShort bool, testRunRegexp string, testBenchRegexp string, testBenchTime string, testBenchMem bool, outpath string) (bool, error) { +func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options, testCompileOnly, testVerbose, testShort bool, testRunRegexp string, testCount int, testBenchRegexp string, testBenchTime string, testBenchMem bool, outpath string) (bool, error) { options.TestConfig.CompileTestBinary = true config, err := builder.NewConfig(options) if err != nil { @@ -243,6 +243,9 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options if testBenchMem { flags = append(flags, "-test.benchmem") } + if testCount != 1 { + flags = append(flags, "-test.count="+strconv.Itoa(testCount)) + } buf := bytes.Buffer{} passed := false @@ -1405,6 +1408,7 @@ func main() { flag.StringVar(&outpath, "o", "", "output filename") } var testCompileOnlyFlag, testVerboseFlag, testShortFlag *bool + var testCount *int var testBenchRegexp *string var testBenchTime *string var testRunRegexp *string @@ -1414,6 +1418,7 @@ func main() { testVerboseFlag = flag.Bool("v", false, "verbose: print additional output") testShortFlag = flag.Bool("short", false, "short: run smaller test suite to save time") testRunRegexp = flag.String("run", "", "run: regexp of tests to run") + testCount = flag.Int("count", 1, "count: number of times to run tests/benchmarks `count` times") testBenchRegexp = flag.String("bench", "", "run: regexp of benchmarks to run") testBenchTime = flag.String("benchtime", "", "run each benchmark for duration `d`") testBenchMem = flag.Bool("benchmem", false, "show memory stats for benchmarks") @@ -1659,7 +1664,7 @@ func main() { defer close(buf.done) stdout := (*testStdout)(buf) stderr := (*testStderr)(buf) - passed, err := Test(pkgName, stdout, stderr, options, *testCompileOnlyFlag, *testVerboseFlag, *testShortFlag, *testRunRegexp, *testBenchRegexp, *testBenchTime, *testBenchMem, outpath) + passed, err := Test(pkgName, stdout, stderr, options, *testCompileOnlyFlag, *testVerboseFlag, *testShortFlag, *testRunRegexp, *testCount, *testBenchRegexp, *testBenchTime, *testBenchMem, outpath) if err != nil { printCompilerError(func(args ...interface{}) { fmt.Fprintln(stderr, args...) diff --git a/src/testing/benchmark.go b/src/testing/benchmark.go index 5f106d99..3293ccd0 100644 --- a/src/testing/benchmark.go +++ b/src/testing/benchmark.go @@ -27,6 +27,7 @@ var ( matchBenchmarks *string benchmarkMemory *bool benchTime = benchTimeFlag{d: 1 * time.Second} // changed during test of testing package + testCount *int ) type benchTimeFlag struct { @@ -213,11 +214,14 @@ func (b *B) doBench() BenchmarkResult { // of benchmark iterations until the benchmark runs for the requested benchtime. // run1 must have been called on b. func (b *B) launch() { + runtime.GC() // 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. @@ -394,24 +398,26 @@ func runBenchmarks(matchString func(pat, str string) (bool, error), benchmarks [ func (b *B) processBench(ctx *benchContext) { benchName := b.name - 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() + 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) } - fmt.Println(results) } } diff --git a/src/testing/testing.go b/src/testing/testing.go index 9c76f097..76ff7e69 100644 --- a/src/testing/testing.go +++ b/src/testing/testing.go @@ -26,6 +26,7 @@ var ( flagVerbose bool flagShort bool flagRunRegexp string + flagCount int ) var initRan bool @@ -40,6 +41,7 @@ func Init() { 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.IntVar(&flagCount, "test.count", 1, "run each test or benchmark `count` times") initBenchmarkFlags() } @@ -485,12 +487,14 @@ func runTests(matchString func(pat, str string) (bool, error), tests []InternalT context: ctx, } - tRunner(t, func(t *T) { - for _, test := range tests { - t.Run(test.Name, test.F) - ok = ok && !t.Failed() - } - }) + 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 }