Skip to content

Commit cd22603

Browse files
committed
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 <[email protected]> Reviewed-by: Bryan Mills <[email protected]> Run-TryBot: Alan Donovan <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 2191a27 commit cd22603

File tree

1 file changed

+28
-21
lines changed

1 file changed

+28
-21
lines changed

go/analysis/unitchecker/separate_test.go

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,21 @@ import (
1313
"go/token"
1414
"go/types"
1515
"io"
16-
"log"
1716
"os"
18-
"os/exec"
1917
"path/filepath"
2018
"strings"
2119
"sync/atomic"
20+
"testing"
2221

2322
"golang.org/x/tools/go/analysis/passes/printf"
2423
"golang.org/x/tools/go/analysis/unitchecker"
2524
"golang.org/x/tools/go/gcexportdata"
2625
"golang.org/x/tools/go/packages"
26+
"golang.org/x/tools/internal/testenv"
2727
"golang.org/x/tools/txtar"
2828
)
2929

30-
// ExampleSeparateAnalysis demonstrates the principle of separate
30+
// TestExampleSeparateAnalysis demonstrates the principle of separate
3131
// analysis, the distribution of units of type-checking and analysis
3232
// work across several processes, using serialized summaries to
3333
// communicate between them.
@@ -49,7 +49,12 @@ import (
4949
// different modes: the Example function is the manager, and the same
5050
// executable invoked with ENTRYPOINT=worker is the worker.
5151
// (See TestIntegration for how this happens.)
52-
func ExampleSeparateAnalysis() {
52+
//
53+
// Unfortunately this can't be a true Example because of the skip,
54+
// which requires a testing.T.
55+
func TestExampleSeparateAnalysis(t *testing.T) {
56+
testenv.NeedsGoPackages(t)
57+
5358
// src is an archive containing a module with a printf mistake.
5459
const src = `
5560
-- go.mod --
@@ -76,12 +81,9 @@ func MyPrintf(format string, args ...any) {
7681
`
7782

7883
// Expand archive into tmp tree.
79-
tmpdir, err := os.MkdirTemp("", "SeparateAnalysis")
80-
if err != nil {
81-
log.Fatal(err)
82-
}
84+
tmpdir := t.TempDir()
8385
if err := extractTxtar(txtar.Parse([]byte(src)), tmpdir); err != nil {
84-
log.Fatal(err)
86+
t.Fatal(err)
8587
}
8688

8789
// Load metadata for the main package and all its dependencies.
@@ -92,14 +94,15 @@ func MyPrintf(format string, args ...any) {
9294
"GOPROXY=off", // disable network
9395
"GOWORK=off", // an ambient GOWORK value would break package loading
9496
),
97+
Logf: t.Logf,
9598
}
9699
pkgs, err := packages.Load(cfg, "separate/main")
97100
if err != nil {
98-
log.Fatal(err)
101+
t.Fatal(err)
99102
}
100103
// Stop if any package had a metadata error.
101104
if packages.PrintErrors(pkgs) > 0 {
102-
os.Exit(1)
105+
t.Fatal("there were errors among loaded packages")
103106
}
104107

105108
// Now we have loaded the import graph,
@@ -117,6 +120,8 @@ func MyPrintf(format string, args ...any) {
117120
// We use it to create names of temporary files.
118121
var nextID atomic.Int32
119122

123+
var allDiagnostics []string
124+
120125
// Visit all packages in postorder: dependencies first.
121126
// TODO(adonovan): opt: use parallel postorder.
122127
packages.Visit(pkgs, nil, func(pkg *packages.Package) {
@@ -165,23 +170,23 @@ func MyPrintf(format string, args ...any) {
165170
// Write the JSON configuration message to a file.
166171
cfgData, err := json.Marshal(cfg)
167172
if err != nil {
168-
log.Fatal(err)
173+
t.Fatalf("internal error in json.Marshal: %v", err)
169174
}
170175
cfgFile := prefix + ".cfg"
171176
if err := os.WriteFile(cfgFile, cfgData, 0666); err != nil {
172-
log.Fatal(err)
177+
t.Fatal(err)
173178
}
174179

175180
// Send the request to the worker.
176-
cmd := exec.Command(os.Args[0], "-json", cfgFile)
181+
cmd := testenv.Command(t, os.Args[0], "-json", cfgFile)
177182
cmd.Stderr = os.Stderr
178183
cmd.Stdout = new(bytes.Buffer)
179184
cmd.Env = append(os.Environ(), "ENTRYPOINT=worker")
180185
if err := cmd.Run(); err != nil {
181-
log.Fatal(err)
186+
t.Fatal(err)
182187
}
183188

184-
// Parse JSON output and print plainly.
189+
// Parse JSON output and gather in allDiagnostics.
185190
dec := json.NewDecoder(cmd.Stdout.(io.Reader))
186191
for {
187192
type jsonDiagnostic struct {
@@ -194,15 +199,15 @@ func MyPrintf(format string, args ...any) {
194199
if err == io.EOF {
195200
break
196201
}
197-
log.Fatal(err)
202+
t.Fatalf("internal error decoding JSON: %v", err)
198203
}
199204
for _, result := range results {
200205
for analyzer, diags := range result {
201206
for _, diag := range diags {
202207
rel := strings.ReplaceAll(diag.Posn, tmpdir, "")
203208
rel = filepath.ToSlash(rel)
204-
fmt.Printf("%s: [%s] %s\n",
205-
rel, analyzer, diag.Message)
209+
msg := fmt.Sprintf("%s: [%s] %s", rel, analyzer, diag.Message)
210+
allDiagnostics = append(allDiagnostics, msg)
206211
}
207212
}
208213
}
@@ -212,8 +217,10 @@ func MyPrintf(format string, args ...any) {
212217
// Observe that the example produces a fact-based diagnostic
213218
// from separate analysis of "main", "lib", and "fmt":
214219

215-
// Output:
216-
// /main/main.go:6:2: [printf] separate/lib.MyPrintf format %s has arg 123 of wrong type int
220+
const want = `/main/main.go:6:2: [printf] separate/lib.MyPrintf format %s has arg 123 of wrong type int`
221+
if got := strings.Join(allDiagnostics, "\n"); got != want {
222+
t.Errorf("Got: %s\nWant: %s", got, want)
223+
}
217224
}
218225

219226
// -- worker process --

0 commit comments

Comments
 (0)