From 9160f3f16dfd39011054dac2423679a90777f172 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Tue, 19 Apr 2022 23:07:31 +0200 Subject: [PATCH] main: use shared code for both run and test subcommands This means that we don't need duplicate code to pass parameters to wasmtime and that the following actually produces verbose output (it didn't before this commit): tinygo test -v -target=cortex-m-qemu math --- main.go | 161 ++++++++++++++++++++++----------------------------- main_test.go | 4 +- 2 files changed, 71 insertions(+), 94 deletions(-) diff --git a/main.go b/main.go index a2a4f58f..b9b7626b 100644 --- a/main.go +++ b/main.go @@ -200,8 +200,26 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options return false, err } + // Pass test flags to the test binary. + var flags []string + if testVerbose { + flags = append(flags, "-test.v") + } + if testShort { + flags = append(flags, "-test.short") + } + if testRunRegexp != "" { + flags = append(flags, "-test.run="+testRunRegexp) + } + if testBenchRegexp != "" { + flags = append(flags, "-test.bench="+testBenchRegexp) + } + if testBenchTime != "" { + flags = append(flags, "-test.benchtime="+testBenchTime) + } + passed := false - err = builder.Build(pkgName, outpath, config, func(result builder.BuildResult) error { + err = buildAndRun(pkgName, config, os.Stdout, flags, nil, 0, func(cmd *exec.Cmd, result builder.BuildResult) error { if testCompileOnly || outpath != "" { // Write test binary to the specified file name. if outpath == "" { @@ -217,27 +235,53 @@ func Test(pkgName string, stdout, stderr io.Writer, options *compileopts.Options return nil } - // Run the test. - config.Options.Semaphore <- struct{}{} - defer func() { - <-config.Options.Semaphore - }() - start := time.Now() - var err error - passed, err = runPackageTest(config, stdout, stderr, result, testVerbose, testShort, testRunRegexp, testBenchRegexp, testBenchTime) - if err != nil { - return err + // Tests are always run in the package directory. + cmd.Dir = result.MainDir + + // Wasmtime needs a few extra flags to work. + emulator := config.Emulator() + if len(emulator) != 0 && emulator[0] == "wasmtime" { + // Add directories to the module root, but skip the current working + // directory which is already added by buildAndRun. + dirs := dirsToModuleRoot(result.MainDir, result.ModuleRoot) + var args []string + for _, d := range dirs[1:] { + args = append(args, "--dir="+d) + } + + // create a new temp directory just for this run, announce it to os.TempDir() via TMPDIR + tmpdir, err := ioutil.TempDir("", "tinygotmp") + if err != nil { + return fmt.Errorf("failed to create temporary directory: %w", err) + } + args = append(args, "--dir="+tmpdir, "--env=TMPDIR="+tmpdir) + // TODO: add option to not delete temp dir for debugging? + defer os.RemoveAll(tmpdir) + + // Insert new argments at the front of the command line argments. + args = append(args, cmd.Args[1:]...) + cmd.Args = append(cmd.Args[:1:1], args...) } + + // Run the test. + start := time.Now() + err = cmd.Run() duration := time.Since(start) // Print the result. importPath := strings.TrimSuffix(result.ImportPath, ".test") + passed = err == nil if passed { fmt.Fprintf(stdout, "ok \t%s\t%.3fs\n", importPath, duration.Seconds()) } else { fmt.Fprintf(stdout, "FAIL\t%s\t%.3fs\n", importPath, duration.Seconds()) } - return nil + if _, ok := err.(*exec.ExitError); ok { + // Binary exited with a non-zero exit code, which means the test + // failed. + return nil + } + return err }) if err, ok := err.(loader.NoTestFilesError); ok { fmt.Fprintf(stdout, "? \t%s\t[no test files]\n", err.ImportPath) @@ -260,81 +304,6 @@ func dirsToModuleRoot(maindir, modroot string) []string { return dirs } -// runPackageTest runs a test binary that was previously built. The return -// values are whether the test passed and any errors encountered while trying to -// run the binary. -func runPackageTest(config *compileopts.Config, stdout, stderr io.Writer, result builder.BuildResult, testVerbose, testShort bool, testRunRegexp string, testBenchRegexp string, testBenchTime string) (bool, error) { - var cmd *exec.Cmd - emulator := config.Emulator() - if len(emulator) == 0 { - // Run directly. - var flags []string - if testVerbose { - flags = append(flags, "-test.v") - } - if testShort { - flags = append(flags, "-test.short") - } - if testRunRegexp != "" { - flags = append(flags, "-test.run="+testRunRegexp) - } - if testBenchRegexp != "" { - flags = append(flags, "-test.bench="+testBenchRegexp) - } - if testBenchTime != "" { - flags = append(flags, "-test.benchtime="+testBenchTime) - } - cmd = executeCommand(config.Options, result.Binary, flags...) - } else { - // Run in an emulator. - args := append(emulator[1:], result.Binary) - if emulator[0] == "wasmtime" { - // create a new temp directory just for this run, announce it to os.TempDir() via TMPDIR - tmpdir, err := ioutil.TempDir("", "tinygotmp") - if err != nil { - return false, &commandError{"failed to create temporary directory", "tinygotmp", err} - } - args = append(args, "--dir="+tmpdir, "--env=TMPDIR="+tmpdir) - // TODO: add option to not delete temp dir for debugging? - defer os.RemoveAll(tmpdir) - - // allow reading from directories up to module root - for _, d := range dirsToModuleRoot(result.MainDir, result.ModuleRoot) { - args = append(args, "--dir="+d) - } - - // mark end of wasmtime arguments and start of program ones: -- - args = append(args, "--") - if testVerbose { - args = append(args, "-test.v") - } - if testShort { - args = append(args, "-test.short") - } - if testRunRegexp != "" { - args = append(args, "-test.run="+testRunRegexp) - } - if testBenchRegexp != "" { - args = append(args, "-test.bench="+testBenchRegexp) - } - } - cmd = executeCommand(config.Options, emulator[0], args...) - } - cmd.Dir = result.MainDir - cmd.Stdout = stdout - cmd.Stderr = stderr - err := cmd.Run() - if err != nil { - if _, ok := err.(*exec.ExitError); ok { - // Binary exited with a non-zero exit code, which means the test - // failed. - return false, nil - } - return false, &commandError{"failed to run compiled binary", result.Binary, err} - } - return true, nil -} - // Flash builds and flashes the built binary to the given serial port. func Flash(pkgName, port string, options *compileopts.Options) error { config, err := builder.NewConfig(options) @@ -715,14 +684,16 @@ func Run(pkgName string, options *compileopts.Options, cmdArgs []string) error { return err } - return buildAndRun(pkgName, config, os.Stdout, cmdArgs, nil, 0) + return buildAndRun(pkgName, config, os.Stdout, cmdArgs, nil, 0, func(cmd *exec.Cmd, result builder.BuildResult) error { + return cmd.Run() + }) } // buildAndRun builds and runs the given program, writing output to stdout and // errors to os.Stderr. It takes care of emulators (qemu, wasmtime, etc) and // passes command line arguments and evironment variables in a way appropriate // for the given emulator. -func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, cmdArgs, environmentVars []string, timeout time.Duration) error { +func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, cmdArgs, environmentVars []string, timeout time.Duration, run func(cmd *exec.Cmd, result builder.BuildResult) error) error { // make sure any special vars in the emulator definition are rewritten emulator := config.Emulator() @@ -764,7 +735,11 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c for _, v := range environmentVars { args = append(args, "--env", v) } - args = append(args, cmdArgs...) + if len(cmdArgs) != 0 { + // mark end of wasmtime arguments and start of program ones: -- + args = append(args, "--") + args = append(args, cmdArgs...) + } } else { // Pass environment variables and command line parameters as usual. // This also works on qemu-aarch64 etc. @@ -821,11 +796,11 @@ func buildAndRun(pkgName string, config *compileopts.Config, stdout io.Writer, c if config.Options.PrintCommands != nil { config.Options.PrintCommands(cmd.Path, cmd.Args...) } - err := cmd.Run() + err := run(cmd, result) if err != nil { - if cerr := ctx.Err(); cerr == context.DeadlineExceeded { + if ctx != nil && ctx.Err() == context.DeadlineExceeded { stdout.Write([]byte(fmt.Sprintf("--- timeout of %s exceeded, terminating...\n", timeout))) - err = cerr + err = ctx.Err() } return &commandError{"failed to run compiled binary", result.Binary, err} } diff --git a/main_test.go b/main_test.go index 4b043932..efb4b514 100644 --- a/main_test.go +++ b/main_test.go @@ -332,7 +332,9 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c // Build the test binary. stdout := &bytes.Buffer{} - err = buildAndRun("./"+path, config, stdout, cmdArgs, environmentVars, time.Minute) + err = buildAndRun("./"+path, config, stdout, cmdArgs, environmentVars, time.Minute, func(cmd *exec.Cmd, result builder.BuildResult) error { + return cmd.Run() + }) if err != nil { printCompilerError(t.Log, err) t.Fail()