From 83e77bfa601d57cf7c8c03c662135947514428ce Mon Sep 17 00:00:00 2001 From: Gregory Petrosyan Date: Tue, 8 Aug 2023 12:34:37 +0300 Subject: [PATCH] Ensure TB.Helper() and TB.Name() are always usable Closes #58. --- engine.go | 41 +++++++++++++++++++++++++++++++---------- generator.go | 4 ++-- generator_test.go | 9 +++++++++ 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/engine.go b/engine.go index 7815fd3..a2ea5c3 100644 --- a/engine.go +++ b/engine.go @@ -360,7 +360,7 @@ func findBug(tb tb, deadline time.Time, checks int, seed uint64, prop func(*T)) } func checkOnce(t *T, prop func(*T)) (err *testError) { - if t.tbLog && t.tb != nil { + if t.tbLog { t.tb.Helper() } defer func() { err = panicToError(recover(), 3) }() @@ -477,6 +477,23 @@ type TB interface { type tb TB // tb is a private copy of TB, made to avoid T having public fields +type nilTB struct{} + +func (nilTB) Helper() {} +func (nilTB) Name() string { return "" } +func (nilTB) Logf(string, ...any) {} +func (nilTB) Log(...any) {} +func (nilTB) Skipf(string, ...any) { panic("call to TB.Skipf() outside a test") } +func (nilTB) Skip(...any) { panic("call to TB.Skip() outside a test") } +func (nilTB) SkipNow() { panic("call to TB.SkipNow() outside a test") } +func (nilTB) Errorf(string, ...any) { panic("call to TB.Errorf() outside a test") } +func (nilTB) Error(...any) { panic("call to TB.Error() outside a test") } +func (nilTB) Fatalf(string, ...any) { panic("call to TB.Fatalf() outside a test") } +func (nilTB) Fatal(...any) { panic("call to TB.Fatal() outside a test") } +func (nilTB) FailNow() { panic("call to TB.FailNow() outside a test") } +func (nilTB) Fail() { panic("call to TB.Fail() outside a test") } +func (nilTB) Failed() bool { panic("call to TB.Failed() outside a test") } + // T is similar to [testing.T], but with extra bookkeeping for property-based tests. // // For tests to be reproducible, they should generally run in a single goroutine. @@ -494,6 +511,10 @@ type T struct { } func newT(tb tb, s bitStream, tbLog bool, rawLog *log.Logger, refDraws ...any) *T { + if tb == nil { + tb = nilTB{} + } + t := &T{ tb: tb, tbLog: tbLog, @@ -515,13 +536,13 @@ func newT(tb tb, s bitStream, tbLog bool, rawLog *log.Logger, refDraws ...any) * } func (t *T) shouldLog() bool { - return t.rawLog != nil || (t.tbLog && t.tb != nil) + return t.rawLog != nil || t.tbLog } func (t *T) Logf(format string, args ...any) { if t.rawLog != nil { t.rawLog.Printf(format, args...) - } else if t.tbLog && t.tb != nil { + } else if t.tbLog { t.tb.Helper() t.tb.Logf(format, args...) } @@ -530,7 +551,7 @@ func (t *T) Logf(format string, args ...any) { func (t *T) Log(args ...any) { if t.rawLog != nil { t.rawLog.Print(args...) - } else if t.tbLog && t.tb != nil { + } else if t.tbLog { t.tb.Helper() t.tb.Log(args...) } @@ -538,7 +559,7 @@ func (t *T) Log(args ...any) { // Skipf is equivalent to [T.Logf] followed by [T.SkipNow]. func (t *T) Skipf(format string, args ...any) { - if t.tbLog && t.tb != nil { + if t.tbLog { t.tb.Helper() } t.Logf(format, args...) @@ -547,7 +568,7 @@ func (t *T) Skipf(format string, args ...any) { // Skip is equivalent to [T.Log] followed by [T.SkipNow]. func (t *T) Skip(args ...any) { - if t.tbLog && t.tb != nil { + if t.tbLog { t.tb.Helper() } t.Log(args...) @@ -567,7 +588,7 @@ func (t *T) SkipNow() { // Errorf is equivalent to [T.Logf] followed by [T.Fail]. func (t *T) Errorf(format string, args ...any) { - if t.tbLog && t.tb != nil { + if t.tbLog { t.tb.Helper() } t.Logf(format, args...) @@ -576,7 +597,7 @@ func (t *T) Errorf(format string, args ...any) { // Error is equivalent to [T.Log] followed by [T.Fail]. func (t *T) Error(args ...any) { - if t.tbLog && t.tb != nil { + if t.tbLog { t.tb.Helper() } t.Log(args...) @@ -585,7 +606,7 @@ func (t *T) Error(args ...any) { // Fatalf is equivalent to [T.Logf] followed by [T.FailNow]. func (t *T) Fatalf(format string, args ...any) { - if t.tbLog && t.tb != nil { + if t.tbLog { t.tb.Helper() } t.Logf(format, args...) @@ -594,7 +615,7 @@ func (t *T) Fatalf(format string, args ...any) { // Fatal is equivalent to [T.Log] followed by [T.FailNow]. func (t *T) Fatal(args ...any) { - if t.tbLog && t.tb != nil { + if t.tbLog { t.tb.Helper() } t.Log(args...) diff --git a/generator.go b/generator.go index 7acdd49..4dc424d 100644 --- a/generator.go +++ b/generator.go @@ -40,7 +40,7 @@ func (g *Generator[V]) String() string { // Draw produces a value from the generator. func (g *Generator[V]) Draw(t *T, label string) V { - if t.tbLog && t.tb != nil { + if t.tbLog { t.tb.Helper() } @@ -58,7 +58,7 @@ func (g *Generator[V]) Draw(t *T, label string) V { label = fmt.Sprintf("#%v", t.draws) } - if t.tbLog && t.tb != nil { + if t.tbLog { t.tb.Helper() } t.Logf("[rapid] draw %v: %#v", label, v) diff --git a/generator_test.go b/generator_test.go index 5d19a34..18702a0 100644 --- a/generator_test.go +++ b/generator_test.go @@ -32,3 +32,12 @@ func BenchmarkGenerator_Value(b *testing.B) { g.value(t) } } + +func TestExampleHelper(t *testing.T) { + g := Custom(func(t *T) int { + t.Helper() + return Int().Draw(t, t.Name()) + }) + + g.Example(0) +}