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