github.com/mtsmfm/go/src@v0.0.0-20221020090648-44bdcb9f8fde/testing/fuzz.go (about) 1 // Copyright 2020 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package testing 6 7 import ( 8 "errors" 9 "flag" 10 "fmt" 11 "io" 12 "os" 13 "path/filepath" 14 "reflect" 15 "runtime" 16 "strings" 17 "time" 18 ) 19 20 func initFuzzFlags() { 21 matchFuzz = flag.String("test.fuzz", "", "run the fuzz test matching `regexp`") 22 flag.Var(&fuzzDuration, "test.fuzztime", "time to spend fuzzing; default is to run indefinitely") 23 flag.Var(&minimizeDuration, "test.fuzzminimizetime", "time to spend minimizing a value after finding a failing input") 24 25 fuzzCacheDir = flag.String("test.fuzzcachedir", "", "directory where interesting fuzzing inputs are stored (for use only by cmd/go)") 26 isFuzzWorker = flag.Bool("test.fuzzworker", false, "coordinate with the parent process to fuzz random values (for use only by cmd/go)") 27 } 28 29 var ( 30 matchFuzz *string 31 fuzzDuration durationOrCountFlag 32 minimizeDuration = durationOrCountFlag{d: 60 * time.Second, allowZero: true} 33 fuzzCacheDir *string 34 isFuzzWorker *bool 35 36 // corpusDir is the parent directory of the fuzz test's seed corpus within 37 // the package. 38 corpusDir = "testdata/fuzz" 39 ) 40 41 // fuzzWorkerExitCode is used as an exit code by fuzz worker processes after an 42 // internal error. This distinguishes internal errors from uncontrolled panics 43 // and other failiures. Keep in sync with internal/fuzz.workerExitCode. 44 const fuzzWorkerExitCode = 70 45 46 // InternalFuzzTarget is an internal type but exported because it is 47 // cross-package; it is part of the implementation of the "go test" command. 48 type InternalFuzzTarget struct { 49 Name string 50 Fn func(f *F) 51 } 52 53 // F is a type passed to fuzz tests. 54 // 55 // Fuzz tests run generated inputs against a provided fuzz target, which can 56 // find and report potential bugs in the code being tested. 57 // 58 // A fuzz test runs the seed corpus by default, which includes entries provided 59 // by (*F).Add and entries in the testdata/fuzz/<FuzzTestName> directory. After 60 // any necessary setup and calls to (*F).Add, the fuzz test must then call 61 // (*F).Fuzz to provide the fuzz target. See the testing package documentation 62 // for an example, and see the F.Fuzz and F.Add method documentation for 63 // details. 64 // 65 // *F methods can only be called before (*F).Fuzz. Once the test is 66 // executing the fuzz target, only (*T) methods can be used. The only *F methods 67 // that are allowed in the (*F).Fuzz function are (*F).Failed and (*F).Name. 68 type F struct { 69 common 70 fuzzContext *fuzzContext 71 testContext *testContext 72 73 // inFuzzFn is true when the fuzz function is running. Most F methods cannot 74 // be called when inFuzzFn is true. 75 inFuzzFn bool 76 77 // corpus is a set of seed corpus entries, added with F.Add and loaded 78 // from testdata. 79 corpus []corpusEntry 80 81 result fuzzResult 82 fuzzCalled bool 83 } 84 85 var _ TB = (*F)(nil) 86 87 // corpusEntry is an alias to the same type as internal/fuzz.CorpusEntry. 88 // We use a type alias because we don't want to export this type, and we can't 89 // import internal/fuzz from testing. 90 type corpusEntry = struct { 91 Parent string 92 Path string 93 Data []byte 94 Values []any 95 Generation int 96 IsSeed bool 97 } 98 99 // Helper marks the calling function as a test helper function. 100 // When printing file and line information, that function will be skipped. 101 // Helper may be called simultaneously from multiple goroutines. 102 func (f *F) Helper() { 103 if f.inFuzzFn { 104 panic("testing: f.Helper was called inside the fuzz target, use t.Helper instead") 105 } 106 107 // common.Helper is inlined here. 108 // If we called it, it would mark F.Helper as the helper 109 // instead of the caller. 110 f.mu.Lock() 111 defer f.mu.Unlock() 112 if f.helperPCs == nil { 113 f.helperPCs = make(map[uintptr]struct{}) 114 } 115 // repeating code from callerName here to save walking a stack frame 116 var pc [1]uintptr 117 n := runtime.Callers(2, pc[:]) // skip runtime.Callers + Helper 118 if n == 0 { 119 panic("testing: zero callers found") 120 } 121 if _, found := f.helperPCs[pc[0]]; !found { 122 f.helperPCs[pc[0]] = struct{}{} 123 f.helperNames = nil // map will be recreated next time it is needed 124 } 125 } 126 127 // Fail marks the function as having failed but continues execution. 128 func (f *F) Fail() { 129 // (*F).Fail may be called by (*T).Fail, which we should allow. However, we 130 // shouldn't allow direct (*F).Fail calls from inside the (*F).Fuzz function. 131 if f.inFuzzFn { 132 panic("testing: f.Fail was called inside the fuzz target, use t.Fail instead") 133 } 134 f.common.Helper() 135 f.common.Fail() 136 } 137 138 // Skipped reports whether the test was skipped. 139 func (f *F) Skipped() bool { 140 // (*F).Skipped may be called by tRunner, which we should allow. However, we 141 // shouldn't allow direct (*F).Skipped calls from inside the (*F).Fuzz function. 142 if f.inFuzzFn { 143 panic("testing: f.Skipped was called inside the fuzz target, use t.Skipped instead") 144 } 145 f.common.Helper() 146 return f.common.Skipped() 147 } 148 149 // Add will add the arguments to the seed corpus for the fuzz test. This will be 150 // a no-op if called after or within the fuzz target, and args must match the 151 // arguments for the fuzz target. 152 func (f *F) Add(args ...any) { 153 var values []any 154 for i := range args { 155 if t := reflect.TypeOf(args[i]); !supportedTypes[t] { 156 panic(fmt.Sprintf("testing: unsupported type to Add %v", t)) 157 } 158 values = append(values, args[i]) 159 } 160 f.corpus = append(f.corpus, corpusEntry{Values: values, IsSeed: true, Path: fmt.Sprintf("seed#%d", len(f.corpus))}) 161 } 162 163 // supportedTypes represents all of the supported types which can be fuzzed. 164 var supportedTypes = map[reflect.Type]bool{ 165 reflect.TypeOf(([]byte)("")): true, 166 reflect.TypeOf((string)("")): true, 167 reflect.TypeOf((bool)(false)): true, 168 reflect.TypeOf((byte)(0)): true, 169 reflect.TypeOf((rune)(0)): true, 170 reflect.TypeOf((float32)(0)): true, 171 reflect.TypeOf((float64)(0)): true, 172 reflect.TypeOf((int)(0)): true, 173 reflect.TypeOf((int8)(0)): true, 174 reflect.TypeOf((int16)(0)): true, 175 reflect.TypeOf((int32)(0)): true, 176 reflect.TypeOf((int64)(0)): true, 177 reflect.TypeOf((uint)(0)): true, 178 reflect.TypeOf((uint8)(0)): true, 179 reflect.TypeOf((uint16)(0)): true, 180 reflect.TypeOf((uint32)(0)): true, 181 reflect.TypeOf((uint64)(0)): true, 182 } 183 184 // Fuzz runs the fuzz function, ff, for fuzz testing. If ff fails for a set of 185 // arguments, those arguments will be added to the seed corpus. 186 // 187 // ff must be a function with no return value whose first argument is *T and 188 // whose remaining arguments are the types to be fuzzed. 189 // For example: 190 // 191 // f.Fuzz(func(t *testing.T, b []byte, i int) { ... }) 192 // 193 // The following types are allowed: []byte, string, bool, byte, rune, float32, 194 // float64, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64. 195 // More types may be supported in the future. 196 // 197 // ff must not call any *F methods, e.g. (*F).Log, (*F).Error, (*F).Skip. Use 198 // the corresponding *T method instead. The only *F methods that are allowed in 199 // the (*F).Fuzz function are (*F).Failed and (*F).Name. 200 // 201 // This function should be fast and deterministic, and its behavior should not 202 // depend on shared state. No mutatable input arguments, or pointers to them, 203 // should be retained between executions of the fuzz function, as the memory 204 // backing them may be mutated during a subsequent invocation. ff must not 205 // modify the underlying data of the arguments provided by the fuzzing engine. 206 // 207 // When fuzzing, F.Fuzz does not return until a problem is found, time runs out 208 // (set with -fuzztime), or the test process is interrupted by a signal. F.Fuzz 209 // should be called exactly once, unless F.Skip or F.Fail is called beforehand. 210 func (f *F) Fuzz(ff any) { 211 if f.fuzzCalled { 212 panic("testing: F.Fuzz called more than once") 213 } 214 f.fuzzCalled = true 215 if f.failed { 216 return 217 } 218 f.Helper() 219 220 // ff should be in the form func(*testing.T, ...interface{}) 221 fn := reflect.ValueOf(ff) 222 fnType := fn.Type() 223 if fnType.Kind() != reflect.Func { 224 panic("testing: F.Fuzz must receive a function") 225 } 226 if fnType.NumIn() < 2 || fnType.In(0) != reflect.TypeOf((*T)(nil)) { 227 panic("testing: fuzz target must receive at least two arguments, where the first argument is a *T") 228 } 229 if fnType.NumOut() != 0 { 230 panic("testing: fuzz target must not return a value") 231 } 232 233 // Save the types of the function to compare against the corpus. 234 var types []reflect.Type 235 for i := 1; i < fnType.NumIn(); i++ { 236 t := fnType.In(i) 237 if !supportedTypes[t] { 238 panic(fmt.Sprintf("testing: unsupported type for fuzzing %v", t)) 239 } 240 types = append(types, t) 241 } 242 243 // Load the testdata seed corpus. Check types of entries in the testdata 244 // corpus and entries declared with F.Add. 245 // 246 // Don't load the seed corpus if this is a worker process; we won't use it. 247 if f.fuzzContext.mode != fuzzWorker { 248 for _, c := range f.corpus { 249 if err := f.fuzzContext.deps.CheckCorpus(c.Values, types); err != nil { 250 // TODO(#48302): Report the source location of the F.Add call. 251 f.Fatal(err) 252 } 253 } 254 255 // Load seed corpus 256 c, err := f.fuzzContext.deps.ReadCorpus(filepath.Join(corpusDir, f.name), types) 257 if err != nil { 258 f.Fatal(err) 259 } 260 for i := range c { 261 c[i].IsSeed = true // these are all seed corpus values 262 if f.fuzzContext.mode == fuzzCoordinator { 263 // If this is the coordinator process, zero the values, since we don't need 264 // to hold onto them. 265 c[i].Values = nil 266 } 267 } 268 269 f.corpus = append(f.corpus, c...) 270 } 271 272 // run calls fn on a given input, as a subtest with its own T. 273 // run is analogous to T.Run. The test filtering and cleanup works similarly. 274 // fn is called in its own goroutine. 275 run := func(captureOut io.Writer, e corpusEntry) (ok bool) { 276 if e.Values == nil { 277 // The corpusEntry must have non-nil Values in order to run the 278 // test. If Values is nil, it is a bug in our code. 279 panic(fmt.Sprintf("corpus file %q was not unmarshaled", e.Path)) 280 } 281 if shouldFailFast() { 282 return true 283 } 284 testName := f.name 285 if e.Path != "" { 286 testName = fmt.Sprintf("%s/%s", testName, filepath.Base(e.Path)) 287 } 288 if f.testContext.isFuzzing { 289 // Don't preserve subtest names while fuzzing. If fn calls T.Run, 290 // there will be a very large number of subtests with duplicate names, 291 // which will use a large amount of memory. The subtest names aren't 292 // useful since there's no way to re-run them deterministically. 293 f.testContext.match.clearSubNames() 294 } 295 296 // Record the stack trace at the point of this call so that if the subtest 297 // function - which runs in a separate stack - is marked as a helper, we can 298 // continue walking the stack into the parent test. 299 var pc [maxStackLen]uintptr 300 n := runtime.Callers(2, pc[:]) 301 t := &T{ 302 common: common{ 303 barrier: make(chan bool), 304 signal: make(chan bool), 305 name: testName, 306 parent: &f.common, 307 level: f.level + 1, 308 creator: pc[:n], 309 chatty: f.chatty, 310 }, 311 context: f.testContext, 312 } 313 if captureOut != nil { 314 // t.parent aliases f.common. 315 t.parent.w = captureOut 316 } 317 t.w = indenter{&t.common} 318 if t.chatty != nil { 319 // TODO(#48132): adjust this to work with test2json. 320 t.chatty.Updatef(t.name, "=== RUN %s\n", t.name) 321 } 322 f.common.inFuzzFn, f.inFuzzFn = true, true 323 go tRunner(t, func(t *T) { 324 args := []reflect.Value{reflect.ValueOf(t)} 325 for _, v := range e.Values { 326 args = append(args, reflect.ValueOf(v)) 327 } 328 // Before resetting the current coverage, defer the snapshot so that 329 // we make sure it is called right before the tRunner function 330 // exits, regardless of whether it was executed cleanly, panicked, 331 // or if the fuzzFn called t.Fatal. 332 if f.testContext.isFuzzing { 333 defer f.fuzzContext.deps.SnapshotCoverage() 334 f.fuzzContext.deps.ResetCoverage() 335 } 336 fn.Call(args) 337 }) 338 <-t.signal 339 f.common.inFuzzFn, f.inFuzzFn = false, false 340 return !t.Failed() 341 } 342 343 switch f.fuzzContext.mode { 344 case fuzzCoordinator: 345 // Fuzzing is enabled, and this is the test process started by 'go test'. 346 // Act as the coordinator process, and coordinate workers to perform the 347 // actual fuzzing. 348 corpusTargetDir := filepath.Join(corpusDir, f.name) 349 cacheTargetDir := filepath.Join(*fuzzCacheDir, f.name) 350 err := f.fuzzContext.deps.CoordinateFuzzing( 351 fuzzDuration.d, 352 int64(fuzzDuration.n), 353 minimizeDuration.d, 354 int64(minimizeDuration.n), 355 *parallel, 356 f.corpus, 357 types, 358 corpusTargetDir, 359 cacheTargetDir) 360 if err != nil { 361 f.result = fuzzResult{Error: err} 362 f.Fail() 363 fmt.Fprintf(f.w, "%v\n", err) 364 if crashErr, ok := err.(fuzzCrashError); ok { 365 crashPath := crashErr.CrashPath() 366 fmt.Fprintf(f.w, "Failing input written to %s\n", crashPath) 367 testName := filepath.Base(crashPath) 368 fmt.Fprintf(f.w, "To re-run:\ngo test -run=%s/%s\n", f.name, testName) 369 } 370 } 371 // TODO(jayconrod,katiehockman): Aggregate statistics across workers 372 // and add to FuzzResult (ie. time taken, num iterations) 373 374 case fuzzWorker: 375 // Fuzzing is enabled, and this is a worker process. Follow instructions 376 // from the coordinator. 377 if err := f.fuzzContext.deps.RunFuzzWorker(func(e corpusEntry) error { 378 // Don't write to f.w (which points to Stdout) if running from a 379 // fuzz worker. This would become very verbose, particularly during 380 // minimization. Return the error instead, and let the caller deal 381 // with the output. 382 var buf strings.Builder 383 if ok := run(&buf, e); !ok { 384 return errors.New(buf.String()) 385 } 386 return nil 387 }); err != nil { 388 // Internal errors are marked with f.Fail; user code may call this too, before F.Fuzz. 389 // The worker will exit with fuzzWorkerExitCode, indicating this is a failure 390 // (and 'go test' should exit non-zero) but a failing input should not be recorded. 391 f.Errorf("communicating with fuzzing coordinator: %v", err) 392 } 393 394 default: 395 // Fuzzing is not enabled, or will be done later. Only run the seed 396 // corpus now. 397 for _, e := range f.corpus { 398 name := fmt.Sprintf("%s/%s", f.name, filepath.Base(e.Path)) 399 if _, ok, _ := f.testContext.match.fullName(nil, name); ok { 400 run(f.w, e) 401 } 402 } 403 } 404 } 405 406 func (f *F) report() { 407 if *isFuzzWorker || f.parent == nil { 408 return 409 } 410 dstr := fmtDuration(f.duration) 411 format := "--- %s: %s (%s)\n" 412 if f.Failed() { 413 f.flushToParent(f.name, format, "FAIL", f.name, dstr) 414 } else if f.chatty != nil { 415 if f.Skipped() { 416 f.flushToParent(f.name, format, "SKIP", f.name, dstr) 417 } else { 418 f.flushToParent(f.name, format, "PASS", f.name, dstr) 419 } 420 } 421 } 422 423 // fuzzResult contains the results of a fuzz run. 424 type fuzzResult struct { 425 N int // The number of iterations. 426 T time.Duration // The total time taken. 427 Error error // Error is the error from the failing input 428 } 429 430 func (r fuzzResult) String() string { 431 if r.Error == nil { 432 return "" 433 } 434 return r.Error.Error() 435 } 436 437 // fuzzCrashError is satisfied by a failing input detected while fuzzing. 438 // These errors are written to the seed corpus and can be re-run with 'go test'. 439 // Errors within the fuzzing framework (like I/O errors between coordinator 440 // and worker processes) don't satisfy this interface. 441 type fuzzCrashError interface { 442 error 443 Unwrap() error 444 445 // CrashPath returns the path of the subtest that corresponds to the saved 446 // crash input file in the seed corpus. The test can be re-run with go test 447 // -run=$test/$name $test is the fuzz test name, and $name is the 448 // filepath.Base of the string returned here. 449 CrashPath() string 450 } 451 452 // fuzzContext holds fields common to all fuzz tests. 453 type fuzzContext struct { 454 deps testDeps 455 mode fuzzMode 456 } 457 458 type fuzzMode uint8 459 460 const ( 461 seedCorpusOnly fuzzMode = iota 462 fuzzCoordinator 463 fuzzWorker 464 ) 465 466 // runFuzzTests runs the fuzz tests matching the pattern for -run. This will 467 // only run the (*F).Fuzz function for each seed corpus without using the 468 // fuzzing engine to generate or mutate inputs. 469 func runFuzzTests(deps testDeps, fuzzTests []InternalFuzzTarget, deadline time.Time) (ran, ok bool) { 470 ok = true 471 if len(fuzzTests) == 0 || *isFuzzWorker { 472 return ran, ok 473 } 474 m := newMatcher(deps.MatchString, *match, "-test.run", *skip) 475 tctx := newTestContext(*parallel, m) 476 tctx.deadline = deadline 477 var mFuzz *matcher 478 if *matchFuzz != "" { 479 mFuzz = newMatcher(deps.MatchString, *matchFuzz, "-test.fuzz", *skip) 480 } 481 fctx := &fuzzContext{deps: deps, mode: seedCorpusOnly} 482 root := common{w: os.Stdout} // gather output in one place 483 if Verbose() { 484 root.chatty = newChattyPrinter(root.w) 485 } 486 for _, ft := range fuzzTests { 487 if shouldFailFast() { 488 break 489 } 490 testName, matched, _ := tctx.match.fullName(nil, ft.Name) 491 if !matched { 492 continue 493 } 494 if mFuzz != nil { 495 if _, fuzzMatched, _ := mFuzz.fullName(nil, ft.Name); fuzzMatched { 496 // If this will be fuzzed, then don't run the seed corpus 497 // right now. That will happen later. 498 continue 499 } 500 } 501 f := &F{ 502 common: common{ 503 signal: make(chan bool), 504 barrier: make(chan bool), 505 name: testName, 506 parent: &root, 507 level: root.level + 1, 508 chatty: root.chatty, 509 }, 510 testContext: tctx, 511 fuzzContext: fctx, 512 } 513 f.w = indenter{&f.common} 514 if f.chatty != nil { 515 // TODO(#48132): adjust this to work with test2json. 516 f.chatty.Updatef(f.name, "=== RUN %s\n", f.name) 517 } 518 519 go fRunner(f, ft.Fn) 520 <-f.signal 521 } 522 return root.ran, !root.Failed() 523 } 524 525 // runFuzzing runs the fuzz test matching the pattern for -fuzz. Only one such 526 // fuzz test must match. This will run the fuzzing engine to generate and 527 // mutate new inputs against the fuzz target. 528 // 529 // If fuzzing is disabled (-test.fuzz is not set), runFuzzing 530 // returns immediately. 531 func runFuzzing(deps testDeps, fuzzTests []InternalFuzzTarget) (ok bool) { 532 if len(fuzzTests) == 0 || *matchFuzz == "" { 533 return true 534 } 535 m := newMatcher(deps.MatchString, *matchFuzz, "-test.fuzz", *skip) 536 tctx := newTestContext(1, m) 537 tctx.isFuzzing = true 538 fctx := &fuzzContext{ 539 deps: deps, 540 } 541 root := common{w: os.Stdout} 542 if *isFuzzWorker { 543 root.w = io.Discard 544 fctx.mode = fuzzWorker 545 } else { 546 fctx.mode = fuzzCoordinator 547 } 548 if Verbose() && !*isFuzzWorker { 549 root.chatty = newChattyPrinter(root.w) 550 } 551 var fuzzTest *InternalFuzzTarget 552 var testName string 553 var matched []string 554 for i := range fuzzTests { 555 name, ok, _ := tctx.match.fullName(nil, fuzzTests[i].Name) 556 if !ok { 557 continue 558 } 559 matched = append(matched, name) 560 fuzzTest = &fuzzTests[i] 561 testName = name 562 } 563 if len(matched) == 0 { 564 fmt.Fprintln(os.Stderr, "testing: warning: no fuzz tests to fuzz") 565 return true 566 } 567 if len(matched) > 1 { 568 fmt.Fprintf(os.Stderr, "testing: will not fuzz, -fuzz matches more than one fuzz test: %v\n", matched) 569 return false 570 } 571 572 f := &F{ 573 common: common{ 574 signal: make(chan bool), 575 barrier: nil, // T.Parallel has no effect when fuzzing. 576 name: testName, 577 parent: &root, 578 level: root.level + 1, 579 chatty: root.chatty, 580 }, 581 fuzzContext: fctx, 582 testContext: tctx, 583 } 584 f.w = indenter{&f.common} 585 if f.chatty != nil { 586 // TODO(#48132): adjust this to work with test2json. 587 f.chatty.Updatef(f.name, "=== FUZZ %s\n", f.name) 588 } 589 go fRunner(f, fuzzTest.Fn) 590 <-f.signal 591 return !f.failed 592 } 593 594 // fRunner wraps a call to a fuzz test and ensures that cleanup functions are 595 // called and status flags are set. fRunner should be called in its own 596 // goroutine. To wait for its completion, receive from f.signal. 597 // 598 // fRunner is analogous to tRunner, which wraps subtests started with T.Run. 599 // Unit tests and fuzz tests work a little differently, so for now, these 600 // functions aren't consolidated. In particular, because there are no F.Run and 601 // F.Parallel methods, i.e., no fuzz sub-tests or parallel fuzz tests, a few 602 // simplifications are made. We also require that F.Fuzz, F.Skip, or F.Fail is 603 // called. 604 func fRunner(f *F, fn func(*F)) { 605 // When this goroutine is done, either because runtime.Goexit was called, a 606 // panic started, or fn returned normally, record the duration and send 607 // t.signal, indicating the fuzz test is done. 608 defer func() { 609 // Detect whether the fuzz test panicked or called runtime.Goexit 610 // without calling F.Fuzz, F.Fail, or F.Skip. If it did, panic (possibly 611 // replacing a nil panic value). Nothing should recover after fRunner 612 // unwinds, so this should crash the process and print stack. 613 // Unfortunately, recovering here adds stack frames, but the location of 614 // the original panic should still be 615 // clear. 616 if f.Failed() { 617 numFailed.Add(1) 618 } 619 err := recover() 620 if err == nil { 621 f.mu.RLock() 622 fuzzNotCalled := !f.fuzzCalled && !f.skipped && !f.failed 623 if !f.finished && !f.skipped && !f.failed { 624 err = errNilPanicOrGoexit 625 } 626 f.mu.RUnlock() 627 if fuzzNotCalled && err == nil { 628 f.Error("returned without calling F.Fuzz, F.Fail, or F.Skip") 629 } 630 } 631 632 // Use a deferred call to ensure that we report that the test is 633 // complete even if a cleanup function calls F.FailNow. See issue 41355. 634 didPanic := false 635 defer func() { 636 if !didPanic { 637 // Only report that the test is complete if it doesn't panic, 638 // as otherwise the test binary can exit before the panic is 639 // reported to the user. See issue 41479. 640 f.signal <- true 641 } 642 }() 643 644 // If we recovered a panic or inappropriate runtime.Goexit, fail the test, 645 // flush the output log up to the root, then panic. 646 doPanic := func(err any) { 647 f.Fail() 648 if r := f.runCleanup(recoverAndReturnPanic); r != nil { 649 f.Logf("cleanup panicked with %v", r) 650 } 651 for root := &f.common; root.parent != nil; root = root.parent { 652 root.mu.Lock() 653 root.duration += time.Since(root.start) 654 d := root.duration 655 root.mu.Unlock() 656 root.flushToParent(root.name, "--- FAIL: %s (%s)\n", root.name, fmtDuration(d)) 657 } 658 didPanic = true 659 panic(err) 660 } 661 if err != nil { 662 doPanic(err) 663 } 664 665 // No panic or inappropriate Goexit. 666 f.duration += time.Since(f.start) 667 668 if len(f.sub) > 0 { 669 // Unblock inputs that called T.Parallel while running the seed corpus. 670 // This only affects fuzz tests run as normal tests. 671 // While fuzzing, T.Parallel has no effect, so f.sub is empty, and this 672 // branch is not taken. f.barrier is nil in that case. 673 f.testContext.release() 674 close(f.barrier) 675 // Wait for the subtests to complete. 676 for _, sub := range f.sub { 677 <-sub.signal 678 } 679 cleanupStart := time.Now() 680 err := f.runCleanup(recoverAndReturnPanic) 681 f.duration += time.Since(cleanupStart) 682 if err != nil { 683 doPanic(err) 684 } 685 } 686 687 // Report after all subtests have finished. 688 f.report() 689 f.done = true 690 f.setRan() 691 }() 692 defer func() { 693 if len(f.sub) == 0 { 694 f.runCleanup(normalPanic) 695 } 696 }() 697 698 f.start = time.Now() 699 fn(f) 700 701 // Code beyond this point will not be executed when FailNow or SkipNow 702 // is invoked. 703 f.mu.Lock() 704 f.finished = true 705 f.mu.Unlock() 706 }