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