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 covMeta cache.ActionID
1412 }
1413
1414 func coverProfTempFile(a *work.Action) string {
1415 if a.Objdir == "" {
1416 panic("internal error: objdir not set in coverProfTempFile")
1417 }
1418 return a.Objdir + "_cover_.out"
1419 }
1420
1421
1422
1423
1424
1425
1426 var stdoutMu sync.Mutex
1427
1428 type lockedStdout struct{}
1429
1430 func (lockedStdout) Write(b []byte) (int, error) {
1431 stdoutMu.Lock()
1432 defer stdoutMu.Unlock()
1433 return os.Stdout.Write(b)
1434 }
1435
1436 func (r *runTestActor) Act(b *work.Builder, ctx context.Context, a *work.Action) error {
1437 sh := b.Shell(a)
1438 barrierAction := a.Deps[0]
1439 buildAction := barrierAction.Deps[0]
1440
1441
1442 select {
1443 case <-r.prev:
1444
1445 if testShouldFailFast.Load() {
1446 close(r.next)
1447 return nil
1448 }
1449 case <-base.Interrupted:
1450
1451
1452
1453 base.SetExitStatus(1)
1454 return nil
1455 }
1456
1457
1458
1459
1460 streamOutput := len(pkgArgs) == 0 || testBench != "" || testFuzz != ""
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476 streamAndCacheOutput := testShowPass() && (len(pkgs) == 1 || cfg.BuildP == 1) || testJSON
1477
1478 var stdout io.Writer = os.Stdout
1479 var err error
1480 var json *test2json.Converter
1481 if testJSON {
1482 json = test2json.NewConverter(lockedStdout{}, a.Package.ImportPath, test2json.Timestamp)
1483 defer func() {
1484 json.Exited(err)
1485 json.Close()
1486 }()
1487 stdout = json
1488 }
1489
1490 var buf bytes.Buffer
1491 if streamOutput {
1492
1493 } else if streamAndCacheOutput {
1494
1495
1496 stdout = io.MultiWriter(stdout, &buf)
1497 } else {
1498 stdout = &buf
1499 }
1500
1501
1502 close(r.next)
1503
1504 if a.Failed != nil {
1505
1506 if json != nil && a.Failed.Package != nil {
1507 if gotestjsonbuildtext.Value() == "1" {
1508 gotestjsonbuildtext.IncNonDefault()
1509 } else {
1510 json.SetFailedBuild(a.Failed.Package.Desc())
1511 }
1512 }
1513 a.Failed = nil
1514 fmt.Fprintf(stdout, "FAIL\t%s [build failed]\n", a.Package.ImportPath)
1515
1516 err = errors.New("build failed")
1517 base.SetExitStatus(1)
1518 if stdout == &buf {
1519 a.TestOutput = &buf
1520 }
1521 return nil
1522 }
1523
1524 if p := a.Package; len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 {
1525 reportNoTestFiles := true
1526 if cfg.BuildCover && p.Internal.Cover.GenMeta {
1527 if err := sh.Mkdir(a.Objdir); err != nil {
1528 return err
1529 }
1530 mf, err := work.BuildActionCoverMetaFile(a)
1531 if err != nil {
1532 return err
1533 } else if mf != "" {
1534 reportNoTestFiles = false
1535
1536 if err := work.WriteCoveragePercent(b, a, mf, stdout); err != nil {
1537 return err
1538 }
1539
1540
1541
1542 if coverMerge.f != nil {
1543 cp := coverProfTempFile(a)
1544 if err := work.WriteCoverageProfile(b, a, mf, cp, stdout); err != nil {
1545 return err
1546 }
1547 mergeCoverProfile(cp)
1548 }
1549 }
1550 }
1551 if reportNoTestFiles {
1552 fmt.Fprintf(stdout, "? \t%s\t[no test files]\n", p.ImportPath)
1553 }
1554 if stdout == &buf {
1555 a.TestOutput = &buf
1556 }
1557 return nil
1558 }
1559
1560 if r.c.buf == nil {
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570 r.c.tryCacheWithID(b, a, buildAction.BuildContentID())
1571 }
1572 if r.c.buf != nil {
1573 if stdout != &buf {
1574 stdout.Write(r.c.buf.Bytes())
1575 r.c.buf.Reset()
1576 }
1577 a.TestOutput = r.c.buf
1578 return nil
1579 }
1580
1581 execCmd := work.FindExecCmd()
1582 testlogArg := []string{}
1583 if !r.c.disableCache && len(execCmd) == 0 {
1584 testlogArg = []string{"-test.testlogfile=" + a.Objdir + "testlog.txt"}
1585 }
1586 panicArg := "-test.paniconexit0"
1587 fuzzArg := []string{}
1588 if testFuzz != "" {
1589 fuzzCacheDir := filepath.Join(cache.Default().FuzzDir(), a.Package.ImportPath)
1590 fuzzArg = []string{"-test.fuzzcachedir=" + fuzzCacheDir}
1591 }
1592 coverdirArg := []string{}
1593 addToEnv := ""
1594 if cfg.BuildCover {
1595 gcd := filepath.Join(a.Objdir, "gocoverdir")
1596 if err := sh.Mkdir(gcd); err != nil {
1597
1598
1599
1600
1601
1602 base.Fatalf("failed to create temporary dir: %v", err)
1603 }
1604 coverdirArg = append(coverdirArg, "-test.gocoverdir="+gcd)
1605 if r.writeCoverMetaAct != nil {
1606
1607
1608
1609 src := r.writeCoverMetaAct.Objdir + coverage.MetaFilesFileName
1610 dst := filepath.Join(gcd, coverage.MetaFilesFileName)
1611 if err := sh.CopyFile(dst, src, 0666, false); err != nil {
1612 return err
1613 }
1614 }
1615
1616
1617
1618
1619 addToEnv = "GOCOVERDIR=" + gcd
1620 }
1621 args := str.StringList(execCmd, buildAction.BuiltTarget(), testlogArg, panicArg, fuzzArg, coverdirArg, testArgs)
1622
1623 if testCoverProfile != "" {
1624
1625 for i, arg := range args {
1626 if strings.HasPrefix(arg, "-test.coverprofile=") {
1627 args[i] = "-test.coverprofile=" + coverProfTempFile(a)
1628 }
1629 }
1630 }
1631
1632 if cfg.BuildN || cfg.BuildX {
1633 sh.ShowCmd("", "%s", strings.Join(args, " "))
1634 if cfg.BuildN {
1635 return nil
1636 }
1637 }
1638
1639
1640
1641 ctx, cancel := context.WithTimeout(ctx, testKillTimeout)
1642 defer cancel()
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655 var (
1656 cmd *exec.Cmd
1657 t0 time.Time
1658 cancelKilled = false
1659 cancelSignaled = false
1660 )
1661 for {
1662 cmd = exec.CommandContext(ctx, args[0], args[1:]...)
1663 cmd.Dir = a.Package.Dir
1664
1665 env := slices.Clip(cfg.OrigEnv)
1666 env = base.AppendPATH(env)
1667 env = base.AppendPWD(env, cmd.Dir)
1668 cmd.Env = env
1669 if addToEnv != "" {
1670 cmd.Env = append(cmd.Env, addToEnv)
1671 }
1672
1673 cmd.Stdout = stdout
1674 cmd.Stderr = stdout
1675
1676 cmd.Cancel = func() error {
1677 if base.SignalTrace == nil {
1678 err := cmd.Process.Kill()
1679 if err == nil {
1680 cancelKilled = true
1681 }
1682 return err
1683 }
1684
1685
1686
1687 err := cmd.Process.Signal(base.SignalTrace)
1688 if err == nil {
1689 cancelSignaled = true
1690 }
1691 return err
1692 }
1693 cmd.WaitDelay = testWaitDelay
1694
1695 base.StartSigHandlers()
1696 t0 = time.Now()
1697 err = cmd.Run()
1698
1699 if !base.IsETXTBSY(err) {
1700
1701
1702 break
1703 }
1704 }
1705
1706 out := buf.Bytes()
1707 a.TestOutput = &buf
1708 t := fmt.Sprintf("%.3fs", time.Since(t0).Seconds())
1709
1710 mergeCoverProfile(coverProfTempFile(a))
1711
1712 if err == nil {
1713 norun := ""
1714 if !testShowPass() && !testJSON {
1715 buf.Reset()
1716 }
1717 if bytes.HasPrefix(out, noTestsToRun[1:]) || bytes.Contains(out, noTestsToRun) {
1718 norun = " [no tests to run]"
1719 }
1720 if bytes.HasPrefix(out, noFuzzTestsToFuzz[1:]) || bytes.Contains(out, noFuzzTestsToFuzz) {
1721 norun = " [no fuzz tests to fuzz]"
1722 }
1723 if bytes.HasPrefix(out, tooManyFuzzTestsToFuzz[1:]) || bytes.Contains(out, tooManyFuzzTestsToFuzz) {
1724 norun = "[-fuzz matches more than one fuzz test, won't fuzz]"
1725 }
1726 if len(out) > 0 && !bytes.HasSuffix(out, []byte("\n")) {
1727
1728
1729 cmd.Stdout.Write([]byte("\n"))
1730 }
1731 fmt.Fprintf(cmd.Stdout, "ok \t%s\t%s%s%s\n", a.Package.ImportPath, t, coveragePercentage(out), norun)
1732 r.c.saveOutput(a)
1733 } else {
1734 if testFailFast {
1735 testShouldFailFast.Store(true)
1736 }
1737
1738 base.SetExitStatus(1)
1739 if cancelSignaled {
1740 fmt.Fprintf(cmd.Stdout, "*** Test killed with %v: ran too long (%v).\n", base.SignalTrace, testKillTimeout)
1741 } else if cancelKilled {
1742 fmt.Fprintf(cmd.Stdout, "*** Test killed: ran too long (%v).\n", testKillTimeout)
1743 } else if errors.Is(err, exec.ErrWaitDelay) {
1744 fmt.Fprintf(cmd.Stdout, "*** Test I/O incomplete %v after exiting.\n", cmd.WaitDelay)
1745 }
1746 if ee, ok := errors.AsType[*exec.ExitError](err); !ok || !ee.Exited() || len(out) == 0 {
1747
1748
1749 fmt.Fprintf(cmd.Stdout, "%s\n", err)
1750 } else if !bytes.HasSuffix(out, []byte("\n")) {
1751
1752
1753 cmd.Stdout.Write([]byte("\n"))
1754 }
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764 prefix := ""
1765 if testJSON || testV.json {
1766 prefix = "\x16"
1767 }
1768 fmt.Fprintf(cmd.Stdout, "%sFAIL\t%s\t%s\n", prefix, a.Package.ImportPath, t)
1769 }
1770
1771 if cmd.Stdout != &buf {
1772 buf.Reset()
1773 }
1774 return nil
1775 }
1776
1777
1778
1779
1780 func (c *runCache) tryCache(b *work.Builder, a *work.Action, linkAction *work.Action) bool {
1781 return c.tryCacheWithID(b, a, linkAction.BuildActionID())
1782 }
1783
1784 func (c *runCache) tryCacheWithID(b *work.Builder, a *work.Action, id string) bool {
1785 if len(pkgArgs) == 0 {
1786
1787
1788 if cache.DebugTest {
1789 fmt.Fprintf(os.Stderr, "testcache: caching disabled in local directory mode\n")
1790 }
1791 c.disableCache = true
1792 return false
1793 }
1794
1795 if a.Package.Root == "" {
1796
1797 if cache.DebugTest {
1798 fmt.Fprintf(os.Stderr, "testcache: caching disabled for package outside of module root, GOPATH, or GOROOT: %s\n", a.Package.ImportPath)
1799 }
1800 c.disableCache = true
1801 return false
1802 }
1803
1804
1805
1806
1807
1808
1809 if len(testCoverPkgs) != 0 {
1810 searchDeps := a.Deps
1811 for _, dep := range a.Deps {
1812 if dep.Mode == "test barrier" {
1813 searchDeps = dep.Deps
1814 break
1815 }
1816 }
1817 for _, dep := range searchDeps {
1818 if dep.Mode == "write coverage meta-data file" {
1819 h := cache.NewHash("covermeta")
1820 for _, metaDep := range dep.Deps {
1821 if aid := metaDep.BuildActionID(); aid != "" {
1822 fmt.Fprintf(h, "dep %s\n", aid)
1823 }
1824 }
1825 c.covMeta = h.Sum()
1826 break
1827 }
1828 }
1829 }
1830
1831 var cacheArgs []string
1832 for _, arg := range testArgs {
1833 i := strings.Index(arg, "=")
1834 if i < 0 || !strings.HasPrefix(arg, "-test.") {
1835 if cache.DebugTest {
1836 fmt.Fprintf(os.Stderr, "testcache: caching disabled for test argument: %s\n", arg)
1837 }
1838 c.disableCache = true
1839 return false
1840 }
1841 switch arg[:i] {
1842 case "-test.benchtime",
1843 "-test.cpu",
1844 "-test.list",
1845 "-test.parallel",
1846 "-test.run",
1847 "-test.short",
1848 "-test.skip",
1849 "-test.timeout",
1850 "-test.failfast",
1851 "-test.v",
1852 "-test.fullpath":
1853
1854
1855
1856 cacheArgs = append(cacheArgs, arg)
1857 case "-test.coverprofile",
1858 "-test.outputdir":
1859
1860
1861
1862 default:
1863
1864 if cache.DebugTest {
1865 fmt.Fprintf(os.Stderr, "testcache: caching disabled for test argument: %s\n", arg)
1866 }
1867 c.disableCache = true
1868 return false
1869 }
1870 }
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897 h := cache.NewHash("testResult")
1898 fmt.Fprintf(h, "test binary %s args %q execcmd %q", id, cacheArgs, work.ExecCmd)
1899 testID := h.Sum()
1900 if c.id1 == (cache.ActionID{}) {
1901 c.id1 = testID
1902 } else {
1903 c.id2 = testID
1904 }
1905 if cache.DebugTest {
1906 fmt.Fprintf(os.Stderr, "testcache: %s: test ID %x => %x\n", a.Package.ImportPath, id, testID)
1907 }
1908
1909
1910
1911 data, entry, err := cache.GetBytes(cache.Default(), testID)
1912 if !bytes.HasPrefix(data, testlogMagic) || data[len(data)-1] != '\n' {
1913 if cache.DebugTest {
1914 if err != nil {
1915 fmt.Fprintf(os.Stderr, "testcache: %s: input list not found: %v\n", a.Package.ImportPath, err)
1916 } else {
1917 fmt.Fprintf(os.Stderr, "testcache: %s: input list malformed\n", a.Package.ImportPath)
1918 }
1919 }
1920 return false
1921 }
1922 testInputsID, err := computeTestInputsID(a, data)
1923 if err != nil {
1924 return false
1925 }
1926 if cache.DebugTest {
1927 fmt.Fprintf(os.Stderr, "testcache: %s: test ID %x => input ID %x => %x\n", a.Package.ImportPath, testID, testInputsID, testAndInputKey(testID, testInputsID))
1928 }
1929
1930
1931
1932 data, entry, err = cache.GetBytes(cache.Default(), testAndInputKey(testID, testInputsID))
1933
1934
1935 if testCoverProfile != "" {
1936
1937 cpData, _, err := cache.GetFile(cache.Default(), coverProfileAndInputKey(testID, testInputsID, c.covMeta))
1938 if err != nil {
1939 if cache.DebugTest {
1940 fmt.Fprintf(os.Stderr, "testcache: %s: cached cover profile missing: %v\n", a.Package.ImportPath, err)
1941 }
1942 return false
1943 }
1944 mergeCoverProfile(cpData)
1945 } else if c.covMeta != (cache.ActionID{}) {
1946
1947
1948
1949
1950 _, _, err := cache.GetFile(cache.Default(), coverProfileAndInputKey(testID, testInputsID, c.covMeta))
1951 if err != nil {
1952 if cache.DebugTest {
1953 fmt.Fprintf(os.Stderr, "testcache: %s: coverage metadata changed, re-running test: %v\n", a.Package.ImportPath, err)
1954 }
1955 return false
1956 }
1957 }
1958
1959 if len(data) == 0 || data[len(data)-1] != '\n' {
1960 if cache.DebugTest {
1961 if err != nil {
1962 fmt.Fprintf(os.Stderr, "testcache: %s: test output not found: %v\n", a.Package.ImportPath, err)
1963 } else {
1964 fmt.Fprintf(os.Stderr, "testcache: %s: test output malformed\n", a.Package.ImportPath)
1965 }
1966 }
1967 return false
1968 }
1969 if entry.Time.Before(testCacheExpire) {
1970 if cache.DebugTest {
1971 fmt.Fprintf(os.Stderr, "testcache: %s: test output expired due to go clean -testcache\n", a.Package.ImportPath)
1972 }
1973 return false
1974 }
1975 i := bytes.LastIndexByte(data[:len(data)-1], '\n') + 1
1976 if !bytes.HasPrefix(data[i:], []byte("ok \t")) {
1977 if cache.DebugTest {
1978 fmt.Fprintf(os.Stderr, "testcache: %s: test output malformed\n", a.Package.ImportPath)
1979 }
1980 return false
1981 }
1982 j := bytes.IndexByte(data[i+len("ok \t"):], '\t')
1983 if j < 0 {
1984 if cache.DebugTest {
1985 fmt.Fprintf(os.Stderr, "testcache: %s: test output malformed\n", a.Package.ImportPath)
1986 }
1987 return false
1988 }
1989 j += i + len("ok \t") + 1
1990
1991
1992 c.buf = new(bytes.Buffer)
1993 c.buf.Write(data[:j])
1994 c.buf.WriteString("(cached)")
1995 for j < len(data) && ('0' <= data[j] && data[j] <= '9' || data[j] == '.' || data[j] == 's') {
1996 j++
1997 }
1998 c.buf.Write(data[j:])
1999 return true
2000 }
2001
2002 var errBadTestInputs = errors.New("error parsing test inputs")
2003 var testlogMagic = []byte("# test log\n")
2004
2005
2006
2007
2008 func computeTestInputsID(a *work.Action, testlog []byte) (cache.ActionID, error) {
2009 testlog = bytes.TrimPrefix(testlog, testlogMagic)
2010 h := cache.NewHash("testInputs")
2011
2012 fmt.Fprintf(h, "env GODEBUG %x\n", hashGetenv("GODEBUG"))
2013 pwd := a.Package.Dir
2014 for _, line := range bytes.Split(testlog, []byte("\n")) {
2015 if len(line) == 0 {
2016 continue
2017 }
2018 s := string(line)
2019 op, name, found := strings.Cut(s, " ")
2020 if !found {
2021 if cache.DebugTest {
2022 fmt.Fprintf(os.Stderr, "testcache: %s: input list malformed (%q)\n", a.Package.ImportPath, line)
2023 }
2024 return cache.ActionID{}, errBadTestInputs
2025 }
2026 switch op {
2027 default:
2028 if cache.DebugTest {
2029 fmt.Fprintf(os.Stderr, "testcache: %s: input list malformed (%q)\n", a.Package.ImportPath, line)
2030 }
2031 return cache.ActionID{}, errBadTestInputs
2032 case "getenv":
2033 fmt.Fprintf(h, "env %s %x\n", name, hashGetenv(name))
2034 case "chdir":
2035 pwd = name
2036 fmt.Fprintf(h, "chdir %s %x\n", name, hashStat(name))
2037 case "stat":
2038 if !filepath.IsAbs(name) {
2039 name = filepath.Join(pwd, name)
2040 }
2041 if a.Package.Root == "" || search.InDir(name, a.Package.Root) == "" {
2042
2043 break
2044 }
2045 fmt.Fprintf(h, "stat %s %x\n", name, hashStat(name))
2046 case "open":
2047 if !filepath.IsAbs(name) {
2048 name = filepath.Join(pwd, name)
2049 }
2050 if a.Package.Root == "" || search.InDir(name, a.Package.Root) == "" {
2051
2052 break
2053 }
2054 fh, err := hashOpen(name)
2055 if err != nil {
2056 if cache.DebugTest {
2057 fmt.Fprintf(os.Stderr, "testcache: %s: input file %s: %s\n", a.Package.ImportPath, name, err)
2058 }
2059 return cache.ActionID{}, err
2060 }
2061 fmt.Fprintf(h, "open %s %x\n", name, fh)
2062 }
2063 }
2064 sum := h.Sum()
2065 return sum, nil
2066 }
2067
2068 func hashGetenv(name string) cache.ActionID {
2069 h := cache.NewHash("getenv")
2070 v, ok := os.LookupEnv(name)
2071 if !ok {
2072 h.Write([]byte{0})
2073 } else {
2074 h.Write([]byte{1})
2075 h.Write([]byte(v))
2076 }
2077 return h.Sum()
2078 }
2079
2080 const modTimeCutoff = 2 * time.Second
2081
2082 var errFileTooNew = errors.New("file used as input is too new")
2083
2084 func hashOpen(name string) (cache.ActionID, error) {
2085 h := cache.NewHash("open")
2086 info, err := os.Stat(name)
2087 if err != nil {
2088 fmt.Fprintf(h, "err %v\n", err)
2089 return h.Sum(), nil
2090 }
2091 hashWriteStat(h, info)
2092 if info.IsDir() {
2093 files, err := os.ReadDir(name)
2094 if err != nil {
2095 fmt.Fprintf(h, "err %v\n", err)
2096 }
2097 for _, f := range files {
2098 fmt.Fprintf(h, "file %s ", f.Name())
2099 finfo, err := f.Info()
2100 if err != nil {
2101 fmt.Fprintf(h, "err %v\n", err)
2102 } else {
2103 hashWriteStat(h, finfo)
2104 }
2105 }
2106 } else if info.Mode().IsRegular() {
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116 if time.Since(info.ModTime()) < modTimeCutoff {
2117 return cache.ActionID{}, errFileTooNew
2118 }
2119 }
2120 return h.Sum(), nil
2121 }
2122
2123 func hashStat(name string) cache.ActionID {
2124 h := cache.NewHash("stat")
2125 if info, err := os.Stat(name); err != nil {
2126 fmt.Fprintf(h, "err %v\n", err)
2127 } else {
2128 hashWriteStat(h, info)
2129 }
2130 if info, err := os.Lstat(name); err != nil {
2131 fmt.Fprintf(h, "err %v\n", err)
2132 } else {
2133 hashWriteStat(h, info)
2134 }
2135 return h.Sum()
2136 }
2137
2138 func hashWriteStat(h io.Writer, info fs.FileInfo) {
2139 fmt.Fprintf(h, "stat %d %x %v %v\n", info.Size(), uint64(info.Mode()), info.ModTime(), info.IsDir())
2140 }
2141
2142
2143 func testAndInputKey(testID, testInputsID cache.ActionID) cache.ActionID {
2144 return cache.Subkey(testID, fmt.Sprintf("inputs:%x", testInputsID))
2145 }
2146
2147
2148
2149
2150 func coverProfileAndInputKey(testID, testInputsID, covMetaID cache.ActionID) cache.ActionID {
2151 key := testAndInputKey(testID, testInputsID)
2152 if covMetaID != (cache.ActionID{}) {
2153 key = cache.Subkey(key, fmt.Sprintf("coverdeps:%x", covMetaID))
2154 }
2155 return cache.Subkey(key, "coverprofile")
2156 }
2157
2158 func (c *runCache) saveOutput(a *work.Action) {
2159 if c.id1 == (cache.ActionID{}) && c.id2 == (cache.ActionID{}) {
2160 return
2161 }
2162
2163
2164 testlog, err := os.ReadFile(a.Objdir + "testlog.txt")
2165 if err != nil || !bytes.HasPrefix(testlog, testlogMagic) || testlog[len(testlog)-1] != '\n' {
2166 if cache.DebugTest {
2167 if err != nil {
2168 fmt.Fprintf(os.Stderr, "testcache: %s: reading testlog: %v\n", a.Package.ImportPath, err)
2169 } else {
2170 fmt.Fprintf(os.Stderr, "testcache: %s: reading testlog: malformed\n", a.Package.ImportPath)
2171 }
2172 }
2173 return
2174 }
2175 testInputsID, err := computeTestInputsID(a, testlog)
2176 if err != nil {
2177 return
2178 }
2179 var coverProfile []byte
2180 if testCoverProfile != "" {
2181 coverProfile, err = os.ReadFile(coverProfTempFile(a))
2182 if err != nil {
2183 if cache.DebugTest {
2184 fmt.Fprintf(os.Stderr, "testcache: %s: reading cover profile: %v\n", a.Package.ImportPath, err)
2185 }
2186 return
2187 }
2188 }
2189 if c.id1 != (cache.ActionID{}) {
2190 if cache.DebugTest {
2191 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))
2192 }
2193 cache.PutNoVerify(cache.Default(), c.id1, bytes.NewReader(testlog))
2194 cache.PutNoVerify(cache.Default(), testAndInputKey(c.id1, testInputsID), bytes.NewReader(a.TestOutput.Bytes()))
2195 if coverProfile != nil {
2196 cache.PutNoVerify(cache.Default(), coverProfileAndInputKey(c.id1, testInputsID, c.covMeta), bytes.NewReader(coverProfile))
2197 } else if c.covMeta != (cache.ActionID{}) {
2198
2199
2200 cache.PutNoVerify(cache.Default(), coverProfileAndInputKey(c.id1, testInputsID, c.covMeta), bytes.NewReader(nil))
2201 }
2202 }
2203 if c.id2 != (cache.ActionID{}) {
2204 if cache.DebugTest {
2205 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))
2206 }
2207 cache.PutNoVerify(cache.Default(), c.id2, bytes.NewReader(testlog))
2208 cache.PutNoVerify(cache.Default(), testAndInputKey(c.id2, testInputsID), bytes.NewReader(a.TestOutput.Bytes()))
2209 if coverProfile != nil {
2210 cache.PutNoVerify(cache.Default(), coverProfileAndInputKey(c.id2, testInputsID, c.covMeta), bytes.NewReader(coverProfile))
2211 } else if c.covMeta != (cache.ActionID{}) {
2212
2213 cache.PutNoVerify(cache.Default(), coverProfileAndInputKey(c.id2, testInputsID, c.covMeta), bytes.NewReader(nil))
2214 }
2215 }
2216 }
2217
2218
2219
2220 func coveragePercentage(out []byte) string {
2221 if !cfg.BuildCover {
2222 return ""
2223 }
2224
2225
2226
2227 re := regexp.MustCompile(`coverage: (.*)\n`)
2228 matches := re.FindSubmatch(out)
2229 if matches == nil {
2230
2231
2232 return ""
2233 }
2234 return fmt.Sprintf("\tcoverage: %s", matches[1])
2235 }
2236
2237
2238 func builderCleanTest(b *work.Builder, ctx context.Context, a *work.Action) error {
2239 if cfg.BuildWork {
2240 return nil
2241 }
2242 b.Shell(a).RemoveAll(a.Objdir)
2243 return nil
2244 }
2245
2246
2247 func builderPrintTest(b *work.Builder, ctx context.Context, a *work.Action) error {
2248 run := a.Deps[0]
2249 if run.Mode == "test clean" {
2250 run = run.Deps[0]
2251 }
2252 if run.Mode != "test run" {
2253 base.Fatalf("internal error: cannot find test run to print")
2254 }
2255 if run.TestOutput != nil {
2256 os.Stdout.Write(run.TestOutput.Bytes())
2257 run.TestOutput = nil
2258 }
2259 return nil
2260 }
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277 func printExitStatus(b *work.Builder, ctx context.Context, a *work.Action) error {
2278 if !testJSON && testFuzz == "" && len(pkgArgs) != 0 {
2279 if base.GetExitStatus() != 0 {
2280 fmt.Println("FAIL")
2281 return nil
2282 }
2283 }
2284 return nil
2285 }
2286
2287
2288
2289
2290
2291
2292 func testBinaryName(p *load.Package) string {
2293 var elem string
2294 if p.ImportPath == "command-line-arguments" {
2295 elem = p.Name
2296 } else {
2297 elem = p.DefaultExecName()
2298 }
2299
2300 return elem + ".test"
2301 }
2302
View as plain text