1
2
3
4
5 package test
6
7 import (
8 "cmd/go/internal/base"
9 "cmd/go/internal/cfg"
10 "cmd/go/internal/cmdflag"
11 "cmd/go/internal/work"
12 "errors"
13 "flag"
14 "fmt"
15 "internal/godebug"
16 "os"
17 "path/filepath"
18 "strconv"
19 "strings"
20 "time"
21 )
22
23
24
25
26
27
28
29
30 var gotestjsonbuildtext = godebug.New("gotestjsonbuildtext")
31
32 func init() {
33 work.AddBuildFlags(CmdTest, work.OmitVFlag|work.OmitJSONFlag)
34
35 cf := CmdTest.Flag
36 cf.BoolVar(&testC, "c", false, "")
37 cf.StringVar(&testO, "o", "", "")
38 work.AddCoverFlags(CmdTest, &testCoverProfile)
39 cf.Var((*base.StringsFlag)(&work.ExecCmd), "exec", "")
40 cf.BoolVar(&testJSON, "json", false, "")
41 cf.Var(&testVet, "vet", "")
42
43
44
45
46
47 cf.BoolVar(&testArtifacts, "artifacts", false, "")
48 cf.StringVar(&testBench, "bench", "", "")
49 cf.Bool("benchmem", false, "")
50 cf.String("benchtime", "", "")
51 cf.StringVar(&testBlockProfile, "blockprofile", "", "")
52 cf.String("blockprofilerate", "", "")
53 cf.Int("count", 0, "")
54 cf.String("cpu", "", "")
55 cf.StringVar(&testCPUProfile, "cpuprofile", "", "")
56 cf.BoolVar(&testFailFast, "failfast", false, "")
57 cf.StringVar(&testFuzz, "fuzz", "", "")
58 cf.Bool("fullpath", false, "")
59 cf.StringVar(&testList, "list", "", "")
60 cf.StringVar(&testMemProfile, "memprofile", "", "")
61 cf.String("memprofilerate", "", "")
62 cf.StringVar(&testMutexProfile, "mutexprofile", "", "")
63 cf.String("mutexprofilefraction", "", "")
64 cf.Var(&testOutputDir, "outputdir", "")
65 cf.Int("parallel", 0, "")
66 cf.String("run", "", "")
67 cf.Bool("short", false, "")
68 cf.String("skip", "", "")
69 cf.DurationVar(&testTimeout, "timeout", 10*time.Minute, "")
70 cf.String("fuzztime", "", "")
71 cf.String("fuzzminimizetime", "", "")
72 cf.StringVar(&testTrace, "trace", "", "")
73 cf.Var(&testV, "v", "")
74 cf.Var(&testShuffle, "shuffle", "")
75
76 for name, ok := range passFlagToTest {
77 if ok {
78 cf.Var(cf.Lookup(name).Value, "test."+name, "")
79 }
80 }
81 }
82
83
84
85 type outputdirFlag struct {
86 abs string
87 }
88
89 func (f *outputdirFlag) String() string {
90 return f.abs
91 }
92
93 func (f *outputdirFlag) Set(value string) (err error) {
94 if value == "" {
95 f.abs = ""
96 } else {
97 f.abs, err = filepath.Abs(value)
98 }
99 return err
100 }
101
102 func (f *outputdirFlag) getAbs() string {
103 if f.abs == "" {
104 return base.Cwd()
105 }
106 return f.abs
107 }
108
109
110
111
112
113
114
115
116 type vetFlag struct {
117 explicit bool
118 off bool
119 flags []string
120 }
121
122 func (f *vetFlag) String() string {
123 switch {
124 case !f.off && !f.explicit && len(f.flags) == 0:
125 return "all"
126 case f.off:
127 return "off"
128 }
129
130 var buf strings.Builder
131 for i, f := range f.flags {
132 if i > 0 {
133 buf.WriteByte(',')
134 }
135 buf.WriteString(f)
136 }
137 return buf.String()
138 }
139
140 func (f *vetFlag) Set(value string) error {
141 switch {
142 case value == "":
143 *f = vetFlag{flags: defaultVetFlags}
144 return nil
145 case strings.Contains(value, "="):
146 return fmt.Errorf("-vet argument cannot contain equal signs")
147 case strings.Contains(value, " "):
148 return fmt.Errorf("-vet argument is comma-separated list, cannot contain spaces")
149 }
150
151 *f = vetFlag{explicit: true}
152 var single string
153 for arg := range strings.SplitSeq(value, ",") {
154 switch arg {
155 case "":
156 return fmt.Errorf("-vet argument contains empty list element")
157 case "all":
158 single = arg
159 *f = vetFlag{explicit: true}
160 continue
161 case "off":
162 single = arg
163 *f = vetFlag{
164 explicit: true,
165 off: true,
166 }
167 continue
168 default:
169 if _, ok := passAnalyzersToVet[arg]; !ok {
170 return fmt.Errorf("-vet argument must be a supported analyzer or a distinguished value; found %s", arg)
171 }
172 f.flags = append(f.flags, "-"+arg)
173 }
174 }
175 if len(f.flags) > 1 && single != "" {
176 return fmt.Errorf("-vet does not accept %q in a list with other analyzers", single)
177 }
178 return nil
179 }
180
181 type shuffleFlag struct {
182 on bool
183 seed *int64
184 }
185
186 func (f *shuffleFlag) String() string {
187 if !f.on {
188 return "off"
189 }
190 if f.seed == nil {
191 return "on"
192 }
193 return fmt.Sprintf("%d", *f.seed)
194 }
195
196 func (f *shuffleFlag) Set(value string) error {
197 if value == "off" {
198 *f = shuffleFlag{on: false}
199 return nil
200 }
201
202 if value == "on" {
203 *f = shuffleFlag{on: true}
204 return nil
205 }
206
207 seed, err := strconv.ParseInt(value, 10, 64)
208 if err != nil {
209 return fmt.Errorf(`-shuffle argument must be "on", "off", or an int64: %v`, err)
210 }
211
212 *f = shuffleFlag{on: true, seed: &seed}
213 return nil
214 }
215
216
217
218
219
220
221
222
223
224
225
226 func testFlags(args []string) (packageNames, passToTest []string) {
227 base.SetFromGOFLAGS(&CmdTest.Flag)
228 addFromGOFLAGS := map[string]bool{}
229 CmdTest.Flag.Visit(func(f *flag.Flag) {
230 if short := strings.TrimPrefix(f.Name, "test."); passFlagToTest[short] {
231 addFromGOFLAGS[f.Name] = true
232 }
233 })
234
235
236
237 firstUnknownFlag := ""
238
239 explicitArgs := make([]string, 0, len(args))
240 inPkgList := false
241 afterFlagWithoutValue := false
242 for len(args) > 0 {
243 f, remainingArgs, err := cmdflag.ParseOne(&CmdTest.Flag, args)
244
245 wasAfterFlagWithoutValue := afterFlagWithoutValue
246 afterFlagWithoutValue = false
247
248 if errors.Is(err, flag.ErrHelp) {
249 exitWithUsage()
250 }
251
252 if errors.Is(err, cmdflag.ErrFlagTerminator) {
253
254
255
256
257 explicitArgs = append(explicitArgs, args...)
258 break
259 }
260
261 if nf, ok := errors.AsType[cmdflag.NonFlagError](err); ok {
262 if !inPkgList && packageNames != nil {
263
264
265
266 if wasAfterFlagWithoutValue {
267
268
269
270
271
272
273 explicitArgs = append(explicitArgs, nf.RawArg)
274 args = remainingArgs
275 continue
276 } else {
277
278
279 explicitArgs = append(explicitArgs, args...)
280 break
281 }
282 }
283
284 inPkgList = true
285 packageNames = append(packageNames, nf.RawArg)
286 args = remainingArgs
287 continue
288 }
289
290 if inPkgList {
291
292
293 inPkgList = false
294 }
295
296 if nd, ok := errors.AsType[cmdflag.FlagNotDefinedError](err); ok {
297
298
299
300
301
302
303
304 if packageNames == nil {
305 packageNames = []string{}
306 }
307
308 if nd.RawArg == "-args" || nd.RawArg == "--args" {
309
310
311 explicitArgs = append(explicitArgs, remainingArgs...)
312 break
313 }
314
315 if firstUnknownFlag == "" {
316 firstUnknownFlag = nd.RawArg
317 }
318
319 explicitArgs = append(explicitArgs, nd.RawArg)
320 args = remainingArgs
321 if !nd.HasValue {
322 afterFlagWithoutValue = true
323 }
324 continue
325 }
326
327 if err != nil {
328 fmt.Fprintln(os.Stderr, err)
329 exitWithUsage()
330 }
331
332 if short := strings.TrimPrefix(f.Name, "test."); passFlagToTest[short] {
333 explicitArgs = append(explicitArgs, fmt.Sprintf("-test.%s=%v", short, f.Value))
334
335
336
337 delete(addFromGOFLAGS, short)
338 delete(addFromGOFLAGS, "test."+short)
339 }
340
341 args = remainingArgs
342 }
343 if firstUnknownFlag != "" && testC {
344 fmt.Fprintf(os.Stderr, "go: unknown flag %s cannot be used with -c\n", firstUnknownFlag)
345 exitWithUsage()
346 }
347
348 var injectedFlags []string
349 if testJSON {
350
351
352
353
354 injectedFlags = append(injectedFlags, "-test.v=test2json")
355 delete(addFromGOFLAGS, "v")
356 delete(addFromGOFLAGS, "test.v")
357
358 if gotestjsonbuildtext.Value() == "1" {
359 gotestjsonbuildtext.IncNonDefault()
360 } else {
361 cfg.BuildJSON = true
362 }
363 }
364
365
366
367
368 var timeoutSet, outputDirSet bool
369 CmdTest.Flag.Visit(func(f *flag.Flag) {
370 short := strings.TrimPrefix(f.Name, "test.")
371 if addFromGOFLAGS[f.Name] {
372 injectedFlags = append(injectedFlags, fmt.Sprintf("-test.%s=%v", short, f.Value))
373 }
374 switch short {
375 case "timeout":
376 timeoutSet = true
377 case "outputdir":
378 outputDirSet = true
379 }
380 })
381
382
383
384
385 if testTimeout > 0 && !timeoutSet {
386 injectedFlags = append(injectedFlags, fmt.Sprintf("-test.timeout=%v", testTimeout))
387 }
388
389
390
391
392
393 needOutputDir := testProfile() != "" || testArtifacts
394 if needOutputDir && !outputDirSet {
395 injectedFlags = append(injectedFlags, "-test.outputdir="+testOutputDir.getAbs())
396 }
397
398
399
400
401
402
403
404 helpLoop:
405 for _, arg := range explicitArgs {
406 switch arg {
407 case "--":
408 break helpLoop
409 case "-h", "-help", "--help":
410 testHelp = true
411 break helpLoop
412 }
413 }
414
415
416 return packageNames, append(injectedFlags, explicitArgs...)
417 }
418
419 func exitWithUsage() {
420 fmt.Fprintf(os.Stderr, "usage: %s\n", CmdTest.UsageLine)
421 fmt.Fprintf(os.Stderr, "Run 'go help %s' and 'go help %s' for details.\n", CmdTest.LongName(), HelpTestflag.LongName())
422
423 base.SetExitStatus(2)
424 base.Exit()
425 }
426
View as plain text