// 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 test import ( "bytes" "fmt" "go/ast" "go/parser" "go/token" "internal/testenv" "os" "path/filepath" "runtime" "strings" "testing" ) // runGenTest runs a test-generator, then runs the generated test. // Generated test can either fail in compilation or execution. // The environment variable parameter(s) is passed to the run // of the generated test. func runGenTest(t *testing.T, filename, tmpname string, ev ...string) { testenv.MustHaveGoRun(t) gotool := testenv.GoToolPath(t) var stdout, stderr bytes.Buffer cmd := testenv.Command(t, gotool, "run", filepath.Join("testdata", filename)) cmd.Stdout = &stdout cmd.Stderr = &stderr if err := cmd.Run(); err != nil { t.Fatalf("Failed: %v:\nOut: %s\nStderr: %s\n", err, &stdout, &stderr) } // Write stdout into a temporary file rungo := filepath.Join(t.TempDir(), "run.go") ok := os.WriteFile(rungo, stdout.Bytes(), 0600) if ok != nil { t.Fatalf("Failed to create temporary file %s", rungo) } stdout.Reset() stderr.Reset() cmd = testenv.Command(t, gotool, "run", "-gcflags=-d=ssa/check/on", rungo) cmd.Stdout = &stdout cmd.Stderr = &stderr cmd.Env = append(cmd.Env, ev...) err := cmd.Run() if err != nil { t.Fatalf("Failed: %v:\nOut: %s\nStderr: %s\n", err, &stdout, &stderr) } if s := stderr.String(); s != "" { t.Errorf("Stderr = %s\nWant empty", s) } if s := stdout.String(); s != "" { t.Errorf("Stdout = %s\nWant empty", s) } } func TestGenFlowGraph(t *testing.T) { if testing.Short() { t.Skip("not run in short mode.") } runGenTest(t, "flowgraph_generator1.go", "ssa_fg_tmp1") } // TestCode runs all the tests in the testdata directory as subtests. // These tests are special because we want to run them with different // compiler flags set (and thus they can't just be _test.go files in // this directory). func TestCode(t *testing.T) { testenv.MustHaveGoBuild(t) gotool := testenv.GoToolPath(t) // Make a temporary directory to work in. tmpdir := t.TempDir() // Find all the test functions (and the files containing them). var srcs []string // files containing Test functions type test struct { name string // TestFoo usesFloat bool // might use float operations } var tests []test files, err := os.ReadDir("testdata") if err != nil { t.Fatalf("can't read testdata directory: %v", err) } for _, f := range files { if !strings.HasSuffix(f.Name(), "_test.go") { continue } text, err := os.ReadFile(filepath.Join("testdata", f.Name())) if err != nil { t.Fatalf("can't read testdata/%s: %v", f.Name(), err) } fset := token.NewFileSet() code, err := parser.ParseFile(fset, f.Name(), text, 0) if err != nil { t.Fatalf("can't parse testdata/%s: %v", f.Name(), err) } srcs = append(srcs, filepath.Join("testdata", f.Name())) foundTest := false for _, d := range code.Decls { fd, ok := d.(*ast.FuncDecl) if !ok { continue } if !strings.HasPrefix(fd.Name.Name, "Test") { continue } if fd.Recv != nil { continue } if fd.Type.Results != nil { continue } if len(fd.Type.Params.List) != 1 { continue } p := fd.Type.Params.List[0] if len(p.Names) != 1 { continue } s, ok := p.Type.(*ast.StarExpr) if !ok { continue } sel, ok := s.X.(*ast.SelectorExpr) if !ok { continue } base, ok := sel.X.(*ast.Ident) if !ok { continue } if base.Name != "testing" { continue } if sel.Sel.Name != "T" { continue } // Found a testing function. tests = append(tests, test{name: fd.Name.Name, usesFloat: bytes.Contains(text, []byte("float"))}) foundTest = true } if !foundTest { t.Fatalf("test file testdata/%s has no tests in it", f.Name()) } } flags := []string{""} if runtime.GOARCH == "arm" || runtime.GOARCH == "mips" || runtime.GOARCH == "mips64" || runtime.GOARCH == "386" { flags = append(flags, ",softfloat") } for _, flag := range flags { args := []string{"test", "-c", "-gcflags=-d=ssa/check/on" + flag, "-o", filepath.Join(tmpdir, "code.test")} args = append(args, srcs...) out, err := testenv.Command(t, gotool, args...).CombinedOutput() if err != nil || len(out) != 0 { t.Fatalf("Build failed: %v\n%s\n", err, out) } // Now we have a test binary. Run it with all the tests as subtests of this one. for _, test := range tests { test := test if flag == ",softfloat" && !test.usesFloat { // No point in running the soft float version if the test doesn't use floats. continue } t.Run(fmt.Sprintf("%s%s", test.name[4:], flag), func(t *testing.T) { out, err := testenv.Command(t, filepath.Join(tmpdir, "code.test"), "-test.run=^"+test.name+"$").CombinedOutput() if err != nil || string(out) != "PASS\n" { t.Errorf("Failed:\n%s\n", out) } }) } } }