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 if len(f.flags) > 1 && single != "" {
179 return fmt.Errorf("-vet does not accept %q in a list with other analyzers", single)
180 }
181 return nil
182 }
183
184 type shuffleFlag struct {
185 on bool
186 seed *int64
187 }
188
189 func (f *shuffleFlag) String() string {
190 if !f.on {
191 return "off"
192 }
193 if f.seed == nil {
194 return "on"
195 }
196 return fmt.Sprintf("%d", *f.seed)
197 }
198
199 func (f *shuffleFlag) Set(value string) error {
200 if value == "off" {
201 *f = shuffleFlag{on: false}
202 return nil
203 }
204
205 if value == "on" {
206 *f = shuffleFlag{on: true}
207 return nil
208 }
209
210 seed, err := strconv.ParseInt(value, 10, 64)
211 if err != nil {
212 return fmt.Errorf(`-shuffle argument must be "on", "off", or an int64: %v`, err)
213 }
214
215 *f = shuffleFlag{on: true, seed: &seed}
216 return nil
217 }
218
219
220
221
222
223
224
225
226
227
228
229 func testFlags(args []string) (packageNames, passToTest []string) {
230 base.SetFromGOFLAGS(&CmdTest.Flag)
231 addFromGOFLAGS := map[string]bool{}
232 CmdTest.Flag.Visit(func(f *flag.Flag) {
233 if short := strings.TrimPrefix(f.Name, "test."); passFlagToTest[short] {
234 addFromGOFLAGS[f.Name] = true
235 }
236 })
237
238
239
240 firstUnknownFlag := ""
241
242 explicitArgs := make([]string, 0, len(args))
243 inPkgList := false
244 afterFlagWithoutValue := false
245 for len(args) > 0 {
246 f, remainingArgs, err := cmdflag.ParseOne(&CmdTest.Flag, args)
247
248 wasAfterFlagWithoutValue := afterFlagWithoutValue
249 afterFlagWithoutValue = false
250
251 if errors.Is(err, flag.ErrHelp) {
252 exitWithUsage()
253 }
254
255 if errors.Is(err, cmdflag.ErrFlagTerminator) {
256
257
258
259
260 explicitArgs = append(explicitArgs, args...)
261 break
262 }
263
264 if nf, ok := errors.AsType[cmdflag.NonFlagError](err); ok {
265 if !inPkgList && packageNames != nil {
266
267
268
269 if wasAfterFlagWithoutValue {
270
271
272
273
274
275
276 explicitArgs = append(explicitArgs, nf.RawArg)
277 args = remainingArgs
278 continue
279 } else {
280
281
282 explicitArgs = append(explicitArgs, args...)
283 break
284 }
285 }
286
287 inPkgList = true
288 packageNames = append(packageNames, nf.RawArg)
289 args = remainingArgs
290 continue
291 }
292
293 if inPkgList {
294
295
296 inPkgList = false
297 }
298
299 if nd, ok := errors.AsType[cmdflag.FlagNotDefinedError](err); ok {
300
301
302
303
304
305
306
307 if packageNames == nil {
308 packageNames = []string{}
309 }
310
311 if nd.RawArg == "-args" || nd.RawArg == "--args" {
312
313
314 explicitArgs = append(explicitArgs, remainingArgs...)
315 break
316 }
317
318 if firstUnknownFlag == "" {
319 firstUnknownFlag = nd.RawArg
320 }
321
322 explicitArgs = append(explicitArgs, nd.RawArg)
323 args = remainingArgs
324 if !nd.HasValue {
325 afterFlagWithoutValue = true
326 }
327 continue
328 }
329
330 if err != nil {
331 fmt.Fprintln(os.Stderr, err)
332 exitWithUsage()
333 }
334
335 if short := strings.TrimPrefix(f.Name, "test."); passFlagToTest[short] {
336 explicitArgs = append(explicitArgs, fmt.Sprintf("-test.%s=%v", short, f.Value))
337
338
339
340 delete(addFromGOFLAGS, short)
341 delete(addFromGOFLAGS, "test."+short)
342 }
343
344 args = remainingArgs
345 }
346 if firstUnknownFlag != "" && testC {
347 fmt.Fprintf(os.Stderr, "go: unknown flag %s cannot be used with -c\n", firstUnknownFlag)
348 exitWithUsage()
349 }
350
351 var injectedFlags []string
352 if testJSON {
353
354
355
356
357 injectedFlags = append(injectedFlags, "-test.v=test2json")
358 delete(addFromGOFLAGS, "v")
359 delete(addFromGOFLAGS, "test.v")
360
361 if gotestjsonbuildtext.Value() == "1" {
362 gotestjsonbuildtext.IncNonDefault()
363 } else {
364 cfg.BuildJSON = true
365 }
366 }
367
368
369
370
371 var timeoutSet, outputDirSet bool
372 CmdTest.Flag.Visit(func(f *flag.Flag) {
373 short := strings.TrimPrefix(f.Name, "test.")
374 if addFromGOFLAGS[f.Name] {
375 injectedFlags = append(injectedFlags, fmt.Sprintf("-test.%s=%v", short, f.Value))
376 }
377 switch short {
378 case "timeout":
379 timeoutSet = true
380 case "outputdir":
381 outputDirSet = true
382 }
383 })
384
385
386
387
388 if testTimeout > 0 && !timeoutSet {
389 injectedFlags = append(injectedFlags, fmt.Sprintf("-test.timeout=%v", testTimeout))
390 }
391
392
393
394
395
396 needOutputDir := testProfile() != "" || testArtifacts
397 if needOutputDir && !outputDirSet {
398 injectedFlags = append(injectedFlags, "-test.outputdir="+testOutputDir.getAbs())
399 }
400
401
402
403
404
405
406
407 helpLoop:
408 for _, arg := range explicitArgs {
409 switch arg {
410 case "--":
411 break helpLoop
412 case "-h", "-help", "--help":
413 testHelp = true
414 break helpLoop
415 }
416 }
417
418
419 return packageNames, append(injectedFlags, explicitArgs...)
420 }
421
422 func exitWithUsage() {
423 fmt.Fprintf(os.Stderr, "usage: %s\n", CmdTest.UsageLine)
424 fmt.Fprintf(os.Stderr, "Run 'go help %s' and 'go help %s' for details.\n", CmdTest.LongName(), HelpTestflag.LongName())
425
426 base.SetExitStatus(2)
427 base.Exit()
428 }
429
View as plain text