go/analysis/unitchecker: NeedGoPackages in ExampleSeparateAnalysis
This example test uses go/packages and thus the go command,
so it fails on some builders. Unfortunately testenv.hasTool
is unexported, and testenv.NeedsTool et al require a testing.T,
so we have to convert this example into a test.
(This is an alternative approach to CL 523076.)
Fixes golang/go#62291
Change-Id: If821464f6d1e82c79a0dd85bfd5fc6e4f0f98d6a
Reviewed-on: https://go-review.googlesource.com/c/tools/+/523077
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
Run-TryBot: Alan Donovan <adonovan@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/go/analysis/unitchecker/separate_test.go b/go/analysis/unitchecker/separate_test.go
index 12d9104..37e74e4 100644
--- a/go/analysis/unitchecker/separate_test.go
+++ b/go/analysis/unitchecker/separate_test.go
@@ -13,21 +13,21 @@
"go/token"
"go/types"
"io"
- "log"
"os"
- "os/exec"
"path/filepath"
"strings"
"sync/atomic"
+ "testing"
"golang.org/x/tools/go/analysis/passes/printf"
"golang.org/x/tools/go/analysis/unitchecker"
"golang.org/x/tools/go/gcexportdata"
"golang.org/x/tools/go/packages"
+ "golang.org/x/tools/internal/testenv"
"golang.org/x/tools/txtar"
)
-// ExampleSeparateAnalysis demonstrates the principle of separate
+// TestExampleSeparateAnalysis demonstrates the principle of separate
// analysis, the distribution of units of type-checking and analysis
// work across several processes, using serialized summaries to
// communicate between them.
@@ -49,7 +49,12 @@
// different modes: the Example function is the manager, and the same
// executable invoked with ENTRYPOINT=worker is the worker.
// (See TestIntegration for how this happens.)
-func ExampleSeparateAnalysis() {
+//
+// Unfortunately this can't be a true Example because of the skip,
+// which requires a testing.T.
+func TestExampleSeparateAnalysis(t *testing.T) {
+ testenv.NeedsGoPackages(t)
+
// src is an archive containing a module with a printf mistake.
const src = `
-- go.mod --
@@ -76,12 +81,9 @@
`
// Expand archive into tmp tree.
- tmpdir, err := os.MkdirTemp("", "SeparateAnalysis")
- if err != nil {
- log.Fatal(err)
- }
+ tmpdir := t.TempDir()
if err := extractTxtar(txtar.Parse([]byte(src)), tmpdir); err != nil {
- log.Fatal(err)
+ t.Fatal(err)
}
// Load metadata for the main package and all its dependencies.
@@ -92,14 +94,15 @@
"GOPROXY=off", // disable network
"GOWORK=off", // an ambient GOWORK value would break package loading
),
+ Logf: t.Logf,
}
pkgs, err := packages.Load(cfg, "separate/main")
if err != nil {
- log.Fatal(err)
+ t.Fatal(err)
}
// Stop if any package had a metadata error.
if packages.PrintErrors(pkgs) > 0 {
- os.Exit(1)
+ t.Fatal("there were errors among loaded packages")
}
// Now we have loaded the import graph,
@@ -117,6 +120,8 @@
// We use it to create names of temporary files.
var nextID atomic.Int32
+ var allDiagnostics []string
+
// Visit all packages in postorder: dependencies first.
// TODO(adonovan): opt: use parallel postorder.
packages.Visit(pkgs, nil, func(pkg *packages.Package) {
@@ -165,23 +170,23 @@
// Write the JSON configuration message to a file.
cfgData, err := json.Marshal(cfg)
if err != nil {
- log.Fatal(err)
+ t.Fatalf("internal error in json.Marshal: %v", err)
}
cfgFile := prefix + ".cfg"
if err := os.WriteFile(cfgFile, cfgData, 0666); err != nil {
- log.Fatal(err)
+ t.Fatal(err)
}
// Send the request to the worker.
- cmd := exec.Command(os.Args[0], "-json", cfgFile)
+ cmd := testenv.Command(t, os.Args[0], "-json", cfgFile)
cmd.Stderr = os.Stderr
cmd.Stdout = new(bytes.Buffer)
cmd.Env = append(os.Environ(), "ENTRYPOINT=worker")
if err := cmd.Run(); err != nil {
- log.Fatal(err)
+ t.Fatal(err)
}
- // Parse JSON output and print plainly.
+ // Parse JSON output and gather in allDiagnostics.
dec := json.NewDecoder(cmd.Stdout.(io.Reader))
for {
type jsonDiagnostic struct {
@@ -194,15 +199,15 @@
if err == io.EOF {
break
}
- log.Fatal(err)
+ t.Fatalf("internal error decoding JSON: %v", err)
}
for _, result := range results {
for analyzer, diags := range result {
for _, diag := range diags {
rel := strings.ReplaceAll(diag.Posn, tmpdir, "")
rel = filepath.ToSlash(rel)
- fmt.Printf("%s: [%s] %s\n",
- rel, analyzer, diag.Message)
+ msg := fmt.Sprintf("%s: [%s] %s", rel, analyzer, diag.Message)
+ allDiagnostics = append(allDiagnostics, msg)
}
}
}
@@ -212,8 +217,10 @@
// Observe that the example produces a fact-based diagnostic
// from separate analysis of "main", "lib", and "fmt":
- // Output:
- // /main/main.go:6:2: [printf] separate/lib.MyPrintf format %s has arg 123 of wrong type int
+ const want = `/main/main.go:6:2: [printf] separate/lib.MyPrintf format %s has arg 123 of wrong type int`
+ if got := strings.Join(allDiagnostics, "\n"); got != want {
+ t.Errorf("Got: %s\nWant: %s", got, want)
+ }
}
// -- worker process --