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