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