1
2
3
4
5 package test
6
7 import (
8 "bytes"
9 "context"
10 "errors"
11 "fmt"
12 "internal/coverage"
13 "internal/platform"
14 "io"
15 "io/fs"
16 "os"
17 "os/exec"
18 "path/filepath"
19 "regexp"
20 "slices"
21 "strconv"
22 "strings"
23 "sync"
24 "sync/atomic"
25 "time"
26
27 "cmd/go/internal/base"
28 "cmd/go/internal/cache"
29 "cmd/go/internal/cfg"
30 "cmd/go/internal/load"
31 "cmd/go/internal/lockedfile"
32 "cmd/go/internal/modload"
33 "cmd/go/internal/search"
34 "cmd/go/internal/str"
35 "cmd/go/internal/trace"
36 "cmd/go/internal/work"
37 "cmd/internal/test2json"
38
39 "golang.org/x/mod/module"
40 )
41
42
43 func init() {
44 CmdTest.Run = runTest
45 }
46
47 const testUsage = "go test [build/test flags] [packages] [build/test flags & test binary flags]"
48
49 var CmdTest = &base.Command{
50 CustomFlags: true,
51 UsageLine: testUsage,
52 Short: "test packages",
53 Long: `
54 'Go test' automates testing the packages named by the import paths.
55 It prints a summary of the test results in the format:
56
57 ok archive/tar 0.011s
58 FAIL archive/zip 0.022s
59 ok compress/gzip 0.033s
60 ...
61
62 followed by detailed output for each failed package.
63
64 'Go test' recompiles each package along with any files with names matching
65 the file pattern "*_test.go".
66 These additional files can contain test functions, benchmark functions, fuzz
67 tests and example functions. See 'go help testfunc' for more.
68 Each listed package causes the execution of a separate test binary.
69 Files whose names begin with "_" (including "_test.go") or "." are ignored.
70
71 Test files that declare a package with the suffix "_test" will be compiled as a
72 separate package, and then linked and run with the main test binary.
73
74 The go tool will ignore a directory named "testdata", making it available
75 to hold ancillary data needed by the tests.
76
77 As part of building a test binary, go test runs go vet on the package
78 and its test source files to identify significant problems. If go vet
79 finds any problems, go test reports those and does not run the test
80 binary. Only a high-confidence subset of the default go vet checks are
81 used. That subset is: atomic, bool, buildtags, directive, errorsas,
82 ifaceassert, nilfunc, printf, stringintconv, and tests. You can see
83 the documentation for these and other vet tests via "go doc cmd/vet".
84 To disable the running of go vet, use the -vet=off flag. To run all
85 checks, use the -vet=all flag.
86
87 All test output and summary lines are printed to the go command's
88 standard output, even if the test printed them to its own standard
89 error. (The go command's standard error is reserved for printing
90 errors building the tests.)
91
92 The go command places $GOROOT/bin at the beginning of $PATH
93 in the test's environment, so that tests that execute
94 'go' commands use the same 'go' as the parent 'go test' command.
95
96 Go test runs in two different modes:
97
98 The first, called local directory mode, occurs when go test is
99 invoked with no package arguments (for example, 'go test' or 'go
100 test -v'). In this mode, go test compiles the package sources and
101 tests found in the current directory and then runs the resulting
102 test binary. In this mode, caching (discussed below) is disabled.
103 After the package test finishes, go test prints a summary line
104 showing the test status ('ok' or 'FAIL'), package name, and elapsed
105 time.
106
107 The second, called package list mode, occurs when go test is invoked
108 with explicit package arguments (for example 'go test math', 'go
109 test ./...', and even 'go test .'). In this mode, go test compiles
110 and tests each of the packages listed on the command line. If a
111 package test passes, go test prints only the final 'ok' summary
112 line. If a package test fails, go test prints the full test output.
113 If invoked with the -bench or -v flag, go test prints the full
114 output even for passing package tests, in order to display the
115 requested benchmark results or verbose logging. After the package
116 tests for all of the listed packages finish, and their output is
117 printed, go test prints a final 'FAIL' status if any package test
118 has failed.
119
120 In package list mode only, go test caches successful package test
121 results to avoid unnecessary repeated running of tests. When the
122 result of a test can be recovered from the cache, go test will
123 redisplay the previous output instead of running the test binary
124 again. When this happens, go test prints '(cached)' in place of the
125 elapsed time in the summary line.
126
127 The rule for a match in the cache is that the run involves the same
128 test binary and the flags on the command line come entirely from a
129 restricted set of 'cacheable' test flags, defined as -benchtime,
130 -coverprofile, -cpu, -failfast, -fullpath, -list, -outputdir, -parallel,
131 -run, -short, -skip, -timeout and -v.
132 If a run of go test has any test or non-test flags outside this set,
133 the result is not cached. To disable test caching, use any test flag
134 or argument other than the cacheable flags. The idiomatic way to disable
135 test caching explicitly is to use -count=1. Tests that open files within
136 the package's module or that consult environment variables only
137 match future runs in which the files and environment variables are
138 unchanged. A cached test result is treated as executing in no time
139 at all, so a successful package test result will be cached and
140 reused regardless of -timeout setting.
141
142 In addition to the build flags, the flags handled by 'go test' itself are:
143
144 -args
145 Pass the remainder of the command line (everything after -args)
146 to the test binary, uninterpreted and unchanged.
147 Because this flag consumes the remainder of the command line,
148 the package list (if present) must appear before this flag.
149
150 -c
151 Compile the test binary to pkg.test in the current directory but do not run it
152 (where pkg is the last element of the package's import path).
153 The file name or target directory can be changed with the -o flag.
154
155 -exec xprog
156 Run the test binary using xprog. The behavior is the same as
157 in 'go run'. See 'go help run' for details.
158
159 -json
160 Convert test output to JSON suitable for automated processing.
161 See 'go doc test2json' for the encoding details.
162 Also emits build output in JSON. See 'go help buildjson'.
163
164 -o file
165 Save a copy of the test binary to the named file.
166 The test still runs (unless -c is specified).
167 If file ends in a slash or names an existing directory,
168 the test is written to pkg.test in that directory.
169
170 The test binary also accepts flags that control execution of the test; these
171 flags are also accessible by 'go test'. See 'go help testflag' for details.
172
173 For more about build flags, see 'go help build'.
174 For more about specifying packages, see 'go help packages'.
175
176 See also: go build, go vet.
177 `,
178 }
179
180 var HelpTestflag = &base.Command{
181 UsageLine: "testflag",
182 Short: "testing flags",
183 Long: `
184 The 'go test' command takes both flags that apply to 'go test' itself
185 and flags that apply to the resulting test binary.
186
187 Several of the flags control profiling and write an execution profile
188 suitable for "go tool pprof"; run "go tool pprof -h" for more
189 information. The -sample_index=alloc_space, -sample_index=alloc_objects,
190 and -show_bytes options of pprof control how the information is presented.
191
192 The following flags are recognized by the 'go test' command and
193 control the execution of any test:
194
195 -artifacts
196 Save test artifacts in the directory specified by -outputdir.
197 See 'go doc testing.T.ArtifactDir'.
198
199 -bench regexp
200 Run only those benchmarks matching a regular expression.
201 By default, no benchmarks are run.
202 To run all benchmarks, use '-bench .' or '-bench=.'.
203 The regular expression is split by unbracketed slash (/)
204 characters into a sequence of regular expressions, and each
205 part of a benchmark's identifier must match the corresponding
206 element in the sequence, if any. Possible parents of matches
207 are run with b.N=1 to identify sub-benchmarks. For example,
208 given -bench=X/Y, top-level benchmarks matching X are run
209 with b.N=1 to find any sub-benchmarks matching Y, which are
210 then run in full.
211
212 -benchtime t
213 Run enough iterations of each benchmark to take t, specified
214 as a time.Duration (for example, -benchtime 1h30s).
215 The default is 1 second (1s).
216 The special syntax Nx means to run the benchmark N times
217 (for example, -benchtime 100x).
218
219 -count n
220 Run each test, benchmark, and fuzz seed n times (default 1).
221 If -cpu is set, run n times for each GOMAXPROCS value.
222 Examples are always run once. -count does not apply to
223 fuzz tests matched by -fuzz.
224
225 -cover
226 Enable coverage analysis.
227 Note that because coverage works by annotating the source
228 code before compilation, compilation and test failures with
229 coverage enabled may report line numbers that don't correspond
230 to the original sources.
231
232 -covermode set,count,atomic
233 Set the mode for coverage analysis for the package[s]
234 being tested. The default is "set" unless -race is enabled,
235 in which case it is "atomic".
236 The values:
237 set: bool: does this statement run?
238 count: int: how many times does this statement run?
239 atomic: int: count, but correct in multithreaded tests;
240 significantly more expensive.
241 Sets -cover.
242
243 -coverpkg pattern1,pattern2,pattern3
244 Apply coverage analysis in each test to packages whose import paths
245 match the patterns. The default is for each test to analyze only
246 the package being tested. See 'go help packages' for a description
247 of package patterns. Sets -cover.
248
249 -cpu 1,2,4
250 Specify a list of GOMAXPROCS values for which the tests, benchmarks or
251 fuzz tests should be executed. The default is the current value
252 of GOMAXPROCS. -cpu does not apply to fuzz tests matched by -fuzz.
253
254 -failfast
255 Do not start new tests after the first test failure.
256
257 -fullpath
258 Show full file names in the error messages.
259
260 -fuzz regexp
261 Run the fuzz test matching the regular expression. When specified,
262 the command line argument must match exactly one package within the
263 main module, and regexp must match exactly one fuzz test within
264 that package. Fuzzing will occur after tests, benchmarks, seed corpora
265 of other fuzz tests, and examples have completed. See the Fuzzing
266 section of the testing package documentation for details.
267
268 -fuzztime t
269 Run enough iterations of the fuzz target during fuzzing to take t,
270 specified as a time.Duration (for example, -fuzztime 1h30s).
271 The default is to run forever.
272 The special syntax Nx means to run the fuzz target N times
273 (for example, -fuzztime 1000x).
274
275 -fuzzminimizetime t
276 Run enough iterations of the fuzz target during each minimization
277 attempt to take t, as specified as a time.Duration (for example,
278 -fuzzminimizetime 30s).
279 The default is 60s.
280 The special syntax Nx means to run the fuzz target N times
281 (for example, -fuzzminimizetime 100x).
282
283 -json
284 Log verbose output and test results in JSON. This presents the
285 same information as the -v flag in a machine-readable format.
286
287 -list regexp
288 List tests, benchmarks, fuzz tests, or examples matching the regular
289 expression. No tests, benchmarks, fuzz tests, or examples will be run.
290 This will only list top-level tests. No subtest or subbenchmarks will be
291 shown.
292
293 -outputdir directory
294 Place output files from profiling and test artifacts in the
295 specified directory, by default the directory in which "go test" is running.
296
297 -parallel n
298 Allow parallel execution of test functions that call t.Parallel, and
299 fuzz targets that call t.Parallel when running the seed corpus.
300 The value of this flag is the maximum number of tests to run
301 simultaneously.
302 While fuzzing, the value of this flag is the maximum number of
303 subprocesses that may call the fuzz function simultaneously, regardless of
304 whether T.Parallel is called.
305 By default, -parallel is set to the value of GOMAXPROCS.
306 Setting -parallel to values higher than GOMAXPROCS may cause degraded
307 performance due to CPU contention, especially when fuzzing.
308 Note that -parallel only applies within a single test binary.
309 The 'go test' command may run tests for different packages
310 in parallel as well, according to the setting of the -p flag
311 (see 'go help build').
312
313 -run regexp
314 Run only those tests, examples, and fuzz tests matching the regular
315 expression. For tests, the regular expression is split by unbracketed
316 slash (/) characters into a sequence of regular expressions, and each
317 part of a test's identifier must match the corresponding element in
318 the sequence, if any. Note that possible parents of matches are
319 run too, so that -run=X/Y matches and runs and reports the result
320 of all tests matching X, even those without sub-tests matching Y,
321 because it must run them to look for those sub-tests.
322 See also -skip.
323
324 -short
325 Tell long-running tests to shorten their run time.
326 It is off by default but set during all.bash so that installing
327 the Go tree can run a sanity check but not spend time running
328 exhaustive tests.
329
330 -shuffle off,on,N
331 Randomize the execution order of tests and benchmarks.
332 It is off by default. If -shuffle is set to on, then it will seed
333 the randomizer using the system clock. If -shuffle is set to an
334 integer N, then N will be used as the seed value. In both cases,
335 the seed will be reported for reproducibility.
336
337 -skip regexp
338 Run only those tests, examples, fuzz tests, and benchmarks that
339 do not match the regular expression. Like for -run and -bench,
340 for tests and benchmarks, the regular expression is split by unbracketed
341 slash (/) characters into a sequence of regular expressions, and each
342 part of a test's identifier must match the corresponding element in
343 the sequence, if any.
344
345 -timeout d
346 If a test binary runs longer than duration d, panic.
347 If d is 0, the timeout is disabled.
348 The default is 10 minutes (10m).
349
350 -v
351 Verbose output: log all tests as they are run. Also print all
352 text from Log and Logf calls even if the test succeeds.
353
354 -vet list
355 Configure the invocation of "go vet" during "go test"
356 to use the comma-separated list of vet checks.
357 If list is empty, "go test" runs "go vet" with a curated list of
358 checks believed to be always worth addressing.
359 If list is "off", "go test" does not run "go vet" at all.
360
361 The following flags are also recognized by 'go test' and can be used to
362 profile the tests during execution:
363
364 -benchmem
365 Print memory allocation statistics for benchmarks.
366 Allocations made in C or using C.malloc are not counted.
367
368 -blockprofile block.out
369 Write a goroutine blocking profile to the specified file
370 when all tests are complete.
371 Writes test binary as -c would.
372
373 -blockprofilerate n
374 Control the detail provided in goroutine blocking profiles by
375 calling runtime.SetBlockProfileRate with n.
376 See 'go doc runtime.SetBlockProfileRate'.
377 The profiler aims to sample, on average, one blocking event every
378 n nanoseconds the program spends blocked. By default,
379 if -test.blockprofile is set without this flag, all blocking events
380 are recorded, equivalent to -test.blockprofilerate=1.
381
382 -coverprofile cover.out
383 Write a coverage profile to the file after all tests have passed.
384 Sets -cover.
385
386 -cpuprofile cpu.out
387 Write a CPU profile to the specified file before exiting.
388 Writes test binary as -c would.
389
390 -memprofile mem.out
391 Write an allocation profile to the file after all tests have passed.
392 Writes test binary as -c would.
393
394 -memprofilerate n
395 Enable more precise (and expensive) memory allocation profiles by
396 setting runtime.MemProfileRate. See 'go doc runtime.MemProfileRate'.
397 To profile all memory allocations, use -test.memprofilerate=1.
398
399 -mutexprofile mutex.out
400 Write a mutex contention profile to the specified file
401 when all tests are complete.
402 Writes test binary as -c would.
403
404 -mutexprofilefraction n
405 Sample 1 in n stack traces of goroutines holding a
406 contended mutex.
407
408 -trace trace.out
409 Write an execution trace to the specified file before exiting.
410
411 Each of these flags is also recognized with an optional 'test.' prefix,
412 as in -test.v. When invoking the generated test binary (the result of
413 'go test -c') directly, however, the prefix is mandatory.
414
415 The 'go test' command rewrites or removes recognized flags,
416 as appropriate, both before and after the optional package list,
417 before invoking the test binary.
418
419 For instance, the command
420
421 go test -v -myflag testdata -cpuprofile=prof.out -x
422
423 will compile the test binary and then run it as
424
425 pkg.test -test.v -myflag testdata -test.cpuprofile=prof.out
426
427 (The -x flag is removed because it applies only to the go command's
428 execution, not to the test itself.)
429
430 The test flags that generate profiles (other than for coverage) also
431 leave the test binary in pkg.test for use when analyzing the profiles.
432
433 When 'go test' runs a test binary, it does so from within the
434 corresponding package's source code directory. Depending on the test,
435 it may be necessary to do the same when invoking a generated test
436 binary directly. Because that directory may be located within the
437 module cache, which may be read-only and is verified by checksums, the
438 test must not write to it or any other directory within the module
439 unless explicitly requested by the user (such as with the -fuzz flag,
440 which writes failures to testdata/fuzz).
441
442 The command-line package list, if present, must appear before any
443 flag not known to the go test command. Continuing the example above,
444 the package list would have to appear before -myflag, but could appear
445 on either side of -v.
446
447 When 'go test' runs in package list mode, 'go test' caches successful
448 package test results to avoid unnecessary repeated running of tests. To
449 disable test caching, use any test flag or argument other than the
450 cacheable flags. The idiomatic way to disable test caching explicitly
451 is to use -count=1.
452
453 To keep an argument for a test binary from being interpreted as a
454 known flag or a package name, use -args (see 'go help test') which
455 passes the remainder of the command line through to the test binary
456 uninterpreted and unaltered.
457
458 For instance, the command
459
460 go test -v -args -x -v
461
462 will compile the test binary and then run it as
463
464 pkg.test -test.v -x -v
465
466 Similarly,
467
468 go test -args math
469
470 will compile the test binary and then run it as
471
472 pkg.test math
473
474 In the first example, the -x and the second -v are passed through to the
475 test binary unchanged and with no effect on the go command itself.
476 In the second example, the argument math is passed through to the test
477 binary, instead of being interpreted as the package list.
478 `,
479 }
480
481 var HelpTestfunc = &base.Command{
482 UsageLine: "testfunc",
483 Short: "testing functions",
484 Long: `
485 The 'go test' command expects to find test, benchmark, and example functions
486 in the "*_test.go" files corresponding to the package under test.
487
488 A test function is one named TestXxx (where Xxx does not start with a
489 lower case letter) and should have the signature,
490
491 func TestXxx(t *testing.T) { ... }
492
493 A benchmark function is one named BenchmarkXxx and should have the signature,
494
495 func BenchmarkXxx(b *testing.B) { ... }
496
497 A fuzz test is one named FuzzXxx and should have the signature,
498
499 func FuzzXxx(f *testing.F) { ... }
500
501 An example function is similar to a test function but, instead of using
502 *testing.T to report success or failure, prints output to os.Stdout.
503 If the last comment in the function starts with "Output:" then the output
504 is compared exactly against the comment (see examples below). If the last
505 comment begins with "Unordered output:" then the output is compared to the
506 comment, however the order of the lines is ignored. An example with no such
507 comment is compiled but not executed. An example with no text after
508 "Output:" is compiled, executed, and expected to produce no output.
509
510 Godoc displays the body of ExampleXxx to demonstrate the use
511 of the function, constant, or variable Xxx. An example of a method M with
512 receiver type T or *T is named ExampleT_M. There may be multiple examples
513 for a given function, constant, or variable, distinguished by a trailing _xxx,
514 where xxx is a suffix not beginning with an upper case letter.
515
516 Here is an example of an example:
517
518 func ExamplePrintln() {
519 Println("The output of\nthis example.")
520 // Output: The output of
521 // this example.
522 }
523
524 Here is another example where the ordering of the output is ignored:
525
526 func ExamplePerm() {
527 for _, value := range Perm(4) {
528 fmt.Println(value)
529 }
530
531 // Unordered output: 4
532 // 2
533 // 1
534 // 3
535 // 0
536 }
537
538 The entire test file is presented as the example when it contains a single
539 example function, at least one other function, type, variable, or constant
540 declaration, and no tests, benchmarks, or fuzz tests.
541
542 See the documentation of the testing package for more information.
543 `,
544 }
545
546 var (
547 testArtifacts bool
548 testBench string
549 testC bool
550 testCoverPkgs []*load.Package
551 testCoverProfile string
552 testFailFast bool
553 testFuzz string
554 testJSON bool
555 testList string
556 testO string
557 testOutputDir outputdirFlag
558 testShuffle shuffleFlag
559 testTimeout time.Duration
560 testV testVFlag
561 testVet = vetFlag{flags: defaultVetFlags}
562 )
563
564 type testVFlag struct {
565 on bool
566 json bool
567 }
568
569 func (*testVFlag) IsBoolFlag() bool { return true }
570
571 func (f *testVFlag) Set(arg string) error {
572 if v, err := strconv.ParseBool(arg); err == nil {
573 f.on = v
574 f.json = false
575 return nil
576 }
577 if arg == "test2json" {
578 f.on = true
579 f.json = true
580 return nil
581 }
582 return fmt.Errorf("invalid flag -test.v=%s", arg)
583 }
584
585 func (f *testVFlag) String() string {
586 if f.json {
587 return "test2json"
588 }
589 if f.on {
590 return "true"
591 }
592 return "false"
593 }
594
595 var (
596 testArgs []string
597 pkgArgs []string
598 pkgs []*load.Package
599
600 testHelp bool
601
602 testKillTimeout = 100 * 365 * 24 * time.Hour
603 testWaitDelay time.Duration
604 testCacheExpire time.Time
605 testShouldFailFast atomic.Bool
606
607 testBlockProfile, testCPUProfile, testMemProfile, testMutexProfile, testTrace string
608
609 testODir = false
610 )
611
612
613
614 func testProfile() string {
615 switch {
616 case testBlockProfile != "":
617 return "-blockprofile"
618 case testCPUProfile != "":
619 return "-cpuprofile"
620 case testMemProfile != "":
621 return "-memprofile"
622 case testMutexProfile != "":
623 return "-mutexprofile"
624 case testTrace != "":
625 return "-trace"
626 default:
627 return ""
628 }
629 }
630
631
632 func testNeedBinary() bool {
633 switch {
634 case testBlockProfile != "":
635 return true
636 case testCPUProfile != "":
637 return true
638 case testMemProfile != "":
639 return true
640 case testMutexProfile != "":
641 return true
642 case testO != "":
643 return true
644 default:
645 return false
646 }
647 }
648
649
650 func testShowPass() bool {
651 return testV.on || testList != "" || testHelp
652 }
653
654 var defaultVetFlags = []string{
655
656
657
658
659 "-atomic",
660 "-bool",
661 "-buildtags",
662
663
664
665 "-directive",
666 "-errorsas",
667
668 "-ifaceassert",
669
670
671 "-nilfunc",
672 "-printf",
673
674
675 "-slog",
676 "-stringintconv",
677
678 "-tests",
679
680
681
682 }
683
684 func runTest(ctx context.Context, cmd *base.Command, args []string) {
685 moduleLoaderState := modload.NewState()
686 pkgArgs, testArgs = testFlags(args)
687 moduleLoaderState.InitWorkfile()
688
689 if cfg.DebugTrace != "" {
690 var close func() error
691 var err error
692 ctx, close, err = trace.Start(ctx, cfg.DebugTrace)
693 if err != nil {
694 base.Fatalf("failed to start trace: %v", err)
695 }
696 defer func() {
697 if err := close(); err != nil {
698 base.Fatalf("failed to stop trace: %v", err)
699 }
700 }()
701 }
702
703 ctx, span := trace.StartSpan(ctx, fmt.Sprint("Running ", cmd.Name(), " command"))
704 defer span.Done()
705
706 work.FindExecCmd()
707
708 work.BuildInit(moduleLoaderState)
709 work.VetFlags = testVet.flags
710 work.VetExplicit = testVet.explicit
711 work.VetTool = base.Tool("vet")
712
713 pkgOpts := load.PackageOpts{ModResolveTests: true}
714 pkgs = load.PackagesAndErrors(moduleLoaderState, ctx, pkgOpts, pkgArgs)
715
716
717 if len(pkgs) == 0 {
718 base.Fatalf("no packages to test")
719 }
720
721 if testFuzz != "" {
722 if !platform.FuzzSupported(cfg.Goos, cfg.Goarch) {
723 base.Fatalf("-fuzz flag is not supported on %s/%s", cfg.Goos, cfg.Goarch)
724 }
725 if len(pkgs) != 1 {
726 base.Fatalf("cannot use -fuzz flag with multiple packages")
727 }
728 if testCoverProfile != "" {
729 base.Fatalf("cannot use -coverprofile flag with -fuzz flag")
730 }
731 if profileFlag := testProfile(); profileFlag != "" {
732 base.Fatalf("cannot use %s flag with -fuzz flag", profileFlag)
733 }
734
735
736
737
738
739
740 mainMods := moduleLoaderState.MainModules
741 if m := pkgs[0].Module; m != nil && m.Path != "" {
742 if !mainMods.Contains(m.Path) {
743 base.Fatalf("cannot use -fuzz flag on package outside the main module")
744 }
745 } else if pkgs[0].Standard && moduleLoaderState.Enabled() {
746
747
748
749
750
751
752
753
754
755
756 if strings.HasPrefix(pkgs[0].ImportPath, "cmd/") {
757 if !mainMods.Contains("cmd") || !mainMods.InGorootSrc(module.Version{Path: "cmd"}) {
758 base.Fatalf("cannot use -fuzz flag on package outside the main module")
759 }
760 } else {
761 if !mainMods.Contains("std") || !mainMods.InGorootSrc(module.Version{Path: "std"}) {
762 base.Fatalf("cannot use -fuzz flag on package outside the main module")
763 }
764 }
765 }
766 }
767 if testProfile() != "" && len(pkgs) != 1 {
768 base.Fatalf("cannot use %s flag with multiple packages", testProfile())
769 }
770
771 if testO != "" {
772 if strings.HasSuffix(testO, "/") || strings.HasSuffix(testO, string(os.PathSeparator)) {
773 testODir = true
774 } else if fi, err := os.Stat(testO); err == nil && fi.IsDir() {
775 testODir = true
776 }
777 }
778
779 if len(pkgs) > 1 && (testC || testO != "") && !base.IsNull(testO) {
780 if testO != "" && !testODir {
781 base.Fatalf("with multiple packages, -o must refer to a directory or %s", os.DevNull)
782 }
783
784 pkgsForBinary := map[string][]*load.Package{}
785
786 for _, p := range pkgs {
787 testBinary := testBinaryName(p)
788 pkgsForBinary[testBinary] = append(pkgsForBinary[testBinary], p)
789 }
790
791 for testBinary, pkgs := range pkgsForBinary {
792 if len(pkgs) > 1 {
793 var buf strings.Builder
794 for _, pkg := range pkgs {
795 buf.WriteString(pkg.ImportPath)
796 buf.WriteString("\n")
797 }
798
799 base.Errorf("cannot write test binary %s for multiple packages:\n%s", testBinary, buf.String())
800 }
801 }
802
803 base.ExitIfErrors()
804 }
805
806 initCoverProfile()
807 defer closeCoverProfile()
808
809
810
811
812
813
814
815 if testTimeout > 0 && testFuzz == "" && testBench == "" {
816
817
818
819
820
821
822
823
824
825
826 if wd := testTimeout / 10; wd < 5*time.Second {
827 testWaitDelay = 5 * time.Second
828 } else {
829 testWaitDelay = wd
830 }
831
832
833
834
835
836
837
838
839
840 if testWaitDelay < 1*time.Minute {
841 testKillTimeout = testTimeout + 1*time.Minute
842 } else {
843 testKillTimeout = testTimeout + testWaitDelay
844 }
845 }
846
847
848
849
850 if dir, _, _ := cache.DefaultDir(); dir != "off" {
851 if data, _ := lockedfile.Read(filepath.Join(dir, "testexpire.txt")); len(data) > 0 && data[len(data)-1] == '\n' {
852 if t, err := strconv.ParseInt(string(data[:len(data)-1]), 10, 64); err == nil {
853 testCacheExpire = time.Unix(0, t)
854 }
855 }
856 }
857
858 b := work.NewBuilder("", moduleLoaderState.VendorDirOrEmpty)
859 defer func() {
860 if err := b.Close(); err != nil {
861 base.Fatal(err)
862 }
863 }()
864
865 var builds, runs, prints []*work.Action
866 var writeCoverMetaAct *work.Action
867
868 if cfg.BuildCoverPkg != nil {
869 match := make([]func(*modload.State, *load.Package) bool, len(cfg.BuildCoverPkg))
870 for i := range cfg.BuildCoverPkg {
871 match[i] = load.MatchPackage(cfg.BuildCoverPkg[i], base.Cwd())
872 }
873
874
875
876 plist := load.TestPackageList(moduleLoaderState, ctx, pkgOpts, pkgs)
877 testCoverPkgs = load.SelectCoverPackages(moduleLoaderState, plist, match, "test")
878 if len(testCoverPkgs) > 0 {
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924 writeCoverMetaAct = &work.Action{
925 Mode: "write coverage meta-data file",
926 Actor: work.ActorFunc(work.WriteCoverMetaFilesFile),
927 Objdir: b.NewObjdir(),
928 }
929 for _, p := range testCoverPkgs {
930 p.Internal.Cover.GenMeta = true
931 }
932 }
933 }
934
935
936
937 if testFuzz != "" {
938
939
940
941 var skipInstrumentation = map[string]bool{
942 "context": true,
943 "internal/fuzz": true,
944 "internal/godebug": true,
945 "internal/runtime/maps": true,
946 "internal/sync": true,
947 "reflect": true,
948 "runtime": true,
949 "sync": true,
950 "sync/atomic": true,
951 "syscall": true,
952 "testing": true,
953 "time": true,
954 }
955 for _, p := range load.TestPackageList(moduleLoaderState, ctx, pkgOpts, pkgs) {
956 if !skipInstrumentation[p.ImportPath] {
957 p.Internal.FuzzInstrument = true
958 }
959 }
960 }
961
962
963 allImports := make(map[*load.Package]bool)
964 for _, p := range pkgs {
965 if p.Error != nil && p.Error.IsImportCycle {
966 continue
967 }
968 for _, p1 := range p.Internal.Imports {
969 allImports[p1] = true
970 }
971 }
972
973 if cfg.BuildCover {
974 for _, p := range pkgs {
975
976
977
978
979
980
981
982
983
984 if cfg.BuildCoverMode == "atomic" && p.ImportPath != "sync/atomic" {
985 load.EnsureImport(moduleLoaderState, p, "sync/atomic")
986 }
987
988
989
990
991
992
993
994
995 if len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 && cfg.BuildCoverPkg == nil {
996 p.Internal.Cover.GenMeta = true
997 }
998
999
1000
1001
1002 if cfg.BuildCover {
1003 if p.Internal.Cover.GenMeta {
1004 p.Internal.Cover.Mode = cfg.BuildCoverMode
1005 }
1006 }
1007 }
1008 }
1009
1010
1011 for _, p := range pkgs {
1012 reportErr := func(perr *load.Package, err error) {
1013 str := err.Error()
1014 if p.ImportPath != "" {
1015 load.DefaultPrinter().Errorf(perr, "# %s\n%s", p.ImportPath, str)
1016 } else {
1017 load.DefaultPrinter().Errorf(perr, "%s", str)
1018 }
1019 }
1020 reportSetupFailed := func(perr *load.Package, err error) {
1021 var stdout io.Writer = os.Stdout
1022 if testJSON {
1023 json := test2json.NewConverter(stdout, p.ImportPath, test2json.Timestamp)
1024 defer func() {
1025 json.Exited(err)
1026 json.Close()
1027 }()
1028 if gotestjsonbuildtext.Value() == "1" {
1029
1030
1031 gotestjsonbuildtext.IncNonDefault()
1032 } else {
1033 json.SetFailedBuild(perr.Desc())
1034 }
1035 stdout = json
1036 }
1037 fmt.Fprintf(stdout, "FAIL\t%s [setup failed]\n", p.ImportPath)
1038 base.SetExitStatus(1)
1039 }
1040
1041 var firstErrPkg *load.Package
1042 load.PackageErrors([]*load.Package{p}, func(p *load.Package) {
1043 reportErr(p, p.Error)
1044 if firstErrPkg == nil {
1045 firstErrPkg = p
1046 }
1047 })
1048 if firstErrPkg != nil {
1049 reportSetupFailed(firstErrPkg, firstErrPkg.Error)
1050 continue
1051 }
1052 buildTest, runTest, printTest, perr, err := builderTest(moduleLoaderState, b, ctx, pkgOpts, p, allImports[p], writeCoverMetaAct)
1053 if err != nil {
1054 reportErr(perr, err)
1055 reportSetupFailed(perr, err)
1056 continue
1057 }
1058 builds = append(builds, buildTest)
1059 runs = append(runs, runTest)
1060 prints = append(prints, printTest)
1061 }
1062
1063
1064
1065
1066
1067
1068 var prevBarrier *work.Action
1069 ch := make(chan struct{})
1070 close(ch)
1071 for _, a := range runs {
1072 if r, ok := a.Actor.(*runTestActor); ok {
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083 barrier := &work.Action{
1084 Mode: "test barrier",
1085 Deps: slices.Clip(a.Deps),
1086 }
1087 if prevBarrier != nil {
1088 barrier.Deps = append(barrier.Deps, prevBarrier)
1089 }
1090 a.Deps = []*work.Action{barrier}
1091 prevBarrier = barrier
1092
1093 r.prev = ch
1094 ch = make(chan struct{})
1095 r.next = ch
1096 }
1097 }
1098
1099
1100 root := &work.Action{Mode: "go test", Actor: work.ActorFunc(printExitStatus), Deps: prints}
1101
1102
1103
1104 for i, a := range prints {
1105 if i > 0 {
1106 a.Deps = append(a.Deps, prints[i-1])
1107 }
1108 }
1109
1110
1111 if !testC && (testBench != "") {
1112
1113
1114 for i, run := range runs {
1115 if i == 0 {
1116 run.Deps = append(run.Deps, builds...)
1117 } else {
1118 run.Deps = append(run.Deps, prints[i-1])
1119 }
1120 }
1121 }
1122
1123 b.Do(ctx, root)
1124 }
1125
1126 var windowsBadWords = []string{
1127 "install",
1128 "patch",
1129 "setup",
1130 "update",
1131 }
1132
1133 func builderTest(loaderstate *modload.State, b *work.Builder, ctx context.Context, pkgOpts load.PackageOpts, p *load.Package, imported bool, writeCoverMetaAct *work.Action) (buildAction, runAction, printAction *work.Action, perr *load.Package, err error) {
1134 if len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 {
1135 build := b.CompileAction(work.ModeBuild, work.ModeBuild, p)
1136 run := &work.Action{
1137 Mode: "test run",
1138 Actor: new(runTestActor),
1139 Deps: []*work.Action{build},
1140 Objdir: b.NewObjdir(),
1141 Package: p,
1142 IgnoreFail: true,
1143 }
1144 if writeCoverMetaAct != nil && build.Actor != nil {
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158 run.Deps = append(run.Deps, writeCoverMetaAct)
1159 writeCoverMetaAct.Deps = append(writeCoverMetaAct.Deps, build)
1160 }
1161 addTestVet(loaderstate, b, p, run, nil)
1162 print := &work.Action{
1163 Mode: "test print",
1164 Actor: work.ActorFunc(builderPrintTest),
1165 Deps: []*work.Action{run},
1166 Package: p,
1167 IgnoreFail: true,
1168 }
1169 return build, run, print, nil, nil
1170 }
1171
1172
1173
1174
1175
1176 var cover *load.TestCover
1177 if cfg.BuildCover {
1178 cover = &load.TestCover{
1179 Mode: cfg.BuildCoverMode,
1180 Local: cfg.BuildCoverPkg == nil,
1181 Pkgs: testCoverPkgs,
1182 Paths: cfg.BuildCoverPkg,
1183 }
1184 }
1185 pmain, ptest, pxtest, perr := load.TestPackagesFor(loaderstate, ctx, pkgOpts, p, cover)
1186 if perr != nil {
1187 return nil, nil, nil, perr, perr.Error
1188 }
1189
1190
1191
1192
1193
1194 if imported && ptest != p {
1195 buildTest := b.CompileAction(work.ModeBuild, work.ModeBuild, ptest)
1196 buildP := b.CompileAction(work.ModeBuild, work.ModeBuild, p)
1197 buildTest.Deps = append(buildTest.Deps, buildP)
1198 }
1199
1200 testBinary := testBinaryName(p)
1201
1202
1203
1204 testDir := b.CompileAction(work.ModeBuild, work.ModeBuild, pmain).Objdir
1205 if err := b.BackgroundShell().Mkdir(testDir); err != nil {
1206 return nil, nil, nil, nil, err
1207 }
1208
1209 pmain.Dir = testDir
1210 pmain.Internal.OmitDebug = !testC && !testNeedBinary()
1211 if pmain.ImportPath == "runtime.test" {
1212
1213
1214 pmain.Internal.OmitDebug = false
1215 }
1216
1217 if !cfg.BuildN {
1218
1219
1220 if err := os.WriteFile(testDir+"_testmain.go", *pmain.Internal.TestmainGo, 0666); err != nil {
1221 return nil, nil, nil, nil, err
1222 }
1223 }
1224
1225 a := b.LinkAction(loaderstate, work.ModeBuild, work.ModeBuild, pmain)
1226 a.Target = testDir + testBinary + cfg.ExeSuffix
1227 if cfg.Goos == "windows" {
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250 for _, bad := range windowsBadWords {
1251 if strings.Contains(testBinary, bad) {
1252 a.Target = testDir + "test.test" + cfg.ExeSuffix
1253 break
1254 }
1255 }
1256 }
1257 buildAction = a
1258 var installAction, cleanAction *work.Action
1259 if testC || testNeedBinary() {
1260
1261 target := filepath.Join(base.Cwd(), testBinary+cfg.ExeSuffix)
1262 isNull := false
1263
1264 if testO != "" {
1265 target = testO
1266
1267 if testODir {
1268 if filepath.IsAbs(target) {
1269 target = filepath.Join(target, testBinary+cfg.ExeSuffix)
1270 } else {
1271 target = filepath.Join(base.Cwd(), target, testBinary+cfg.ExeSuffix)
1272 }
1273 } else {
1274 if base.IsNull(target) {
1275 isNull = true
1276 } else if !filepath.IsAbs(target) {
1277 target = filepath.Join(base.Cwd(), target)
1278 }
1279 }
1280 }
1281
1282 if isNull {
1283 runAction = buildAction
1284 } else {
1285 pmain.Target = target
1286 installAction = &work.Action{
1287 Mode: "test build",
1288 Actor: work.ActorFunc(work.BuildInstallFunc),
1289 Deps: []*work.Action{buildAction},
1290 Package: pmain,
1291 Target: target,
1292 }
1293 runAction = installAction
1294 }
1295 }
1296
1297 var vetRunAction *work.Action
1298 if testC {
1299 printAction = &work.Action{Mode: "test print (nop)", Package: p, Deps: []*work.Action{runAction}}
1300 vetRunAction = printAction
1301 } else {
1302
1303 rta := &runTestActor{
1304 writeCoverMetaAct: writeCoverMetaAct,
1305 }
1306 runAction = &work.Action{
1307 Mode: "test run",
1308 Actor: rta,
1309 Deps: []*work.Action{buildAction},
1310 Package: p,
1311 IgnoreFail: true,
1312 TryCache: rta.c.tryCache,
1313 }
1314 if writeCoverMetaAct != nil {
1315
1316
1317
1318
1319
1320 runAction.Deps = append(runAction.Deps, writeCoverMetaAct)
1321 if !p.IsTestOnly() {
1322
1323
1324
1325
1326
1327 compileAction := b.CompileAction(work.ModeBuild, work.ModeBuild, p)
1328 writeCoverMetaAct.Deps = append(writeCoverMetaAct.Deps, compileAction)
1329 }
1330 }
1331 runAction.Objdir = testDir
1332 vetRunAction = runAction
1333 cleanAction = &work.Action{
1334 Mode: "test clean",
1335 Actor: work.ActorFunc(builderCleanTest),
1336 Deps: []*work.Action{runAction},
1337 Package: p,
1338 IgnoreFail: true,
1339 Objdir: testDir,
1340 }
1341 printAction = &work.Action{
1342 Mode: "test print",
1343 Actor: work.ActorFunc(builderPrintTest),
1344 Deps: []*work.Action{cleanAction},
1345 Package: p,
1346 IgnoreFail: true,
1347 }
1348 }
1349
1350 if len(ptest.GoFiles)+len(ptest.CgoFiles) > 0 {
1351 addTestVet(loaderstate, b, ptest, vetRunAction, installAction)
1352 }
1353 if pxtest != nil {
1354 addTestVet(loaderstate, b, pxtest, vetRunAction, installAction)
1355 }
1356
1357 if installAction != nil {
1358 if runAction != installAction {
1359 installAction.Deps = append(installAction.Deps, runAction)
1360 }
1361 if cleanAction != nil {
1362 cleanAction.Deps = append(cleanAction.Deps, installAction)
1363 }
1364 }
1365
1366 return buildAction, runAction, printAction, nil, nil
1367 }
1368
1369 func addTestVet(loaderstate *modload.State, b *work.Builder, p *load.Package, runAction, installAction *work.Action) {
1370 if testVet.off {
1371 return
1372 }
1373
1374 vet := b.VetAction(loaderstate, work.ModeBuild, work.ModeBuild, false, p)
1375 runAction.Deps = append(runAction.Deps, vet)
1376
1377
1378
1379
1380 if installAction != nil {
1381 installAction.Deps = append(installAction.Deps, vet)
1382 }
1383 }
1384
1385 var noTestsToRun = []byte("\ntesting: warning: no tests to run\n")
1386 var noFuzzTestsToFuzz = []byte("\ntesting: warning: no fuzz tests to fuzz\n")
1387 var tooManyFuzzTestsToFuzz = []byte("\ntesting: warning: -fuzz matches more than one fuzz test, won't fuzz\n")
1388
1389
1390 type runTestActor struct {
1391 c runCache
1392
1393
1394
1395
1396
1397 writeCoverMetaAct *work.Action
1398
1399
1400 prev <-chan struct{}
1401 next chan<- struct{}
1402 }
1403
1404
1405 type runCache struct {
1406 disableCache bool
1407
1408 buf *bytes.Buffer
1409 id1 cache.ActionID
1410 id2 cache.ActionID
1411 }
1412
1413 func coverProfTempFile(a *work.Action) string {
1414 if a.Objdir == "" {
1415 panic("internal error: objdir not set in coverProfTempFile")
1416 }
1417 return a.Objdir + "_cover_.out"
1418 }
1419
1420
1421
1422
1423
1424
1425 var stdoutMu sync.Mutex
1426
1427 type lockedStdout struct{}
1428
1429 func (lockedStdout) Write(b []byte) (int, error) {
1430 stdoutMu.Lock()
1431 defer stdoutMu.Unlock()
1432 return os.Stdout.Write(b)
1433 }
1434
1435 func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action) error {
1436 sh := b.Shell(a)
1437 barrierAction := a.Deps[0]
1438 buildAction := barrierAction.Deps[0]
1439
1440
1441 select {
1442 case <-r.prev:
1443
1444 if testShouldFailFast.Load() {
1445 close(r.next)
1446 return nil
1447 }
1448 case <-base.Interrupted:
1449
1450
1451
1452 base.SetExitStatus(1)
1453 return nil
1454 }
1455
1456
1457
1458
1459 streamOutput := len(pkgArgs) == 0 || testBench != "" || testFuzz != ""
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475 streamAndCacheOutput := testShowPass() && (len(pkgs) == 1 || cfg.BuildP == 1) || testJSON
1476
1477 var stdout io.Writer = os.Stdout
1478 var err error
1479 var json *test2json.Converter
1480 if testJSON {
1481 json = test2json.NewConverter(lockedStdout{}, a.Package.ImportPath, test2json.Timestamp)
1482 defer func() {
1483 json.Exited(err)
1484 json.Close()
1485 }()
1486 stdout = json
1487 }
1488
1489 var buf bytes.Buffer
1490 if streamOutput {
1491
1492 } else if streamAndCacheOutput {
1493
1494
1495 stdout = io.MultiWriter(stdout, &buf)
1496 } else {
1497 stdout = &buf
1498 }
1499
1500
1501 close(r.next)
1502
1503 if a.Failed != nil {
1504
1505 if json != nil && a.Failed.Package != nil {
1506 if gotestjsonbuildtext.Value() == "1" {
1507 gotestjsonbuildtext.IncNonDefault()
1508 } else {
1509 json.SetFailedBuild(a.Failed.Package.Desc())
1510 }
1511 }
1512 a.Failed = nil
1513 fmt.Fprintf(stdout, "FAIL\t%s [build failed]\n", a.Package.ImportPath)
1514
1515 err = errors.New("build failed")
1516 base.SetExitStatus(1)
1517 if stdout == &buf {
1518 a.TestOutput = &buf
1519 }
1520 return nil
1521 }
1522
1523 if p := a.Package; len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 {
1524 reportNoTestFiles := true
1525 if cfg.BuildCover && p.Internal.Cover.GenMeta {
1526 if err := sh.Mkdir(a.Objdir); err != nil {
1527 return err
1528 }
1529 mf, err := work.BuildActionCoverMetaFile(a)
1530 if err != nil {
1531 return err
1532 } else if mf != "" {
1533 reportNoTestFiles = false
1534
1535 if err := work.WriteCoveragePercent(b, a, mf, stdout); err != nil {
1536 return err
1537 }
1538
1539
1540
1541 if coverMerge.f != nil {
1542 cp := coverProfTempFile(a)
1543 if err := work.WriteCoverageProfile(b, a, mf, cp, stdout); err != nil {
1544 return err
1545 }
1546 mergeCoverProfile(cp)
1547 }
1548 }
1549 }
1550 if reportNoTestFiles {
1551 fmt.Fprintf(stdout, "? \t%s\t[no test files]\n", p.ImportPath)
1552 }
1553 if stdout == &buf {
1554 a.TestOutput = &buf
1555 }
1556 return nil
1557 }
1558
1559 if r.c.buf == nil {
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569 r.c.tryCacheWithID(b, a, buildAction.BuildContentID())
1570 }
1571 if r.c.buf != nil {
1572 if stdout != &buf {
1573 stdout.Write(r.c.buf.Bytes())
1574 r.c.buf.Reset()
1575 }
1576 a.TestOutput = r.c.buf
1577 return nil
1578 }
1579
1580 execCmd := work.FindExecCmd()
1581 testlogArg := []string{}
1582 if !r.c.disableCache && len(execCmd) == 0 {
1583 testlogArg = []string{"-test.testlogfile=" + a.Objdir + "testlog.txt"}
1584 }
1585 panicArg := "-test.paniconexit0"
1586 fuzzArg := []string{}
1587 if testFuzz != "" {
1588 fuzzCacheDir := filepath.Join(cache.Default().FuzzDir(), a.Package.ImportPath)
1589 fuzzArg = []string{"-test.fuzzcachedir=" + fuzzCacheDir}
1590 }
1591 coverdirArg := []string{}
1592 addToEnv := ""
1593 if cfg.BuildCover {
1594 gcd := filepath.Join(a.Objdir, "gocoverdir")
1595 if err := sh.Mkdir(gcd); err != nil {
1596
1597
1598
1599
1600
1601 base.Fatalf("failed to create temporary dir: %v", err)
1602 }
1603 coverdirArg = append(coverdirArg, "-test.gocoverdir="+gcd)
1604 if r.writeCoverMetaAct != nil {
1605
1606
1607
1608 src := r.writeCoverMetaAct.Objdir + coverage.MetaFilesFileName
1609 dst := filepath.Join(gcd, coverage.MetaFilesFileName)
1610 if err := sh.CopyFile(dst, src, 0666, false); err != nil {
1611 return err
1612 }
1613 }
1614
1615
1616
1617
1618 addToEnv = "GOCOVERDIR=" + gcd
1619 }
1620 args := str.StringList(execCmd, buildAction.BuiltTarget(), testlogArg, panicArg, fuzzArg, coverdirArg, testArgs)
1621
1622 if testCoverProfile != "" {
1623
1624 for i, arg := range args {
1625 if strings.HasPrefix(arg, "-test.coverprofile=") {
1626 args[i] = "-test.coverprofile=" + coverProfTempFile(a)
1627 }
1628 }
1629 }
1630
1631 if cfg.BuildN || cfg.BuildX {
1632 sh.ShowCmd("", "%s", strings.Join(args, " "))
1633 if cfg.BuildN {
1634 return nil
1635 }
1636 }
1637
1638
1639
1640 ctx, cancel := context.WithTimeout(ctx, testKillTimeout)
1641 defer cancel()
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654 var (
1655 cmd *exec.Cmd
1656 t0 time.Time
1657 cancelKilled = false
1658 cancelSignaled = false
1659 )
1660 for {
1661 cmd = exec.CommandContext(ctx, args[0], args[1:]...)
1662 cmd.Dir = a.Package.Dir
1663
1664 env := slices.Clip(cfg.OrigEnv)
1665 env = base.AppendPATH(env)
1666 env = base.AppendPWD(env, cmd.Dir)
1667 cmd.Env = env
1668 if addToEnv != "" {
1669 cmd.Env = append(cmd.Env, addToEnv)
1670 }
1671
1672 cmd.Stdout = stdout
1673 cmd.Stderr = stdout
1674
1675 cmd.Cancel = func() error {
1676 if base.SignalTrace == nil {
1677 err := cmd.Process.Kill()
1678 if err == nil {
1679 cancelKilled = true
1680 }
1681 return err
1682 }
1683
1684
1685
1686 err := cmd.Process.Signal(base.SignalTrace)
1687 if err == nil {
1688 cancelSignaled = true
1689 }
1690 return err
1691 }
1692 cmd.WaitDelay = testWaitDelay
1693
1694 base.StartSigHandlers()
1695 t0 = time.Now()
1696 err = cmd.Run()
1697
1698 if !base.IsETXTBSY(err) {
1699
1700
1701 break
1702 }
1703 }
1704
1705 out := buf.Bytes()
1706 a.TestOutput = &buf
1707 t := fmt.Sprintf("%.3fs", time.Since(t0).Seconds())
1708
1709 mergeCoverProfile(coverProfTempFile(a))
1710
1711 if err == nil {
1712 norun := ""
1713 if !testShowPass() && !testJSON {
1714 buf.Reset()
1715 }
1716 if bytes.HasPrefix(out, noTestsToRun[1:]) || bytes.Contains(out, noTestsToRun) {
1717 norun = " [no tests to run]"
1718 }
1719 if bytes.HasPrefix(out, noFuzzTestsToFuzz[1:]) || bytes.Contains(out, noFuzzTestsToFuzz) {
1720 norun = " [no fuzz tests to fuzz]"
1721 }
1722 if bytes.HasPrefix(out, tooManyFuzzTestsToFuzz[1:]) || bytes.Contains(out, tooManyFuzzTestsToFuzz) {
1723 norun = "[-fuzz matches more than one fuzz test, won't fuzz]"
1724 }
1725 if len(out) > 0 && !bytes.HasSuffix(out, []byte("\n")) {
1726
1727
1728 cmd.Stdout.Write([]byte("\n"))
1729 }
1730 fmt.Fprintf(cmd.Stdout, "ok \t%s\t%s%s%s\n", a.Package.ImportPath, t, coveragePercentage(out), norun)
1731 r.c.saveOutput(a)
1732 } else {
1733 if testFailFast {
1734 testShouldFailFast.Store(true)
1735 }
1736
1737 base.SetExitStatus(1)
1738 if cancelSignaled {
1739 fmt.Fprintf(cmd.Stdout, "*** Test killed with %v: ran too long (%v).\n", base.SignalTrace, testKillTimeout)
1740 } else if cancelKilled {
1741 fmt.Fprintf(cmd.Stdout, "*** Test killed: ran too long (%v).\n", testKillTimeout)
1742 } else if errors.Is(err, exec.ErrWaitDelay) {
1743 fmt.Fprintf(cmd.Stdout, "*** Test I/O incomplete %v after exiting.\n", cmd.WaitDelay)
1744 }
1745 if ee, ok := errors.AsType[*exec.ExitError](err); !ok || !ee.Exited() || len(out) == 0 {
1746
1747
1748 fmt.Fprintf(cmd.Stdout, "%s\n", err)
1749 } else if !bytes.HasSuffix(out, []byte("\n")) {
1750
1751
1752 cmd.Stdout.Write([]byte("\n"))
1753 }
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763 prefix := ""
1764 if testJSON || testV.json {
1765 prefix = "\x16"
1766 }
1767 fmt.Fprintf(cmd.Stdout, "%sFAIL\t%s\t%s\n", prefix, a.Package.ImportPath, t)
1768 }
1769
1770 if cmd.Stdout != &buf {
1771 buf.Reset()
1772 }
1773 return nil
1774 }
1775
1776
1777
1778
1779 func (c *runCache) tryCache(b *work.Builder, a *work.Action, linkAction *work.Action) bool {
1780 return c.tryCacheWithID(b, a, linkAction.BuildActionID())
1781 }
1782
1783 func (c *runCache) tryCacheWithID(b *work.Builder, a *work.Action, id string) bool {
1784 if len(pkgArgs) == 0 {
1785
1786
1787 if cache.DebugTest {
1788 fmt.Fprintf(os.Stderr, "testcache: caching disabled in local directory mode\n")
1789 }
1790 c.disableCache = true
1791 return false
1792 }
1793
1794 if a.Package.Root == "" {
1795
1796 if cache.DebugTest {
1797 fmt.Fprintf(os.Stderr, "testcache: caching disabled for package outside of module root, GOPATH, or GOROOT: %s\n", a.Package.ImportPath)
1798 }
1799 c.disableCache = true
1800 return false
1801 }
1802
1803 var cacheArgs []string
1804 for _, arg := range testArgs {
1805 i := strings.Index(arg, "=")
1806 if i < 0 || !strings.HasPrefix(arg, "-test.") {
1807 if cache.DebugTest {
1808 fmt.Fprintf(os.Stderr, "testcache: caching disabled for test argument: %s\n", arg)
1809 }
1810 c.disableCache = true
1811 return false
1812 }
1813 switch arg[:i] {
1814 case "-test.benchtime",
1815 "-test.cpu",
1816 "-test.list",
1817 "-test.parallel",
1818 "-test.run",
1819 "-test.short",
1820 "-test.skip",
1821 "-test.timeout",
1822 "-test.failfast",
1823 "-test.v",
1824 "-test.fullpath":
1825
1826
1827
1828 cacheArgs = append(cacheArgs, arg)
1829 case "-test.coverprofile",
1830 "-test.outputdir":
1831
1832
1833
1834 default:
1835
1836 if cache.DebugTest {
1837 fmt.Fprintf(os.Stderr, "testcache: caching disabled for test argument: %s\n", arg)
1838 }
1839 c.disableCache = true
1840 return false
1841 }
1842 }
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869 h := cache.NewHash("testResult")
1870 fmt.Fprintf(h, "test binary %s args %q execcmd %q", id, cacheArgs, work.ExecCmd)
1871 testID := h.Sum()
1872 if c.id1 == (cache.ActionID{}) {
1873 c.id1 = testID
1874 } else {
1875 c.id2 = testID
1876 }
1877 if cache.DebugTest {
1878 fmt.Fprintf(os.Stderr, "testcache: %s: test ID %x => %x\n", a.Package.ImportPath, id, testID)
1879 }
1880
1881
1882
1883 data, entry, err := cache.GetBytes(cache.Default(), testID)
1884 if !bytes.HasPrefix(data, testlogMagic) || data[len(data)-1] != '\n' {
1885 if cache.DebugTest {
1886 if err != nil {
1887 fmt.Fprintf(os.Stderr, "testcache: %s: input list not found: %v\n", a.Package.ImportPath, err)
1888 } else {
1889 fmt.Fprintf(os.Stderr, "testcache: %s: input list malformed\n", a.Package.ImportPath)
1890 }
1891 }
1892 return false
1893 }
1894 testInputsID, err := computeTestInputsID(a, data)
1895 if err != nil {
1896 return false
1897 }
1898 if cache.DebugTest {
1899 fmt.Fprintf(os.Stderr, "testcache: %s: test ID %x => input ID %x => %x\n", a.Package.ImportPath, testID, testInputsID, testAndInputKey(testID, testInputsID))
1900 }
1901
1902
1903
1904 data, entry, err = cache.GetBytes(cache.Default(), testAndInputKey(testID, testInputsID))
1905
1906
1907 if testCoverProfile != "" {
1908
1909 cpData, _, err := cache.GetFile(cache.Default(), coverProfileAndInputKey(testID, testInputsID))
1910 if err != nil {
1911 if cache.DebugTest {
1912 fmt.Fprintf(os.Stderr, "testcache: %s: cached cover profile missing: %v\n", a.Package.ImportPath, err)
1913 }
1914 return false
1915 }
1916 mergeCoverProfile(cpData)
1917 }
1918
1919 if len(data) == 0 || data[len(data)-1] != '\n' {
1920 if cache.DebugTest {
1921 if err != nil {
1922 fmt.Fprintf(os.Stderr, "testcache: %s: test output not found: %v\n", a.Package.ImportPath, err)
1923 } else {
1924 fmt.Fprintf(os.Stderr, "testcache: %s: test output malformed\n", a.Package.ImportPath)
1925 }
1926 }
1927 return false
1928 }
1929 if entry.Time.Before(testCacheExpire) {
1930 if cache.DebugTest {
1931 fmt.Fprintf(os.Stderr, "testcache: %s: test output expired due to go clean -testcache\n", a.Package.ImportPath)
1932 }
1933 return false
1934 }
1935 i := bytes.LastIndexByte(data[:len(data)-1], '\n') + 1
1936 if !bytes.HasPrefix(data[i:], []byte("ok \t")) {
1937 if cache.DebugTest {
1938 fmt.Fprintf(os.Stderr, "testcache: %s: test output malformed\n", a.Package.ImportPath)
1939 }
1940 return false
1941 }
1942 j := bytes.IndexByte(data[i+len("ok \t"):], '\t')
1943 if j < 0 {
1944 if cache.DebugTest {
1945 fmt.Fprintf(os.Stderr, "testcache: %s: test output malformed\n", a.Package.ImportPath)
1946 }
1947 return false
1948 }
1949 j += i + len("ok \t") + 1
1950
1951
1952 c.buf = new(bytes.Buffer)
1953 c.buf.Write(data[:j])
1954 c.buf.WriteString("(cached)")
1955 for j < len(data) && ('0' <= data[j] && data[j] <= '9' || data[j] == '.' || data[j] == 's') {
1956 j++
1957 }
1958 c.buf.Write(data[j:])
1959 return true
1960 }
1961
1962 var errBadTestInputs = errors.New("error parsing test inputs")
1963 var testlogMagic = []byte("# test log\n")
1964
1965
1966
1967
1968 func computeTestInputsID(a *work.Action, testlog []byte) (cache.ActionID, error) {
1969 testlog = bytes.TrimPrefix(testlog, testlogMagic)
1970 h := cache.NewHash("testInputs")
1971
1972 fmt.Fprintf(h, "env GODEBUG %x\n", hashGetenv("GODEBUG"))
1973 pwd := a.Package.Dir
1974 for _, line := range bytes.Split(testlog, []byte("\n")) {
1975 if len(line) == 0 {
1976 continue
1977 }
1978 s := string(line)
1979 op, name, found := strings.Cut(s, " ")
1980 if !found {
1981 if cache.DebugTest {
1982 fmt.Fprintf(os.Stderr, "testcache: %s: input list malformed (%q)\n", a.Package.ImportPath, line)
1983 }
1984 return cache.ActionID{}, errBadTestInputs
1985 }
1986 switch op {
1987 default:
1988 if cache.DebugTest {
1989 fmt.Fprintf(os.Stderr, "testcache: %s: input list malformed (%q)\n", a.Package.ImportPath, line)
1990 }
1991 return cache.ActionID{}, errBadTestInputs
1992 case "getenv":
1993 fmt.Fprintf(h, "env %s %x\n", name, hashGetenv(name))
1994 case "chdir":
1995 pwd = name
1996 fmt.Fprintf(h, "chdir %s %x\n", name, hashStat(name))
1997 case "stat":
1998 if !filepath.IsAbs(name) {
1999 name = filepath.Join(pwd, name)
2000 }
2001 if a.Package.Root == "" || search.InDir(name, a.Package.Root) == "" {
2002
2003 break
2004 }
2005 fmt.Fprintf(h, "stat %s %x\n", name, hashStat(name))
2006 case "open":
2007 if !filepath.IsAbs(name) {
2008 name = filepath.Join(pwd, name)
2009 }
2010 if a.Package.Root == "" || search.InDir(name, a.Package.Root) == "" {
2011
2012 break
2013 }
2014 fh, err := hashOpen(name)
2015 if err != nil {
2016 if cache.DebugTest {
2017 fmt.Fprintf(os.Stderr, "testcache: %s: input file %s: %s\n", a.Package.ImportPath, name, err)
2018 }
2019 return cache.ActionID{}, err
2020 }
2021 fmt.Fprintf(h, "open %s %x\n", name, fh)
2022 }
2023 }
2024 sum := h.Sum()
2025 return sum, nil
2026 }
2027
2028 func hashGetenv(name string) cache.ActionID {
2029 h := cache.NewHash("getenv")
2030 v, ok := os.LookupEnv(name)
2031 if !ok {
2032 h.Write([]byte{0})
2033 } else {
2034 h.Write([]byte{1})
2035 h.Write([]byte(v))
2036 }
2037 return h.Sum()
2038 }
2039
2040 const modTimeCutoff = 2 * time.Second
2041
2042 var errFileTooNew = errors.New("file used as input is too new")
2043
2044 func hashOpen(name string) (cache.ActionID, error) {
2045 h := cache.NewHash("open")
2046 info, err := os.Stat(name)
2047 if err != nil {
2048 fmt.Fprintf(h, "err %v\n", err)
2049 return h.Sum(), nil
2050 }
2051 hashWriteStat(h, info)
2052 if info.IsDir() {
2053 files, err := os.ReadDir(name)
2054 if err != nil {
2055 fmt.Fprintf(h, "err %v\n", err)
2056 }
2057 for _, f := range files {
2058 fmt.Fprintf(h, "file %s ", f.Name())
2059 finfo, err := f.Info()
2060 if err != nil {
2061 fmt.Fprintf(h, "err %v\n", err)
2062 } else {
2063 hashWriteStat(h, finfo)
2064 }
2065 }
2066 } else if info.Mode().IsRegular() {
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076 if time.Since(info.ModTime()) < modTimeCutoff {
2077 return cache.ActionID{}, errFileTooNew
2078 }
2079 }
2080 return h.Sum(), nil
2081 }
2082
2083 func hashStat(name string) cache.ActionID {
2084 h := cache.NewHash("stat")
2085 if info, err := os.Stat(name); err != nil {
2086 fmt.Fprintf(h, "err %v\n", err)
2087 } else {
2088 hashWriteStat(h, info)
2089 }
2090 if info, err := os.Lstat(name); err != nil {
2091 fmt.Fprintf(h, "err %v\n", err)
2092 } else {
2093 hashWriteStat(h, info)
2094 }
2095 return h.Sum()
2096 }
2097
2098 func hashWriteStat(h io.Writer, info fs.FileInfo) {
2099 fmt.Fprintf(h, "stat %d %x %v %v\n", info.Size(), uint64(info.Mode()), info.ModTime(), info.IsDir())
2100 }
2101
2102
2103 func testAndInputKey(testID, testInputsID cache.ActionID) cache.ActionID {
2104 return cache.Subkey(testID, fmt.Sprintf("inputs:%x", testInputsID))
2105 }
2106
2107
2108 func coverProfileAndInputKey(testID, testInputsID cache.ActionID) cache.ActionID {
2109 return cache.Subkey(testAndInputKey(testID, testInputsID), "coverprofile")
2110 }
2111
2112 func (c *runCache) saveOutput(a *work.Action) {
2113 if c.id1 == (cache.ActionID{}) && c.id2 == (cache.ActionID{}) {
2114 return
2115 }
2116
2117
2118 testlog, err := os.ReadFile(a.Objdir + "testlog.txt")
2119 if err != nil || !bytes.HasPrefix(testlog, testlogMagic) || testlog[len(testlog)-1] != '\n' {
2120 if cache.DebugTest {
2121 if err != nil {
2122 fmt.Fprintf(os.Stderr, "testcache: %s: reading testlog: %v\n", a.Package.ImportPath, err)
2123 } else {
2124 fmt.Fprintf(os.Stderr, "testcache: %s: reading testlog: malformed\n", a.Package.ImportPath)
2125 }
2126 }
2127 return
2128 }
2129 testInputsID, err := computeTestInputsID(a, testlog)
2130 if err != nil {
2131 return
2132 }
2133 var coverProfile []byte
2134 if testCoverProfile != "" {
2135 coverProfile, err = os.ReadFile(coverProfTempFile(a))
2136 if err != nil {
2137 if cache.DebugTest {
2138 fmt.Fprintf(os.Stderr, "testcache: %s: reading cover profile: %v\n", a.Package.ImportPath, err)
2139 }
2140 return
2141 }
2142 }
2143 if c.id1 != (cache.ActionID{}) {
2144 if cache.DebugTest {
2145 fmt.Fprintf(os.Stderr, "testcache: %s: save test ID %x => input ID %x => %x\n", a.Package.ImportPath, c.id1, testInputsID, testAndInputKey(c.id1, testInputsID))
2146 }
2147 cache.PutNoVerify(cache.Default(), c.id1, bytes.NewReader(testlog))
2148 cache.PutNoVerify(cache.Default(), testAndInputKey(c.id1, testInputsID), bytes.NewReader(a.TestOutput.Bytes()))
2149 if coverProfile != nil {
2150 cache.PutNoVerify(cache.Default(), coverProfileAndInputKey(c.id1, testInputsID), bytes.NewReader(coverProfile))
2151 }
2152 }
2153 if c.id2 != (cache.ActionID{}) {
2154 if cache.DebugTest {
2155 fmt.Fprintf(os.Stderr, "testcache: %s: save test ID %x => input ID %x => %x\n", a.Package.ImportPath, c.id2, testInputsID, testAndInputKey(c.id2, testInputsID))
2156 }
2157 cache.PutNoVerify(cache.Default(), c.id2, bytes.NewReader(testlog))
2158 cache.PutNoVerify(cache.Default(), testAndInputKey(c.id2, testInputsID), bytes.NewReader(a.TestOutput.Bytes()))
2159 if coverProfile != nil {
2160 cache.PutNoVerify(cache.Default(), coverProfileAndInputKey(c.id2, testInputsID), bytes.NewReader(coverProfile))
2161 }
2162 }
2163 }
2164
2165
2166
2167 func coveragePercentage(out []byte) string {
2168 if !cfg.BuildCover {
2169 return ""
2170 }
2171
2172
2173
2174 re := regexp.MustCompile(`coverage: (.*)\n`)
2175 matches := re.FindSubmatch(out)
2176 if matches == nil {
2177
2178
2179 return ""
2180 }
2181 return fmt.Sprintf("\tcoverage: %s", matches[1])
2182 }
2183
2184
2185 func builderCleanTest(b *work.Builder, ctx context.Context, a *work.Action) error {
2186 if cfg.BuildWork {
2187 return nil
2188 }
2189 b.Shell(a).RemoveAll(a.Objdir)
2190 return nil
2191 }
2192
2193
2194 func builderPrintTest(b *work.Builder, ctx context.Context, a *work.Action) error {
2195 run := a.Deps[0]
2196 if run.Mode == "test clean" {
2197 run = run.Deps[0]
2198 }
2199 if run.Mode != "test run" {
2200 base.Fatalf("internal error: cannot find test run to print")
2201 }
2202 if run.TestOutput != nil {
2203 os.Stdout.Write(run.TestOutput.Bytes())
2204 run.TestOutput = nil
2205 }
2206 return nil
2207 }
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224 func printExitStatus(b *work.Builder, ctx context.Context, a *work.Action) error {
2225 if !testJSON && testFuzz == "" && len(pkgArgs) != 0 {
2226 if base.GetExitStatus() != 0 {
2227 fmt.Println("FAIL")
2228 return nil
2229 }
2230 }
2231 return nil
2232 }
2233
2234
2235
2236
2237
2238
2239 func testBinaryName(p *load.Package) string {
2240 var elem string
2241 if p.ImportPath == "command-line-arguments" {
2242 elem = p.Name
2243 } else {
2244 elem = p.DefaultExecName()
2245 }
2246
2247 return elem + ".test"
2248 }
2249
View as plain text