diff --git a/Race_test.go b/Race_test.go index 18b52a3..144bc59 100644 --- a/Race_test.go +++ b/Race_test.go @@ -14,6 +14,7 @@ import ( "github.com/adamluzsi/testcase" "github.com/adamluzsi/testcase/assert" "github.com/adamluzsi/testcase/internal" + "github.com/adamluzsi/testcase/internal/doubles" ) func TestRace(t *testing.T) { @@ -57,7 +58,7 @@ func TestRace(t *testing.T) { testcase.Race(func() { fn1Finished = true }, func() { - fakeTB := &testcase.StubTB{} + fakeTB := &doubles.TB{} // this only meant to represent why goroutine exit needs to be propagated. fakeTB.FailNow() fn2Finished = true diff --git a/Sandbox_test.go b/Sandbox_test.go index 6f19d8b..51f253e 100644 --- a/Sandbox_test.go +++ b/Sandbox_test.go @@ -7,12 +7,13 @@ import ( "github.com/adamluzsi/testcase" "github.com/adamluzsi/testcase/assert" + "github.com/adamluzsi/testcase/internal/doubles" "github.com/adamluzsi/testcase/random" "github.com/adamluzsi/testcase/sandbox" ) func ExampleSandbox() { - stb := &testcase.StubTB{} + stb := &doubles.TB{} outcome := testcase.Sandbox(func() { // some test helper function calls fatal, which cause runtime.Goexit after marking the test failed. stb.FailNow() diff --git a/Spec.go b/Spec.go index 71a9c08..a96f5cd 100644 --- a/Spec.go +++ b/Spec.go @@ -12,6 +12,7 @@ import ( "github.com/adamluzsi/testcase/assert" "github.com/adamluzsi/testcase/internal" "github.com/adamluzsi/testcase/internal/caller" + "github.com/adamluzsi/testcase/internal/teardown" ) // NewSpec create new Spec struct that is ready for usage. @@ -468,7 +469,7 @@ func (spec *Spec) Finish() { allHookOnce = append(allHookOnce, s.hooks.AroundAll...) })) spec.orderer.Order(tests) - td := &internal.Teardown{} + td := &teardown.Teardown{} defer td.Finish() for _, hook := range allHookOnce { td.Defer(hook.Block()) diff --git a/Spec_bc_test.go b/Spec_bc_test.go index 4a51398..001fe6f 100644 --- a/Spec_bc_test.go +++ b/Spec_bc_test.go @@ -6,10 +6,11 @@ import ( "github.com/adamluzsi/testcase/assert" "github.com/adamluzsi/testcase/internal" + "github.com/adamluzsi/testcase/internal/doubles" ) func TestSpec_FriendlyVarNotDefined(t *testing.T) { - stub := &StubTB{} + stub := &doubles.TB{} s := NewSpec(stub) willFatalWithMessage := willFatalWithMessageFn(stub) @@ -30,7 +31,7 @@ func TestSpec_FriendlyVarNotDefined(t *testing.T) { }) } -func isFatalFn(stub *StubTB) func(block func()) bool { +func isFatalFn(stub *doubles.TB) func(block func()) bool { return func(block func()) bool { stub.IsFailed = false defer func() { stub.IsFailed = false }() @@ -39,7 +40,7 @@ func isFatalFn(stub *StubTB) func(block func()) bool { } } -func willFatalWithMessageFn(stub *StubTB) func(tb testing.TB, blk func()) bytes.Buffer { +func willFatalWithMessageFn(stub *doubles.TB) func(tb testing.TB, blk func()) bytes.Buffer { isFatal := isFatalFn(stub) return func(tb testing.TB, blk func()) bytes.Buffer { stub.Logs = bytes.Buffer{} diff --git a/Spec_test.go b/Spec_test.go index 5eded4f..82079f9 100644 --- a/Spec_test.go +++ b/Spec_test.go @@ -12,6 +12,7 @@ import ( "github.com/adamluzsi/testcase/assert" "github.com/adamluzsi/testcase/internal" + doubles2 "github.com/adamluzsi/testcase/internal/doubles" "github.com/adamluzsi/testcase/internal/spechelper" "github.com/adamluzsi/testcase/random" @@ -257,7 +258,7 @@ func TestSpec_ParallelSafeVariableSupport(t *testing.T) { } func TestSpec_InvalidUsages(t *testing.T) { - stub := &testcase.StubTB{} + stub := &doubles2.TB{} s := testcase.NewSpec(stub) nest1Value := rand.Int() nest2Value := rand.Int() @@ -433,7 +434,7 @@ func hackCallParallel(tb testing.TB) { switch tb := tb.(type) { case *testing.T: tb.Parallel() - case *internal.RecorderTB: + case *doubles2.RecorderTB: hackCallParallel(tb.TB) case *testcase.T: hackCallParallel(tb.TB) @@ -600,7 +601,7 @@ func TestSpec_LetValue_ValueDefinedAtDeclarationWithoutTheNeedOfFunctionCallback } func TestSpec_LetValue_mutableValuesAreNotAllowed(t *testing.T) { - stub := &testcase.StubTB{} + stub := &doubles2.TB{} s := testcase.NewSpec(stub) var finished bool @@ -696,7 +697,7 @@ func (tb *UnknownTestingTB) Log(args ...interface{}) { } func TestSpec_Test_withSomethingThatImplementsTestcaseTB(t *testing.T) { - rtb := &internal.RecorderTB{TB: &testcase.StubTB{}} + rtb := &doubles2.RecorderTB{TB: &doubles2.TB{}} var tb testcase.TBRunner = rtb // implements check s := testcase.NewSpec(tb) @@ -831,7 +832,7 @@ func TestSpec_panicDoNotLeakOutFromTestingScope(t *testing.T) { var noPanic bool func() { defer recover() - rtb := &internal.RecorderTB{TB: &testcase.StubTB{}} + rtb := &doubles2.RecorderTB{TB: &doubles2.TB{}} defer rtb.CleanupNow() s := testcase.NewSpec(rtb) s.Test(``, func(t *testcase.T) { panic(`die`) }) @@ -966,7 +967,7 @@ func BenchmarkTest_Spec_SkipBenchmark2(b *testing.B) { } type stubB struct { - *testcase.StubTB + *doubles2.TB TestingB *testing.B } @@ -975,9 +976,9 @@ func (b *stubB) Run(name string, fn func(b *testing.B)) bool { } func BenchmarkTest_Spec_SkipBenchmark_invalidUse(b *testing.B) { - stub := &testcase.StubTB{} + stub := &doubles2.TB{} stb := &stubB{ - StubTB: stub, + TB: stub, TestingB: b, } s := testcase.NewSpec(stb) @@ -1005,7 +1006,7 @@ func BenchmarkTest_Spec_Test_flaky(b *testing.B) { } func TestSpec_Test_FailNowWithCustom(t *testing.T) { - rtb := &internal.RecorderTB{TB: &testcase.StubTB{}} + rtb := &doubles2.RecorderTB{TB: &doubles2.TB{}} s := testcase.NewSpec(rtb) var failCount int @@ -1020,7 +1021,7 @@ func TestSpec_Test_FailNowWithCustom(t *testing.T) { } func TestSpec_Test_flaky_withoutFlakyFlag_willFailAndNeverRunAgain(t *testing.T) { - stub := &testcase.StubTB{} + stub := &doubles2.TB{} s := testcase.NewSpec(stub) var total int s.Test(``, func(t *testcase.T) { total++; t.FailNow() }) @@ -1174,7 +1175,7 @@ func TestSpec_Finish(t *testing.T) { } func TestSpec_Finish_finishedSpecIsImmutable(t *testing.T) { - stub := &testcase.StubTB{} + stub := &doubles2.TB{} s := testcase.NewSpec(stub) s.Before(func(t *testcase.T) {}) s.Finish() @@ -1313,7 +1314,7 @@ func TestNewSpec_withTestingT_optionsPassed(t *testing.T) { func TestNewSpec_withTestcaseT_optionsPassed(t *testing.T) { testcase.NewSpec(t).Test(``, func(t *testcase.T) { rnd := random.New(random.CryptoSeed{}) - stub := &testcase.StubTB{} + stub := &doubles2.TB{} s := testcase.NewSpec(t, testcase.Flaky(time.Second)) s.Test(``, func(t *testcase.T) { if rnd.Bool() { diff --git a/Suite_test.go b/Suite_test.go index 5cb56ce..77a1efe 100644 --- a/Suite_test.go +++ b/Suite_test.go @@ -5,12 +5,13 @@ import ( "github.com/adamluzsi/testcase" "github.com/adamluzsi/testcase/assert" + "github.com/adamluzsi/testcase/internal/doubles" ) func TestRunSuite(t *testing.T) { t.Run(`when TB is testing.TB`, func(t *testing.T) { sT := &RunContractContract{} - var tb testing.TB = &testcase.StubTB{} + var tb testing.TB = &doubles.TB{} tb = testcase.NewT(tb, testcase.NewSpec(tb)) testcase.RunSuite(tb, sT) assert.Must(t).True(sT.SpecWasCalled) diff --git a/T.go b/T.go index cd7d96c..6f09573 100644 --- a/T.go +++ b/T.go @@ -7,9 +7,8 @@ import ( "time" "github.com/adamluzsi/testcase/assert" + "github.com/adamluzsi/testcase/internal/teardown" "github.com/adamluzsi/testcase/random" - - "github.com/adamluzsi/testcase/internal" ) // NewT returns a *testcase.T prepared for the given testing.TB @@ -37,7 +36,7 @@ func newT(tb testing.TB, spec *Spec) *T { spec: spec, vars: newVariables(), tags: spec.getTagSet(), - teardown: &internal.Teardown{CallerOffset: 1}, + teardown: &teardown.Teardown{CallerOffset: 1}, } } @@ -66,7 +65,7 @@ type T struct { spec *Spec vars *variables tags map[string]struct{} - teardown *internal.Teardown + teardown *teardown.Teardown // TODO: protect it against concurrency timerPaused bool diff --git a/T_test.go b/T_test.go index 8d3e813..d4be897 100644 --- a/T_test.go +++ b/T_test.go @@ -10,12 +10,11 @@ import ( "github.com/adamluzsi/testcase/assert" "github.com/adamluzsi/testcase/contracts" + doubles2 "github.com/adamluzsi/testcase/internal/doubles" "github.com/adamluzsi/testcase/sandbox" "github.com/adamluzsi/testcase/random" - "github.com/adamluzsi/testcase/internal" - "github.com/adamluzsi/testcase" ) @@ -24,7 +23,7 @@ var _ testing.TB = &testcase.T{} func TestT_implementsTestingTB(t *testing.T) { testcase.RunSuite(t, contracts.TestingTB{ Subject: func(t *testcase.T) testing.TB { - stub := &testcase.StubTB{} + stub := &doubles2.TB{} t.Cleanup(stub.Finish) return testcase.NewT(stub, nil) }, @@ -131,7 +130,7 @@ func TestT_Defer_failNowWillNotHang(t *testing.T) { go func() { defer wg.Done() defer recover() - s := testcase.NewSpec(&internal.RecorderTB{}) + s := testcase.NewSpec(&doubles2.RecorderTB{}) s.Before(func(t *testcase.T) { t.Defer(func() { t.FailNow() }) @@ -356,7 +355,7 @@ func TestT_Random(t *testing.T) { func TestT_Eventually(t *testing.T) { t.Run(`with default eventually retry strategy`, func(t *testing.T) { - stub := &testcase.StubTB{} + stub := &doubles2.TB{} s := testcase.NewSpec(stub) s.HasSideEffect() var eventuallyRan bool @@ -373,7 +372,7 @@ func TestT_Eventually(t *testing.T) { }) t.Run(`with config passed`, func(t *testing.T) { - stub := &testcase.StubTB{} + stub := &doubles2.TB{} var strategyUsed bool strategy := assert.RetryStrategyFunc(func(condition func() bool) { strategyUsed = true @@ -402,7 +401,7 @@ func TestNewT(t *testing.T) { Init: func(t *testcase.T) int { return t.Random.Int() }, } t.Run(`with *Spec`, func(t *testing.T) { - tb := &testcase.StubTB{} + tb := &doubles2.TB{} t.Cleanup(tb.Finish) s := testcase.NewSpec(tb) expectedY := rnd.Int() @@ -412,7 +411,7 @@ func TestNewT(t *testing.T) { assert.Must(t).Equal(v.Get(subject), v.Get(subject), `has test variable cache`) }) t.Run(`without *Spec`, func(t *testing.T) { - tb := &testcase.StubTB{} + tb := &doubles2.TB{} t.Cleanup(tb.Finish) expectedY := rnd.Int() subject := testcase.NewT(tb, nil) @@ -421,7 +420,7 @@ func TestNewT(t *testing.T) { assert.Must(t).Equal(v.Get(subject), v.Get(subject), `has test variable cache`) }) t.Run(`with *testcase.T, same returned`, func(t *testing.T) { - tb := &testcase.StubTB{} + tb := &doubles2.TB{} t.Cleanup(tb.Finish) tcT1 := testcase.NewT(tb, nil) tcT2 := testcase.NewT(tcT1, nil) @@ -431,7 +430,7 @@ func TestNewT(t *testing.T) { assert.Must(t).Nil(testcase.NewT(nil, nil)) }) t.Run(`when NewT is retrieved multiple times, hooks executed only once`, func(t *testing.T) { - stb := &testcase.StubTB{} + stb := &doubles2.TB{} s := testcase.NewSpec(stb) var out []struct{} s.Before(func(t *testcase.T) { @@ -474,7 +473,7 @@ func TestT_SkipUntil(t *testing.T) { rnd := random.New(rand.NewSource(time.Now().UnixNano())) future := time.Now().AddDate(0, 0, 1) t.Run("before SkipUntil deadline, test is skipped", func(t *testing.T) { - stubTB := &testcase.StubTB{} + stubTB := &doubles2.TB{} s := testcase.NewSpec(stubTB) var ran bool s.Test("", func(t *testcase.T) { @@ -488,7 +487,7 @@ func TestT_SkipUntil(t *testing.T) { assert.Must(t).Contain(stubTB.Logs.String(), fmt.Sprintf(skipUntilFormat, future.Format(timeLayout))) }) t.Run("at or after SkipUntil deadline, test is failed", func(t *testing.T) { - stubTB := &testcase.StubTB{} + stubTB := &doubles2.TB{} s := testcase.NewSpec(stubTB) today := time.Now().AddDate(0, 0, -1*rnd.IntN(3)) var ran bool diff --git a/Var_test.go b/Var_test.go index 60a8860..8f5e539 100644 --- a/Var_test.go +++ b/Var_test.go @@ -7,6 +7,7 @@ import ( "time" "github.com/adamluzsi/testcase/assert" + "github.com/adamluzsi/testcase/internal/doubles" "github.com/adamluzsi/testcase/random" "github.com/adamluzsi/testcase" @@ -25,7 +26,7 @@ func TestVar(t *testing.T) { var testVar = testcase.Var[int]{ID: rnd.StringNWithCharset(5, "abcdefghijklmnopqrstuvwxyz")} expected := rnd.Int() - stub := &testcase.StubTB{} + stub := &doubles.TB{} willFatal := willFatalWithMessageFn(stub) willFatalWithVariableNotFoundMessage := func(s *testcase.Spec, tb testing.TB, varName string, blk func(*testcase.T)) { tct := testcase.NewT(stub, s) @@ -779,7 +780,7 @@ func TestVar_Before(t *testing.T) { func TestVar_missingID(t *testing.T) { varWithoutID := testcase.Var[string]{} - stub := &testcase.StubTB{} + stub := &doubles.TB{} tct := testcase.NewT(stub, nil) assert.Panic(t, func() { _ = varWithoutID.Get(tct) }) assert.Contain(t, stub.Logs.String(), "ID for testcase.Var[string] is missing. Maybe it's uninitialized?") diff --git a/doubles.go b/doubles.go new file mode 100644 index 0000000..3508307 --- /dev/null +++ b/doubles.go @@ -0,0 +1,8 @@ +package testcase + +import ( + "github.com/adamluzsi/testcase/internal/doubles" +) + +// StubTB is a stub that implements testing.TB +type StubTB = doubles.TB diff --git a/env_test.go b/env_test.go index 2dddb22..8a0ec1f 100644 --- a/env_test.go +++ b/env_test.go @@ -6,14 +6,15 @@ import ( "github.com/adamluzsi/testcase" "github.com/adamluzsi/testcase/internal" + doubles2 "github.com/adamluzsi/testcase/internal/doubles" ) func TestEnvVarHelpers(t *testing.T) { s := testcase.NewSpec(t) s.Describe(`#SetEnv`, func(s *testcase.Spec) { var ( - recTB = testcase.Let(s, func(t *testcase.T) *internal.RecorderTB { - return &internal.RecorderTB{TB: &testcase.StubTB{}} + recTB = testcase.Let(s, func(t *testcase.T) *doubles2.RecorderTB { + return &doubles2.RecorderTB{TB: &doubles2.TB{}} }) tbCleanupNow = func(t *testcase.T) { recTB.Get(t).CleanupNow() } key = testcase.Let(s, func(t *testcase.T) string { @@ -97,7 +98,7 @@ func TestEnvVarHelpers(t *testing.T) { s.Describe(`#UnsetEnv`, func(s *testcase.Spec) { var ( - recTB = testcase.Let(s, func(t *testcase.T) *internal.RecorderTB { return &internal.RecorderTB{} }) + recTB = testcase.Let(s, func(t *testcase.T) *doubles2.RecorderTB { return &doubles2.RecorderTB{} }) tbCleanupNow = func(t *testcase.T) { recTB.Get(t).CleanupNow() } key = testcase.Let(s, func(t *testcase.T) string { return `TESTING_DATA_` + t.Random.StringNWithCharset(5, "ABCDEFGHIJKLMNOPQRSTUVWXYZ") diff --git a/examples_test.go b/examples_test.go index 9bdf64f..18b9920 100644 --- a/examples_test.go +++ b/examples_test.go @@ -13,6 +13,7 @@ import ( "github.com/adamluzsi/testcase" "github.com/adamluzsi/testcase/assert" "github.com/adamluzsi/testcase/faultinject" + "github.com/adamluzsi/testcase/internal/doubles" "github.com/adamluzsi/testcase/internal/example/memory" "github.com/adamluzsi/testcase/internal/example/mydomain" "github.com/adamluzsi/testcase/internal/example/spechelper" @@ -481,7 +482,7 @@ func ExampleT_should() { } func ExampleStubTB_testingATestHelper() { - stub := &testcase.StubTB{} + stub := &doubles.TB{} stub.Log("hello", "world") fmt.Println(stub.Logs.String()) @@ -905,8 +906,8 @@ func ExampleSpec_Let_testingDouble() { var t *testing.T s := testcase.NewSpec(t) - stubTB := testcase.Let(s, func(t *testcase.T) *testcase.StubTB { - stub := &testcase.StubTB{} + stubTB := testcase.Let(s, func(t *testcase.T) *doubles.TB { + stub := &doubles.TB{} t.Defer(stub.Finish) return stub }) diff --git a/hooks_test.go b/hooks_test.go index 4330fc6..fd76f96 100644 --- a/hooks_test.go +++ b/hooks_test.go @@ -7,6 +7,7 @@ import ( "github.com/adamluzsi/testcase" "github.com/adamluzsi/testcase/assert" "github.com/adamluzsi/testcase/internal" + "github.com/adamluzsi/testcase/internal/doubles" ) func TestSpec_Before_Ordered(t *testing.T) { @@ -126,7 +127,7 @@ func TestSpec_AroundAll_blkRunsOnlyOnce(t *testing.T) { func TestSpec_BeforeAll_failIfDefinedAfterTestCases(t *testing.T) { var isAnyOfTheTestCaseRan bool blk := func(t *testcase.T) { isAnyOfTheTestCaseRan = true } - stub := &testcase.StubTB{} + stub := &doubles.TB{} internal.RecoverGoexit(func() { s := testcase.NewSpec(stub) @@ -143,7 +144,7 @@ func TestSpec_BeforeAll_failIfDefinedAfterTestCases(t *testing.T) { func TestSpec_AfterAll_failIfDefinedAfterTestCases(t *testing.T) { var isAnyOfTheTestCaseRan bool blk := func(t *testcase.T) { isAnyOfTheTestCaseRan = true } - stub := &testcase.StubTB{} + stub := &doubles.TB{} internal.RecoverGoexit(func() { s := testcase.NewSpec(stub) @@ -160,7 +161,7 @@ func TestSpec_AfterAll_failIfDefinedAfterTestCases(t *testing.T) { func TestSpec_AroundAll_failIfDefinedAfterTestCases(t *testing.T) { var isAnyOfTheTestCaseRan bool blk := func(t *testcase.T) { isAnyOfTheTestCaseRan = true } - stub := &testcase.StubTB{} + stub := &doubles.TB{} internal.RecoverGoexit(func() { s := testcase.NewSpec(stub) diff --git a/internal/backward.go b/internal/backward.go new file mode 100644 index 0000000..257b043 --- /dev/null +++ b/internal/backward.go @@ -0,0 +1,9 @@ +package internal + +import ( + "github.com/adamluzsi/testcase/internal/doubles" + "github.com/adamluzsi/testcase/internal/teardown" +) + +type RecorderTB = doubles.RecorderTB +type Teardown = teardown.Teardown diff --git a/internal/RecorderTB.go b/internal/doubles/RecorderTB.go similarity index 97% rename from internal/RecorderTB.go rename to internal/doubles/RecorderTB.go index 1f055ed..2da326d 100644 --- a/internal/RecorderTB.go +++ b/internal/doubles/RecorderTB.go @@ -1,9 +1,11 @@ -package internal +package doubles import ( "runtime" "sync" "testing" + + "github.com/adamluzsi/testcase/internal/teardown" ) type RecorderTB struct { @@ -60,7 +62,7 @@ func (rtb *RecorderTB) Forward() { func (rtb *RecorderTB) CleanupNow() { defer rtb.withPassthrough()() - td := &Teardown{} + td := &teardown.Teardown{} for _, event := range rtb.records { if event.Cleanup != nil && !event.Skip { td.Defer(event.Cleanup) diff --git a/internal/RecorderTB_test.go b/internal/doubles/RecorderTB_test.go similarity index 95% rename from internal/RecorderTB_test.go rename to internal/doubles/RecorderTB_test.go index 3ff8b46..04a7520 100644 --- a/internal/RecorderTB_test.go +++ b/internal/doubles/RecorderTB_test.go @@ -1,4 +1,4 @@ -package internal_test +package doubles_test import ( "fmt" @@ -10,22 +10,22 @@ import ( "github.com/adamluzsi/testcase" "github.com/adamluzsi/testcase/assert" "github.com/adamluzsi/testcase/contracts" - "github.com/adamluzsi/testcase/internal" + doubles2 "github.com/adamluzsi/testcase/internal/doubles" "github.com/adamluzsi/testcase/sandbox" ) -var _ testcase.TBRunner = &internal.RecorderTB{} +var _ testcase.TBRunner = &doubles2.RecorderTB{} func TestRecorderTB(t *testing.T) { s := testcase.NewSpec(t) - stubTB := testcase.Let(s, func(t *testcase.T) *testcase.StubTB { - stub := &testcase.StubTB{} + stubTB := testcase.Let(s, func(t *testcase.T) *doubles2.TB { + stub := &doubles2.TB{} t.Cleanup(stub.Finish) return stub }) - recorder := testcase.Let(s, func(t *testcase.T) *internal.RecorderTB { - return &internal.RecorderTB{TB: stubTB.Get(t)} + recorder := testcase.Let(s, func(t *testcase.T) *doubles2.RecorderTB { + return &doubles2.RecorderTB{TB: stubTB.Get(t)} }) expectToExitGoroutine := func(t *testcase.T, fn func()) { @@ -65,7 +65,7 @@ func TestRecorderTB(t *testing.T) { }) } - thenUnderlyingTBWillExpect := func(s *testcase.Spec, subject func(t *testcase.T), fn func(t *testcase.T, stub *testcase.StubTB)) { + thenUnderlyingTBWillExpect := func(s *testcase.Spec, subject func(t *testcase.T), fn func(t *testcase.T, stub *doubles2.TB)) { s.Then(`on #Forward, the method call is forwarded to the received testing.TB`, func(t *testcase.T) { fn(t, stubTB.Get(t)) subject(t) @@ -84,7 +84,7 @@ func TestRecorderTB(t *testing.T) { thenTBWillMarkedAsFailed(s, subject) - thenUnderlyingTBWillExpect(s, subject, func(t *testcase.T, stub *testcase.StubTB) { + thenUnderlyingTBWillExpect(s, subject, func(t *testcase.T, stub *doubles2.TB) { t.Cleanup(func() { t.Must.True(stub.IsFailed) }) @@ -98,7 +98,7 @@ func TestRecorderTB(t *testing.T) { thenTBWillMarkedAsFailed(s, subject) - thenUnderlyingTBWillExpect(s, subject, func(t *testcase.T, stub *testcase.StubTB) { + thenUnderlyingTBWillExpect(s, subject, func(t *testcase.T, stub *doubles2.TB) { t.Cleanup(func() { t.Must.True(stub.IsFailed) }) @@ -112,7 +112,7 @@ func TestRecorderTB(t *testing.T) { thenTBWillMarkedAsFailed(s, subject) - thenUnderlyingTBWillExpect(s, subject, func(t *testcase.T, stub *testcase.StubTB) { + thenUnderlyingTBWillExpect(s, subject, func(t *testcase.T, stub *doubles2.TB) { t.Cleanup(func() { t.Must.Contain(stub.Logs.String(), `foo`) }) @@ -126,7 +126,7 @@ func TestRecorderTB(t *testing.T) { thenTBWillMarkedAsFailed(s, subject) - thenUnderlyingTBWillExpect(s, subject, func(t *testcase.T, stub *testcase.StubTB) { + thenUnderlyingTBWillExpect(s, subject, func(t *testcase.T, stub *doubles2.TB) { t.Cleanup(func() { t.Must.Contain(stub.Logs.String(), `errorf -`) }) @@ -140,7 +140,7 @@ func TestRecorderTB(t *testing.T) { thenTBWillMarkedAsFailed(s, subject) - thenUnderlyingTBWillExpect(s, subject, func(t *testcase.T, stub *testcase.StubTB) { + thenUnderlyingTBWillExpect(s, subject, func(t *testcase.T, stub *doubles2.TB) { t.Cleanup(func() { t.Must.Contain(stub.Logs.String(), `fatal`) }) @@ -154,7 +154,7 @@ func TestRecorderTB(t *testing.T) { thenTBWillMarkedAsFailed(s, subject) - thenUnderlyingTBWillExpect(s, subject, func(t *testcase.T, stub *testcase.StubTB) { + thenUnderlyingTBWillExpect(s, subject, func(t *testcase.T, stub *doubles2.TB) { t.Cleanup(func() { t.Must.Contain(stub.Logs.String(), `fatalf -`) }) @@ -180,7 +180,7 @@ func TestRecorderTB(t *testing.T) { assert.Must(t).True(subject(t)) }) - thenUnderlyingTBWillExpect(s, func(t *testcase.T) { _ = subject(t) }, func(t *testcase.T, stub *testcase.StubTB) { + thenUnderlyingTBWillExpect(s, func(t *testcase.T) { _ = subject(t) }, func(t *testcase.T, stub *doubles2.TB) { t.Cleanup(func() { t.Must.False(stub.Failed(), "expect that IsFailed don't affect the testing.TB") }) @@ -194,7 +194,7 @@ func TestRecorderTB(t *testing.T) { assert.Must(t).False(subject(t)) }) - thenUnderlyingTBWillExpect(s, func(t *testcase.T) { _ = subject(t) }, func(t *testcase.T, stub *testcase.StubTB) { + thenUnderlyingTBWillExpect(s, func(t *testcase.T) { _ = subject(t) }, func(t *testcase.T, stub *doubles2.TB) { t.Cleanup(func() { t.Must.False(stub.Failed()) }) @@ -605,9 +605,9 @@ func TestRecorderTB(t *testing.T) { func TestRecorderTB_implementsCustomTB(t *testing.T) { testcase.RunSuite(t, contracts.CustomTB{ Subject: func(t *testcase.T) testcase.TBRunner { - stub := &testcase.StubTB{} + stub := &doubles2.TB{} t.Defer(stub.Finish) - rtb := &internal.RecorderTB{TB: stub} + rtb := &doubles2.RecorderTB{TB: stub} rtb.Config.Passthrough = true return rtb }, @@ -616,8 +616,8 @@ func TestRecorderTB_implementsCustomTB(t *testing.T) { func TestRecorderTB_Record_ConcurrentAccess(t *testing.T) { var ( - stub = &testcase.StubTB{} - rtb = &internal.RecorderTB{TB: stub} + stub = &doubles2.TB{} + rtb = &doubles2.RecorderTB{TB: stub} ) var wg sync.WaitGroup diff --git a/StubTB.go b/internal/doubles/TB.go similarity index 60% rename from StubTB.go rename to internal/doubles/TB.go index ef5313f..dd2428f 100644 --- a/StubTB.go +++ b/internal/doubles/TB.go @@ -1,4 +1,4 @@ -package testcase +package doubles import ( "bytes" @@ -9,10 +9,10 @@ import ( "testing" "time" - "github.com/adamluzsi/testcase/internal" + "github.com/adamluzsi/testcase/internal/teardown" ) -type StubTB struct { +type TB struct { // TB is an optional value here. // If provided, some default behaviour might be taken from it, like TempDir. // @@ -26,94 +26,94 @@ type StubTB struct { StubName string StubTempDir string - td internal.Teardown + td teardown.Teardown mutex sync.Mutex } -func (m *StubTB) Finish() { +func (m *TB) Finish() { m.td.Finish() } -func (m *StubTB) Cleanup(f func()) { +func (m *TB) Cleanup(f func()) { m.td.Defer(f) } -func (m *StubTB) Error(args ...any) { +func (m *TB) Error(args ...any) { m.appendLogs(fmt.Sprintln(args...)) m.Fail() } -func (m *StubTB) Errorf(format string, args ...any) { +func (m *TB) Errorf(format string, args ...any) { m.appendLogs(fmt.Sprintf(format+"\n", args...)) m.Fail() } -func (m *StubTB) Fail() { +func (m *TB) Fail() { m.IsFailed = true } -func (m *StubTB) FailNow() { +func (m *TB) FailNow() { m.Fail() runtime.Goexit() } -func (m *StubTB) Failed() bool { +func (m *TB) Failed() bool { return m.IsFailed } -func (m *StubTB) Fatal(args ...any) { +func (m *TB) Fatal(args ...any) { m.appendLogs(fmt.Sprintln(args...)) m.FailNow() } -func (m *StubTB) Fatalf(format string, args ...any) { +func (m *TB) Fatalf(format string, args ...any) { m.appendLogs(fmt.Sprintf(format+"\n", args...)) m.FailNow() } -func (m *StubTB) Helper() {} +func (m *TB) Helper() {} -func (m *StubTB) appendLogs(msg string) { +func (m *TB) appendLogs(msg string) { m.mutex.Lock() defer m.mutex.Unlock() _, _ = fmt.Fprint(&m.Logs, msg) } -func (m *StubTB) Log(args ...any) { +func (m *TB) Log(args ...any) { m.appendLogs(fmt.Sprintln(args...)) } -func (m *StubTB) Logf(format string, args ...any) { +func (m *TB) Logf(format string, args ...any) { m.appendLogs(fmt.Sprintf(format+"\n", args...)) } -func (m *StubTB) Name() string { +func (m *TB) Name() string { if m.StubName == "" { m.StubName = fmt.Sprintf("%d", time.Now().UnixNano()) } return m.StubName } -func (m *StubTB) Skip(args ...any) { +func (m *TB) Skip(args ...any) { m.Log(args...) m.SkipNow() } -func (m *StubTB) SkipNow() { +func (m *TB) SkipNow() { m.IsSkipped = true runtime.Goexit() } -func (m *StubTB) Skipf(format string, args ...any) { +func (m *TB) Skipf(format string, args ...any) { m.Logf(format, args...) m.SkipNow() } -func (m *StubTB) Skipped() bool { +func (m *TB) Skipped() bool { return m.IsSkipped } -func (m *StubTB) TempDir() string { +func (m *TB) TempDir() string { if m.StubTempDir != "" { return m.StubTempDir } diff --git a/StubTB_test.go b/internal/doubles/TB_test.go similarity index 97% rename from StubTB_test.go rename to internal/doubles/TB_test.go index d0b6cb3..3f4c097 100644 --- a/StubTB_test.go +++ b/internal/doubles/TB_test.go @@ -1,4 +1,4 @@ -package testcase_test +package doubles_test import ( "os" @@ -9,13 +9,14 @@ import ( "github.com/adamluzsi/testcase/assert" "github.com/adamluzsi/testcase/contracts" "github.com/adamluzsi/testcase/internal" + "github.com/adamluzsi/testcase/internal/doubles" ) func TestStubTB(t *testing.T) { s := testcase.NewSpec(t) - var stub = testcase.Let(s, func(t *testcase.T) *testcase.StubTB { - return &testcase.StubTB{} + var stub = testcase.Let(s, func(t *testcase.T) *doubles.TB { + return &doubles.TB{} }) s.Test(`.Cleanup + .Finish`, func(t *testcase.T) { @@ -227,7 +228,7 @@ func TestStubTB(t *testing.T) { func TestStubTB_implementsTestingTB(t *testing.T) { testcase.RunSuite(t, contracts.TestingTB{ Subject: func(t *testcase.T) testing.TB { - stb := &testcase.StubTB{} + stb := &doubles.TB{} t.Cleanup(stb.Finish) return stb }, diff --git a/internal/offset_helper_test.go b/internal/offset_helper_test.go deleted file mode 100644 index 4d30f6e..0000000 --- a/internal/offset_helper_test.go +++ /dev/null @@ -1,5 +0,0 @@ -package internal_test - -import "github.com/adamluzsi/testcase/internal" - -func offsetHelper(td *internal.Teardown, fn interface{}, args ...interface{}) { td.Defer(fn, args...) } diff --git a/internal/Teardown.go b/internal/teardown/Teardown.go similarity index 90% rename from internal/Teardown.go rename to internal/teardown/Teardown.go index 1ee825d..705b30d 100644 --- a/internal/Teardown.go +++ b/internal/teardown/Teardown.go @@ -1,10 +1,12 @@ -package internal +package teardown import ( "fmt" "reflect" "runtime" "sync" + + "github.com/adamluzsi/testcase/sandbox" ) type Teardown struct { @@ -123,7 +125,18 @@ func (td *Teardown) isEmpty() bool { func (td *Teardown) add(fn func()) { td.mutex.Lock() defer td.mutex.Unlock() - td.fns = append(td.fns, func() { RecoverGoexit(fn) }) + td.fns = append(td.fns, func() { td.recoverGoexit(fn) }) +} + +func (td *Teardown) recoverGoexit(fn func()) sandbox.RunOutcome { + runOutcome := sandbox.Run(fn) + if runOutcome.Goexit { // ignore goexit + return runOutcome + } + if !runOutcome.OK { // propagate panic + panic(runOutcome.PanicValue) + } + return runOutcome } func (td *Teardown) run() { diff --git a/internal/Teardown_test.go b/internal/teardown/Teardown_test.go similarity index 92% rename from internal/Teardown_test.go rename to internal/teardown/Teardown_test.go index b93c51f..f0bbe12 100644 --- a/internal/Teardown_test.go +++ b/internal/teardown/Teardown_test.go @@ -1,4 +1,4 @@ -package internal_test +package teardown_test import ( "context" @@ -9,10 +9,11 @@ import ( "github.com/adamluzsi/testcase" "github.com/adamluzsi/testcase/assert" "github.com/adamluzsi/testcase/internal" + "github.com/adamluzsi/testcase/internal/teardown" ) func TestTeardown_Defer_order(t *testing.T) { - td := &internal.Teardown{} + td := &teardown.Teardown{} var res []int td.Defer(func() { res = append(res, 3) }) td.Defer(func() { res = append(res, 2) }) @@ -24,7 +25,7 @@ func TestTeardown_Defer_order(t *testing.T) { } func TestTeardown_Defer_commonFunctionSignatures(t *testing.T) { - td := &internal.Teardown{} + td := &teardown.Teardown{} var res []int td.Defer(func() error { res = append(res, 1); return nil }) td.Defer(func() { res = append(res, 0) }) @@ -57,7 +58,7 @@ func TestTeardown_Defer_ignoresGoExit(t *testing.T) { var a, b, c bool internal.RecoverGoexit(func() { - td := &internal.Teardown{} + td := &teardown.Teardown{} defer td.Finish() td.Defer(func() { a = true @@ -82,7 +83,7 @@ func TestTeardown_Defer_panic(t *testing.T) { var a, b, c bool const expectedPanicMessage = `boom` - td := &internal.Teardown{} + td := &teardown.Teardown{} td.Defer(func() { a = true }) td.Defer(func() { b = true; panic(expectedPanicMessage) }) td.Defer(func() { c = true }) @@ -101,7 +102,7 @@ func TestTeardown_Defer_panic(t *testing.T) { func TestTeardown_Defer_withinCleanup(t *testing.T) { var a, b, c bool - td := &internal.Teardown{} + td := &teardown.Teardown{} td.Defer(func() { a = true td.Defer(func() { @@ -119,7 +120,7 @@ func TestTeardown_Defer_withinCleanup(t *testing.T) { } func TestTeardown_Defer_args(t *testing.T) { - td := &internal.Teardown{} + td := &teardown.Teardown{} t.Run(`arg is primitive type`, func(t *testing.T) { fn := func(_ int) {} @@ -149,7 +150,7 @@ func TestTeardown_Defer_args(t *testing.T) { }) t.Run(`pass by value`, func(t *testing.T) { - td := &internal.Teardown{} + td := &teardown.Teardown{} v := 42 var out int td.Defer(func(n int) { out = n }, v) @@ -192,7 +193,7 @@ func TestTeardown_Defer_withVariadicArgument_argumentPassed(t *testing.T) { func TestT_Defer_withArgumentsButArgumentCountMismatch(t *testing.T) { var subject = func() { - td := &internal.Teardown{} + td := &teardown.Teardown{} td.Defer(func(text string) {}, `this would be ok`, `but this extra argument is not ok`) } @@ -211,7 +212,7 @@ func TestT_Defer_withArgumentsButArgumentCountMismatch(t *testing.T) { type notContextForSure struct{} var fn = func(ctx context.Context) {} var subject = func(ctx interface{}) { - td := &internal.Teardown{} + td := &teardown.Teardown{} td.Defer(fn, ctx) } assert.Must(t).Panic(func() { subject(notContextForSure{}) }) @@ -235,7 +236,7 @@ func TestTeardown_Defer_runtimeGoexit(t *testing.T) { internal.RecoverGoexit(func() { var ran bool defer func() { assert.Must(t).True(ran) }() - td := &internal.Teardown{} + td := &teardown.Teardown{} td.Defer(func() { ran = true }) td.Defer(func() { runtime.Goexit() }) td.Finish() @@ -246,16 +247,16 @@ func TestTeardown_Defer_runtimeGoexit(t *testing.T) { func TestTeardown_Defer_CallerOffset(t *testing.T) { var subject = func(offset int) string { - td := &internal.Teardown{CallerOffset: offset} + td := &teardown.Teardown{CallerOffset: offset} return getPanicMessage(t, func() { offsetHelper(td, func(int) {}, "42") }) } - assert.Must(t).Contain(subject(0), `offset_helper_test.go:5`) + assert.Must(t).Contain(subject(0), `offset_helper_test.go:7`) assert.Must(t).Contain(subject(1), `Teardown_test.go`) } func TestTeardown_Defer_isThreadSafe(t *testing.T) { var ( - td = &internal.Teardown{} + td = &teardown.Teardown{} out = &sync.Map{} sampling = runtime.NumCPU() * 42 @@ -290,7 +291,7 @@ func TestTeardown_Defer_isThreadSafe(t *testing.T) { func TestTeardown_Finish_idempotent(t *testing.T) { var count int - td := &internal.Teardown{} + td := &teardown.Teardown{} td.Defer(func() { count++ }) td.Finish() td.Finish() diff --git a/internal/teardown/offset_helper_test.go b/internal/teardown/offset_helper_test.go new file mode 100644 index 0000000..c188639 --- /dev/null +++ b/internal/teardown/offset_helper_test.go @@ -0,0 +1,7 @@ +package teardown_test + +import ( + "github.com/adamluzsi/testcase/internal/teardown" +) + +func offsetHelper(td *teardown.Teardown, fn interface{}, args ...interface{}) { td.Defer(fn, args...) } diff --git a/let_test.go b/let_test.go index 783d51e..0bec479 100644 --- a/let_test.go +++ b/let_test.go @@ -9,6 +9,7 @@ import ( "github.com/adamluzsi/testcase/assert" "github.com/adamluzsi/testcase/httpspec" "github.com/adamluzsi/testcase/internal/caller" + "github.com/adamluzsi/testcase/internal/doubles" "github.com/adamluzsi/testcase/sandbox" ) @@ -102,7 +103,7 @@ func TestLet_posName(t *testing.T) { func TestLet_withNilBlock(tt *testing.T) { it := assert.MakeIt(tt) - stub := &testcase.StubTB{} + stub := &doubles.TB{} defer stub.Finish() s := testcase.NewSpec(stub) v := testcase.Let[int](s, nil) @@ -121,7 +122,7 @@ func TestLet_withNilBlock(tt *testing.T) { func TestLetValue_withNil(tt *testing.T) { it := assert.MakeIt(tt) - stub := &testcase.StubTB{} + stub := &doubles.TB{} defer stub.Finish() s := testcase.NewSpec(stub) v := testcase.Let[[]int](s, nil) diff --git a/spec_helper_test.go b/spec_helper_test.go index c0c9f69..379a0fe 100644 --- a/spec_helper_test.go +++ b/spec_helper_test.go @@ -4,8 +4,8 @@ import ( "bytes" "testing" - "github.com/adamluzsi/testcase" "github.com/adamluzsi/testcase/internal" + "github.com/adamluzsi/testcase/internal/doubles" "github.com/adamluzsi/testcase/assert" ) @@ -35,7 +35,7 @@ func unsupported(tb testing.TB) { tb.Skip(`unsupported`) } -func isFatalFn(stub *testcase.StubTB) func(block func()) bool { +func isFatalFn(stub *doubles.TB) func(block func()) bool { return func(block func()) bool { stub.IsFailed = false defer func() { stub.IsFailed = false }() @@ -48,7 +48,7 @@ func isFatalFn(stub *testcase.StubTB) func(block func()) bool { } } -func willFatalWithMessageFn(stub *testcase.StubTB) func(tb testing.TB, blk func()) string { +func willFatalWithMessageFn(stub *doubles.TB) func(tb testing.TB, blk func()) string { isFatal := isFatalFn(stub) return func(tb testing.TB, blk func()) string { stub.Logs = bytes.Buffer{}