github.com/AndrienkoAleksandr/go@v0.0.19/src/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 failures. 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 t.chatty.Updatef(t.name, "=== RUN %s\n", t.name) 320 } 321 f.common.inFuzzFn, f.inFuzzFn = true, true 322 go tRunner(t, func(t *T) { 323 args := []reflect.Value{reflect.ValueOf(t)} 324 for _, v := range e.Values { 325 args = append(args, reflect.ValueOf(v)) 326 } 327 // Before resetting the current coverage, defer the snapshot so that 328 // we make sure it is called right before the tRunner function 329 // exits, regardless of whether it was executed cleanly, panicked, 330 // or if the fuzzFn called t.Fatal. 331 if f.testContext.isFuzzing { 332 defer f.fuzzContext.deps.SnapshotCoverage() 333 f.fuzzContext.deps.ResetCoverage() 334 } 335 fn.Call(args) 336 }) 337 <-t.signal 338 if t.chatty != nil && t.chatty.json { 339 t.chatty.Updatef(t.parent.name, "=== NAME %s\n", t.parent.name) 340 } 341 f.common.inFuzzFn, f.inFuzzFn = false, false 342 return !t.Failed() 343 } 344 345 switch f.fuzzContext.mode { 346 case fuzzCoordinator: 347 // Fuzzing is enabled, and this is the test process started by 'go test'. 348 // Act as the coordinator process, and coordinate workers to perform the 349 // actual fuzzing. 350 corpusTargetDir := filepath.Join(corpusDir, f.name) 351 cacheTargetDir := filepath.Join(*fuzzCacheDir, f.name) 352 err := f.fuzzContext.deps.CoordinateFuzzing( 353 fuzzDuration.d, 354 int64(fuzzDuration.n), 355 minimizeDuration.d, 356 int64(minimizeDuration.n), 357 *parallel, 358 f.corpus, 359 types, 360 corpusTargetDir, 361 cacheTargetDir) 362 if err != nil { 363 f.result = fuzzResult{Error: err} 364 f.Fail() 365 fmt.Fprintf(f.w, "%v\n", err) 366 if crashErr, ok := err.(fuzzCrashError); ok { 367 crashPath := crashErr.CrashPath() 368 fmt.Fprintf(f.w, "Failing input written to %s\n", crashPath) 369 testName := filepath.Base(crashPath) 370 fmt.Fprintf(f.w, "To re-run:\ngo test -run=%s/%s\n", f.name, testName) 371 } 372 } 373 // TODO(jayconrod,katiehockman): Aggregate statistics across workers 374 // and add to FuzzResult (ie. time taken, num iterations) 375 376 case fuzzWorker: 377 // Fuzzing is enabled, and this is a worker process. Follow instructions 378 // from the coordinator. 379 if err := f.fuzzContext.deps.RunFuzzWorker(func(e corpusEntry) error { 380 // Don't write to f.w (which points to Stdout) if running from a 381 // fuzz worker. This would become very verbose, particularly during 382 // minimization. Return the error instead, and let the caller deal 383 // with the output. 384 var buf strings.Builder 385 if ok := run(&buf, e); !ok { 386 return errors.New(buf.String()) 387 } 388 return nil 389 }); err != nil { 390 // Internal errors are marked with f.Fail; user code may call this too, before F.Fuzz. 391 // The worker will exit with fuzzWorkerExitCode, indicating this is a failure 392 // (and 'go test' should exit non-zero) but a failing input should not be recorded. 393 f.Errorf("communicating with fuzzing coordinator: %v", err) 394 } 395 396 default: 397 // Fuzzing is not enabled, or will be done later. Only run the seed 398 // corpus now. 399 for _, e := range f.corpus { 400 name := fmt.Sprintf("%s/%s", f.name, filepath.Base(e.Path)) 401 if _, ok, _ := f.testContext.match.fullName(nil, name); ok { 402 run(f.w, e) 403 } 404 } 405 } 406 } 407 408 func (f *F) report() { 409 if *isFuzzWorker || f.parent == nil { 410 return 411 } 412 dstr := fmtDuration(f.duration) 413 format := "--- %s: %s (%s)\n" 414 if f.Failed() { 415 f.flushToParent(f.name, format, "FAIL", f.name, dstr) 416 } else if f.chatty != nil { 417 if f.Skipped() { 418 f.flushToParent(f.name, format, "SKIP", f.name, dstr) 419 } else { 420 f.flushToParent(f.name, format, "PASS", f.name, dstr) 421 } 422 } 423 } 424 425 // fuzzResult contains the results of a fuzz run. 426 type fuzzResult struct { 427 N int // The number of iterations. 428 T time.Duration // The total time taken. 429 Error error // Error is the error from the failing input 430 } 431 432 func (r fuzzResult) String() string { 433 if r.Error == nil { 434 return "" 435 } 436 return r.Error.Error() 437 } 438 439 // fuzzCrashError is satisfied by a failing input detected while fuzzing. 440 // These errors are written to the seed corpus and can be re-run with 'go test'. 441 // Errors within the fuzzing framework (like I/O errors between coordinator 442 // and worker processes) don't satisfy this interface. 443 type fuzzCrashError interface { 444 error 445 Unwrap() error 446 447 // CrashPath returns the path of the subtest that corresponds to the saved 448 // crash input file in the seed corpus. The test can be re-run with go test 449 // -run=$test/$name $test is the fuzz test name, and $name is the 450 // filepath.Base of the string returned here. 451 CrashPath() string 452 } 453 454 // fuzzContext holds fields common to all fuzz tests. 455 type fuzzContext struct { 456 deps testDeps 457 mode fuzzMode 458 } 459 460 type fuzzMode uint8 461 462 const ( 463 seedCorpusOnly fuzzMode = iota 464 fuzzCoordinator 465 fuzzWorker 466 ) 467 468 // runFuzzTests runs the fuzz tests matching the pattern for -run. This will 469 // only run the (*F).Fuzz function for each seed corpus without using the 470 // fuzzing engine to generate or mutate inputs. 471 func runFuzzTests(deps testDeps, fuzzTests []InternalFuzzTarget, deadline time.Time) (ran, ok bool) { 472 ok = true 473 if len(fuzzTests) == 0 || *isFuzzWorker { 474 return ran, ok 475 } 476 m := newMatcher(deps.MatchString, *match, "-test.run", *skip) 477 var mFuzz *matcher 478 if *matchFuzz != "" { 479 mFuzz = newMatcher(deps.MatchString, *matchFuzz, "-test.fuzz", *skip) 480 } 481 482 for _, procs := range cpuList { 483 runtime.GOMAXPROCS(procs) 484 for i := uint(0); i < *count; i++ { 485 if shouldFailFast() { 486 break 487 } 488 489 tctx := newTestContext(*parallel, m) 490 tctx.deadline = deadline 491 fctx := &fuzzContext{deps: deps, mode: seedCorpusOnly} 492 root := common{w: os.Stdout} // gather output in one place 493 if Verbose() { 494 root.chatty = newChattyPrinter(root.w) 495 } 496 for _, ft := range fuzzTests { 497 if shouldFailFast() { 498 break 499 } 500 testName, matched, _ := tctx.match.fullName(nil, ft.Name) 501 if !matched { 502 continue 503 } 504 if mFuzz != nil { 505 if _, fuzzMatched, _ := mFuzz.fullName(nil, ft.Name); fuzzMatched { 506 // If this will be fuzzed, then don't run the seed corpus 507 // right now. That will happen later. 508 continue 509 } 510 } 511 f := &F{ 512 common: common{ 513 signal: make(chan bool), 514 barrier: make(chan bool), 515 name: testName, 516 parent: &root, 517 level: root.level + 1, 518 chatty: root.chatty, 519 }, 520 testContext: tctx, 521 fuzzContext: fctx, 522 } 523 f.w = indenter{&f.common} 524 if f.chatty != nil { 525 f.chatty.Updatef(f.name, "=== RUN %s\n", f.name) 526 } 527 go fRunner(f, ft.Fn) 528 <-f.signal 529 if f.chatty != nil && f.chatty.json { 530 f.chatty.Updatef(f.parent.name, "=== NAME %s\n", f.parent.name) 531 } 532 ok = ok && !f.Failed() 533 ran = ran || f.ran 534 } 535 if !ran { 536 // There were no tests to run on this iteration. 537 // This won't change, so no reason to keep trying. 538 break 539 } 540 } 541 } 542 543 return ran, ok 544 } 545 546 // runFuzzing runs the fuzz test matching the pattern for -fuzz. Only one such 547 // fuzz test must match. This will run the fuzzing engine to generate and 548 // mutate new inputs against the fuzz target. 549 // 550 // If fuzzing is disabled (-test.fuzz is not set), runFuzzing 551 // returns immediately. 552 func runFuzzing(deps testDeps, fuzzTests []InternalFuzzTarget) (ok bool) { 553 if len(fuzzTests) == 0 || *matchFuzz == "" { 554 return true 555 } 556 m := newMatcher(deps.MatchString, *matchFuzz, "-test.fuzz", *skip) 557 tctx := newTestContext(1, m) 558 tctx.isFuzzing = true 559 fctx := &fuzzContext{ 560 deps: deps, 561 } 562 root := common{w: os.Stdout} 563 if *isFuzzWorker { 564 root.w = io.Discard 565 fctx.mode = fuzzWorker 566 } else { 567 fctx.mode = fuzzCoordinator 568 } 569 if Verbose() && !*isFuzzWorker { 570 root.chatty = newChattyPrinter(root.w) 571 } 572 var fuzzTest *InternalFuzzTarget 573 var testName string 574 var matched []string 575 for i := range fuzzTests { 576 name, ok, _ := tctx.match.fullName(nil, fuzzTests[i].Name) 577 if !ok { 578 continue 579 } 580 matched = append(matched, name) 581 fuzzTest = &fuzzTests[i] 582 testName = name 583 } 584 if len(matched) == 0 { 585 fmt.Fprintln(os.Stderr, "testing: warning: no fuzz tests to fuzz") 586 return true 587 } 588 if len(matched) > 1 { 589 fmt.Fprintf(os.Stderr, "testing: will not fuzz, -fuzz matches more than one fuzz test: %v\n", matched) 590 return false 591 } 592 593 f := &F{ 594 common: common{ 595 signal: make(chan bool), 596 barrier: nil, // T.Parallel has no effect when fuzzing. 597 name: testName, 598 parent: &root, 599 level: root.level + 1, 600 chatty: root.chatty, 601 }, 602 fuzzContext: fctx, 603 testContext: tctx, 604 } 605 f.w = indenter{&f.common} 606 if f.chatty != nil { 607 f.chatty.Updatef(f.name, "=== RUN %s\n", f.name) 608 } 609 go fRunner(f, fuzzTest.Fn) 610 <-f.signal 611 if f.chatty != nil { 612 f.chatty.Updatef(f.parent.name, "=== NAME %s\n", f.parent.name) 613 } 614 return !f.failed 615 } 616 617 // fRunner wraps a call to a fuzz test and ensures that cleanup functions are 618 // called and status flags are set. fRunner should be called in its own 619 // goroutine. To wait for its completion, receive from f.signal. 620 // 621 // fRunner is analogous to tRunner, which wraps subtests started with T.Run. 622 // Unit tests and fuzz tests work a little differently, so for now, these 623 // functions aren't consolidated. In particular, because there are no F.Run and 624 // F.Parallel methods, i.e., no fuzz sub-tests or parallel fuzz tests, a few 625 // simplifications are made. We also require that F.Fuzz, F.Skip, or F.Fail is 626 // called. 627 func fRunner(f *F, fn func(*F)) { 628 // When this goroutine is done, either because runtime.Goexit was called, a 629 // panic started, or fn returned normally, record the duration and send 630 // t.signal, indicating the fuzz test is done. 631 defer func() { 632 // Detect whether the fuzz test panicked or called runtime.Goexit 633 // without calling F.Fuzz, F.Fail, or F.Skip. If it did, panic (possibly 634 // replacing a nil panic value). Nothing should recover after fRunner 635 // unwinds, so this should crash the process and print stack. 636 // Unfortunately, recovering here adds stack frames, but the location of 637 // the original panic should still be 638 // clear. 639 if f.Failed() { 640 numFailed.Add(1) 641 } 642 err := recover() 643 if err == nil { 644 f.mu.RLock() 645 fuzzNotCalled := !f.fuzzCalled && !f.skipped && !f.failed 646 if !f.finished && !f.skipped && !f.failed { 647 err = errNilPanicOrGoexit 648 } 649 f.mu.RUnlock() 650 if fuzzNotCalled && err == nil { 651 f.Error("returned without calling F.Fuzz, F.Fail, or F.Skip") 652 } 653 } 654 655 // Use a deferred call to ensure that we report that the test is 656 // complete even if a cleanup function calls F.FailNow. See issue 41355. 657 didPanic := false 658 defer func() { 659 if !didPanic { 660 // Only report that the test is complete if it doesn't panic, 661 // as otherwise the test binary can exit before the panic is 662 // reported to the user. See issue 41479. 663 f.signal <- true 664 } 665 }() 666 667 // If we recovered a panic or inappropriate runtime.Goexit, fail the test, 668 // flush the output log up to the root, then panic. 669 doPanic := func(err any) { 670 f.Fail() 671 if r := f.runCleanup(recoverAndReturnPanic); r != nil { 672 f.Logf("cleanup panicked with %v", r) 673 } 674 for root := &f.common; root.parent != nil; root = root.parent { 675 root.mu.Lock() 676 root.duration += time.Since(root.start) 677 d := root.duration 678 root.mu.Unlock() 679 root.flushToParent(root.name, "--- FAIL: %s (%s)\n", root.name, fmtDuration(d)) 680 } 681 didPanic = true 682 panic(err) 683 } 684 if err != nil { 685 doPanic(err) 686 } 687 688 // No panic or inappropriate Goexit. 689 f.duration += time.Since(f.start) 690 691 if len(f.sub) > 0 { 692 // Unblock inputs that called T.Parallel while running the seed corpus. 693 // This only affects fuzz tests run as normal tests. 694 // While fuzzing, T.Parallel has no effect, so f.sub is empty, and this 695 // branch is not taken. f.barrier is nil in that case. 696 f.testContext.release() 697 close(f.barrier) 698 // Wait for the subtests to complete. 699 for _, sub := range f.sub { 700 <-sub.signal 701 } 702 cleanupStart := time.Now() 703 err := f.runCleanup(recoverAndReturnPanic) 704 f.duration += time.Since(cleanupStart) 705 if err != nil { 706 doPanic(err) 707 } 708 } 709 710 // Report after all subtests have finished. 711 f.report() 712 f.done = true 713 f.setRan() 714 }() 715 defer func() { 716 if len(f.sub) == 0 { 717 f.runCleanup(normalPanic) 718 } 719 }() 720 721 f.start = time.Now() 722 fn(f) 723 724 // Code beyond this point will not be executed when FailNow or SkipNow 725 // is invoked. 726 f.mu.Lock() 727 f.finished = true 728 f.mu.Unlock() 729 }