go/ssa: speed up TestTypeparamTest by loading tests once
Previously, TestTypeparamTest constructs a go module for each file
under test/typeparam and uses packages.Load to load it. However, the
packages.Load utilizes the 'go list' underlying and it costs a lot.
As a result, this made the test 50x slower.
This change tries to put each file under a separated package
in the same go module so 'go list' will be used only once.
It supports to skip the test if the test/typeparam under GOROOT doesn't
exist.
Fixes golang/go#70078
Change-Id: I2953709236602f1f6d0b53457ef21bb83561e656
Reviewed-on: https://go-review.googlesource.com/c/tools/+/623135
Reviewed-by: Alan Donovan <adonovan@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
Auto-Submit: Alan Donovan <adonovan@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Richard Miller <millerresearch@gmail.com>
diff --git a/go/ssa/builder_test.go b/go/ssa/builder_test.go
index a38186d..f001fff 100644
--- a/go/ssa/builder_test.go
+++ b/go/ssa/builder_test.go
@@ -6,6 +6,7 @@
import (
"bytes"
+ "errors"
"fmt"
"go/ast"
"go/importer"
@@ -686,8 +687,10 @@
t.Fatalf("Failed to load errors package from std: %s", err)
}
goroot := filepath.Dir(filepath.Dir(filepath.Dir(stdPkgs[0].GoFiles[0])))
-
dir := filepath.Join(goroot, "test", "typeparam")
+ if _, err = os.Stat(dir); errors.Is(err, os.ErrNotExist) {
+ t.Skipf("test/typeparam doesn't exist under GOROOT %s", goroot)
+ }
// Collect all of the .go files in
fsys := os.DirFS(dir)
@@ -696,24 +699,39 @@
t.Fatal(err)
}
+ // Each call to buildPackage calls package.Load, which invokes "go list",
+ // and with over 300 subtests this can be very slow (minutes, or tens
+ // on some platforms). So, we use an overlay to map each test file to a
+ // distinct single-file package and load them all at once.
+ overlay := map[string][]byte{
+ "go.mod": goMod("example.com", -1),
+ }
for _, entry := range entries {
if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") {
continue // Consider standalone go files.
}
- t.Run(entry.Name(), func(t *testing.T) {
- src, err := fs.ReadFile(fsys, entry.Name())
- if err != nil {
- t.Fatal(err)
- }
+ src, err := fs.ReadFile(fsys, entry.Name())
+ if err != nil {
+ t.Fatal(err)
+ }
+ // Only build test files that can be compiled, or compiled and run.
+ if !bytes.HasPrefix(src, []byte("// run")) && !bytes.HasPrefix(src, []byte("// compile")) {
+ t.Logf("%s: not detected as a run test", entry.Name())
+ continue
+ }
- // Only build test files that can be compiled, or compiled and run.
- if !bytes.HasPrefix(src, []byte("// run")) && !bytes.HasPrefix(src, []byte("// compile")) {
- t.Skipf("not detected as a run test")
- }
+ filename := fmt.Sprintf("%s/main.go", entry.Name())
+ overlay[filename] = src
+ }
- t.Logf("Input: %s\n", entry.Name())
-
- _, _ = buildPackage(t, string(src), ssa.SanityCheckFunctions|ssa.InstantiateGenerics)
+ // load all packages inside the overlay so 'go list' will be triggered only once.
+ pkgs := loadPackages(t, overlayFS(overlay), "./...")
+ for _, p := range pkgs {
+ originFilename := filepath.Base(filepath.Dir(p.GoFiles[0]))
+ t.Run(originFilename, func(t *testing.T) {
+ t.Parallel()
+ prog, _ := ssautil.Packages([]*packages.Package{p}, ssa.SanityCheckFunctions|ssa.InstantiateGenerics)
+ prog.Package(p.Types).Build()
})
}
}