@@ -13,21 +13,21 @@ import (
13
13
"go/token"
14
14
"go/types"
15
15
"io"
16
- "log"
17
16
"os"
18
- "os/exec"
19
17
"path/filepath"
20
18
"strings"
21
19
"sync/atomic"
20
+ "testing"
22
21
23
22
"golang.org/x/tools/go/analysis/passes/printf"
24
23
"golang.org/x/tools/go/analysis/unitchecker"
25
24
"golang.org/x/tools/go/gcexportdata"
26
25
"golang.org/x/tools/go/packages"
26
+ "golang.org/x/tools/internal/testenv"
27
27
"golang.org/x/tools/txtar"
28
28
)
29
29
30
- // ExampleSeparateAnalysis demonstrates the principle of separate
30
+ // TestExampleSeparateAnalysis demonstrates the principle of separate
31
31
// analysis, the distribution of units of type-checking and analysis
32
32
// work across several processes, using serialized summaries to
33
33
// communicate between them.
@@ -49,7 +49,12 @@ import (
49
49
// different modes: the Example function is the manager, and the same
50
50
// executable invoked with ENTRYPOINT=worker is the worker.
51
51
// (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
+
53
58
// src is an archive containing a module with a printf mistake.
54
59
const src = `
55
60
-- go.mod --
@@ -76,12 +81,9 @@ func MyPrintf(format string, args ...any) {
76
81
`
77
82
78
83
// Expand archive into tmp tree.
79
- tmpdir , err := os .MkdirTemp ("" , "SeparateAnalysis" )
80
- if err != nil {
81
- log .Fatal (err )
82
- }
84
+ tmpdir := t .TempDir ()
83
85
if err := extractTxtar (txtar .Parse ([]byte (src )), tmpdir ); err != nil {
84
- log .Fatal (err )
86
+ t .Fatal (err )
85
87
}
86
88
87
89
// Load metadata for the main package and all its dependencies.
@@ -92,14 +94,15 @@ func MyPrintf(format string, args ...any) {
92
94
"GOPROXY=off" , // disable network
93
95
"GOWORK=off" , // an ambient GOWORK value would break package loading
94
96
),
97
+ Logf : t .Logf ,
95
98
}
96
99
pkgs , err := packages .Load (cfg , "separate/main" )
97
100
if err != nil {
98
- log .Fatal (err )
101
+ t .Fatal (err )
99
102
}
100
103
// Stop if any package had a metadata error.
101
104
if packages .PrintErrors (pkgs ) > 0 {
102
- os . Exit ( 1 )
105
+ t . Fatal ( "there were errors among loaded packages" )
103
106
}
104
107
105
108
// Now we have loaded the import graph,
@@ -117,6 +120,8 @@ func MyPrintf(format string, args ...any) {
117
120
// We use it to create names of temporary files.
118
121
var nextID atomic.Int32
119
122
123
+ var allDiagnostics []string
124
+
120
125
// Visit all packages in postorder: dependencies first.
121
126
// TODO(adonovan): opt: use parallel postorder.
122
127
packages .Visit (pkgs , nil , func (pkg * packages.Package ) {
@@ -165,23 +170,23 @@ func MyPrintf(format string, args ...any) {
165
170
// Write the JSON configuration message to a file.
166
171
cfgData , err := json .Marshal (cfg )
167
172
if err != nil {
168
- log . Fatal ( err )
173
+ t . Fatalf ( "internal error in json.Marshal: %v" , err )
169
174
}
170
175
cfgFile := prefix + ".cfg"
171
176
if err := os .WriteFile (cfgFile , cfgData , 0666 ); err != nil {
172
- log .Fatal (err )
177
+ t .Fatal (err )
173
178
}
174
179
175
180
// 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 )
177
182
cmd .Stderr = os .Stderr
178
183
cmd .Stdout = new (bytes.Buffer )
179
184
cmd .Env = append (os .Environ (), "ENTRYPOINT=worker" )
180
185
if err := cmd .Run (); err != nil {
181
- log .Fatal (err )
186
+ t .Fatal (err )
182
187
}
183
188
184
- // Parse JSON output and print plainly .
189
+ // Parse JSON output and gather in allDiagnostics .
185
190
dec := json .NewDecoder (cmd .Stdout .(io.Reader ))
186
191
for {
187
192
type jsonDiagnostic struct {
@@ -194,15 +199,15 @@ func MyPrintf(format string, args ...any) {
194
199
if err == io .EOF {
195
200
break
196
201
}
197
- log . Fatal ( err )
202
+ t . Fatalf ( "internal error decoding JSON: %v" , err )
198
203
}
199
204
for _ , result := range results {
200
205
for analyzer , diags := range result {
201
206
for _ , diag := range diags {
202
207
rel := strings .ReplaceAll (diag .Posn , tmpdir , "" )
203
208
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 )
206
211
}
207
212
}
208
213
}
@@ -212,8 +217,10 @@ func MyPrintf(format string, args ...any) {
212
217
// Observe that the example produces a fact-based diagnostic
213
218
// from separate analysis of "main", "lib", and "fmt":
214
219
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\n Want: %s" , got , want )
223
+ }
217
224
}
218
225
219
226
// -- worker process --
0 commit comments