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