aboutsummaryrefslogtreecommitdiffhomepage
path: root/main_test.go
diff options
context:
space:
mode:
authorNia Waldvogel <[email protected]>2022-01-02 12:13:32 -0500
committerNia <[email protected]>2022-01-03 18:59:19 -0500
commitd86dd642b30a42becc99dc050070d1c96f69678e (patch)
treec9d530b468773573536fba78d7e7b8c4917c9305 /main_test.go
parent21b89ef327b549dbe133424a571966b77e7ef5be (diff)
downloadtinygo-d86dd642b30a42becc99dc050070d1c96f69678e.tar.gz
tinygo-d86dd642b30a42becc99dc050070d1c96f69678e.zip
main (test): add tests to `tinygo test`
Diffstat (limited to 'main_test.go')
-rw-r--r--main_test.go242
1 files changed, 196 insertions, 46 deletions
diff --git a/main_test.go b/main_test.go
index 607f109ba..bb1e26f31 100644
--- a/main_test.go
+++ b/main_test.go
@@ -9,12 +9,14 @@ import (
"errors"
"flag"
"fmt"
+ "io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
+ "sync"
"testing"
"time"
@@ -27,7 +29,11 @@ const TESTDATA = "testdata"
var testTarget = flag.String("target", "", "override test target")
-func TestCompiler(t *testing.T) {
+var sema = make(chan struct{}, runtime.NumCPU())
+
+func TestBuild(t *testing.T) {
+ t.Parallel()
+
tests := []string{
"alias.go",
"atomic.go",
@@ -62,8 +68,6 @@ func TestCompiler(t *testing.T) {
tests = append(tests, "go1.17.go")
}
- sema := make(chan struct{}, runtime.NumCPU())
-
if *testTarget != "" {
// This makes it possible to run one specific test (instead of all),
// which is especially useful to quickly check whether some changes
@@ -84,41 +88,29 @@ func TestCompiler(t *testing.T) {
// Test with few optimizations enabled (no inlining, etc).
t.Run("opt=1", func(t *testing.T) {
t.Parallel()
- runTestWithConfig("stdlib.go", t, compileopts.Options{
- GOOS: goenv.Get("GOOS"),
- GOARCH: goenv.Get("GOARCH"),
- GOARM: goenv.Get("GOARM"),
- Opt: "1",
- Semaphore: sema,
- }, nil, nil)
+ opts := optionsFromTarget("", sema)
+ opts.Opt = "1"
+ runTestWithConfig("stdlib.go", t, opts, nil, nil)
})
// Test with only the bare minimum of optimizations enabled.
// TODO: fix this for stdlib.go, which currently fails.
t.Run("opt=0", func(t *testing.T) {
t.Parallel()
- runTestWithConfig("print.go", t, compileopts.Options{
- GOOS: goenv.Get("GOOS"),
- GOARCH: goenv.Get("GOARCH"),
- GOARM: goenv.Get("GOARM"),
- Opt: "0",
- Semaphore: sema,
- }, nil, nil)
+ opts := optionsFromTarget("", sema)
+ opts.Opt = "0"
+ runTestWithConfig("print.go", t, opts, nil, nil)
})
t.Run("ldflags", func(t *testing.T) {
t.Parallel()
- runTestWithConfig("ldflags.go", t, compileopts.Options{
- GOOS: goenv.Get("GOOS"),
- GOARCH: goenv.Get("GOARCH"),
- GOARM: goenv.Get("GOARM"),
- GlobalValues: map[string]map[string]string{
- "main": {
- "someGlobal": "foobar",
- },
+ opts := optionsFromTarget("", sema)
+ opts.GlobalValues = map[string]map[string]string{
+ "main": {
+ "someGlobal": "foobar",
},
- Semaphore: sema,
- }, nil, nil)
+ }
+ runTestWithConfig("ldflags.go", t, opts, nil, nil)
})
})
@@ -211,22 +203,12 @@ func TestCompiler(t *testing.T) {
}
func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
- // Check if the emulator is installed.
+ emuCheck(t, options)
+
spec, err := compileopts.LoadTarget(&options)
if err != nil {
t.Fatal("failed to load target spec:", err)
}
- if len(spec.Emulator) != 0 {
- _, err := exec.LookPath(spec.Emulator[0])
- if err != nil {
- if errors.Is(err, exec.ErrNotFound) {
- t.Skipf("emulator not installed: %q", spec.Emulator[0])
- }
-
- t.Errorf("searching for emulator: %v", err)
- return
- }
- }
for _, name := range tests {
name := name // redefine to avoid race condition
@@ -263,6 +245,25 @@ func runPlatTests(options compileopts.Options, tests []string, t *testing.T) {
}
}
+func emuCheck(t *testing.T, options compileopts.Options) {
+ // Check if the emulator is installed.
+ spec, err := compileopts.LoadTarget(&options)
+ if err != nil {
+ t.Fatal("failed to load target spec:", err)
+ }
+ if len(spec.Emulator) != 0 {
+ _, err := exec.LookPath(spec.Emulator[0])
+ if err != nil {
+ if errors.Is(err, exec.ErrNotFound) {
+ t.Skipf("emulator not installed: %q", spec.Emulator[0])
+ }
+
+ t.Errorf("searching for emulator: %v", err)
+ return
+ }
+ }
+}
+
func optionsFromTarget(target string, sema chan struct{}) compileopts.Options {
return compileopts.Options{
// GOOS/GOARCH are only used if target == ""
@@ -271,6 +272,9 @@ func optionsFromTarget(target string, sema chan struct{}) compileopts.Options {
GOARM: goenv.Get("GOARM"),
Target: target,
Semaphore: sema,
+ Debug: true,
+ VerifyIR: true,
+ Opt: "z",
}
}
@@ -283,6 +287,9 @@ func optionsFromOSARCH(osarch string, sema chan struct{}) compileopts.Options {
GOOS: parts[0],
GOARCH: parts[1],
Semaphore: sema,
+ Debug: true,
+ VerifyIR: true,
+ Opt: "z",
}
if options.GOARCH == "arm" {
options.GOARM = parts[2]
@@ -295,13 +302,6 @@ func runTest(name string, options compileopts.Options, t *testing.T, cmdArgs, en
}
func runTestWithConfig(name string, t *testing.T, options compileopts.Options, cmdArgs, environmentVars []string) {
- // Set default config.
- options.Debug = true
- options.VerifyIR = true
- if options.Opt == "" {
- options.Opt = "z"
- }
-
// Get the expected output for this test.
// Note: not using filepath.Join as it strips the path separator at the end
// of the path.
@@ -466,6 +466,156 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
}
}
+func TestTest(t *testing.T) {
+ t.Parallel()
+
+ type targ struct {
+ name string
+ opts compileopts.Options
+ }
+ targs := []targ{
+ // Host
+ {"Host", optionsFromTarget("", sema)},
+ }
+ if !testing.Short() {
+ if runtime.GOOS == "linux" {
+ targs = append(targs,
+ // Linux
+ targ{"X86Linux", optionsFromOSARCH("linux/386", sema)},
+ targ{"ARMLinux", optionsFromOSARCH("linux/arm/6", sema)},
+ targ{"ARM64Linux", optionsFromOSARCH("linux/arm64", sema)},
+ )
+ }
+
+ targs = append(targs,
+ // QEMU microcontrollers
+ targ{"EmulatedCortexM3", optionsFromTarget("cortex-m-qemu", sema)},
+ targ{"EmulatedRISCV", optionsFromTarget("riscv-qemu", sema)},
+
+ // Node/Wasmtime
+ targ{"WASM", optionsFromTarget("wasm", sema)},
+ targ{"WASI", optionsFromTarget("wasi", sema)},
+ )
+ }
+ for _, targ := range targs {
+ targ := targ
+ t.Run(targ.name, func(t *testing.T) {
+ t.Parallel()
+
+ emuCheck(t, targ.opts)
+
+ t.Run("Pass", func(t *testing.T) {
+ t.Parallel()
+
+ // Test a package which builds and passes normally.
+
+ var wg sync.WaitGroup
+ defer wg.Wait()
+
+ out := ioLogger(t, &wg)
+ defer out.Close()
+
+ opts := targ.opts
+ passed, err := Test("github.com/tinygo-org/tinygo/tests/testing/pass", out, out, &opts, false, false, false, "", "")
+ if err != nil {
+ t.Errorf("test error: %v", err)
+ }
+ if !passed {
+ t.Error("test failed")
+ }
+ })
+
+ t.Run("Fail", func(t *testing.T) {
+ t.Parallel()
+
+ // Test a package which builds fine but fails.
+
+ var wg sync.WaitGroup
+ defer wg.Wait()
+
+ out := ioLogger(t, &wg)
+ defer out.Close()
+
+ opts := targ.opts
+ passed, err := Test("github.com/tinygo-org/tinygo/tests/testing/fail", out, out, &opts, false, false, false, "", "")
+ if err != nil {
+ t.Errorf("test error: %v", err)
+ }
+ if passed {
+ t.Error("test passed")
+ }
+ })
+
+ if targ.name != "Host" {
+ // Emulated tests are somewhat slow, and these do not need to be run across every platform.
+ return
+ }
+
+ t.Run("Nothing", func(t *testing.T) {
+ t.Parallel()
+
+ // Test a package with no test files.
+
+ var wg sync.WaitGroup
+ defer wg.Wait()
+
+ out := ioLogger(t, &wg)
+ defer out.Close()
+
+ var output bytes.Buffer
+ opts := targ.opts
+ passed, err := Test("github.com/tinygo-org/tinygo/tests/testing/nothing", io.MultiWriter(&output, out), out, &opts, false, false, false, "", "")
+ if err != nil {
+ t.Errorf("test error: %v", err)
+ }
+ if !passed {
+ t.Error("test failed")
+ }
+ if !strings.Contains(output.String(), "[no test files]") {
+ t.Error("missing [no test files] in output")
+ }
+ })
+
+ t.Run("BuildErr", func(t *testing.T) {
+ t.Parallel()
+
+ // Test a package which fails to build.
+
+ var wg sync.WaitGroup
+ defer wg.Wait()
+
+ out := ioLogger(t, &wg)
+ defer out.Close()
+
+ opts := targ.opts
+ passed, err := Test("github.com/tinygo-org/tinygo/tests/testing/builderr", out, out, &opts, false, false, false, "", "")
+ if err == nil {
+ t.Error("test did not error")
+ }
+ if passed {
+ t.Error("test passed")
+ }
+ })
+ })
+ }
+}
+
+func ioLogger(t *testing.T, wg *sync.WaitGroup) io.WriteCloser {
+ r, w := io.Pipe()
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ defer r.Close()
+
+ scanner := bufio.NewScanner(r)
+ for scanner.Scan() {
+ t.Log(scanner.Text())
+ }
+ }()
+
+ return w
+}
+
// This TestMain is necessary because TinyGo may also be invoked to run certain
// LLVM tools in a separate process. Not capturing these invocations would lead
// to recursive tests.