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