| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338 |
- package common
- import (
- "context"
- "errors"
- "fmt"
- "testing"
- "time"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- "github.com/prometheus/client_golang/prometheus"
- dto "github.com/prometheus/client_model/go"
- )
- func getCounterValue(metric *prometheus.CounterVec, runnableName string) float64 {
- var m = &dto.Metric{}
- if err := metric.WithLabelValues(runnableName).Write(m); err != nil {
- return 0
- }
- return m.Counter.GetValue()
- }
- func throwNil(ctx context.Context) error {
- var x *int = nil
- //nolint:govet // This test function is specifically checking for nil errors, so being flagged for `nilness` is expected
- *x = 5
- return nil
- }
- func runTest(t *testing.T, ctx context.Context, testCase int) (result error) {
- t.Helper()
- defer func() {
- if r := recover(); r != nil {
- switch x := r.(type) {
- case string:
- result = errors.New(x)
- case error:
- result = x
- default:
- result = fmt.Errorf("unknown panic in runTest/%d", testCase)
- }
- }
- }()
- errC := make(chan error)
- switch testCase {
- case 0:
- _ = throwNil(ctx) // fall into defer above
- case 1:
- RunWithScissors(ctx, errC, "test1Thread", throwNil)
- case 2:
- RunWithScissors(ctx, errC, "test2Thread", func(ctx context.Context) error {
- <-ctx.Done()
- return nil
- })
- _ = throwNil(ctx)
- case 3:
- go func() { _ = throwNil(ctx) }() // uncatchable
- }
- select {
- case <-ctx.Done():
- return ctx.Err()
- case err := <-errC:
- return err
- }
- }
- func TestSupervisor(t *testing.T) {
- for i := 0; i < 3; i++ {
- t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
- rootCtx := context.Background()
- ctx, fn := context.WithCancel(rootCtx)
- err := runTest(t, ctx, i)
- switch i {
- case 0:
- assert.EqualError(t, err, "runtime error: invalid memory address or nil pointer dereference")
- case 1:
- assert.EqualError(t, err, "test1Thread: runtime error: invalid memory address or nil pointer dereference")
- case 2:
- assert.EqualError(t, err, "runtime error: invalid memory address or nil pointer dereference")
- }
- fn()
- },
- )
- }
- }
- func TestRunWithScissorsCleanExit(t *testing.T) {
- ctx := context.Background()
- errC := make(chan error)
- itRan := make(chan bool, 1)
- RunWithScissors(ctx, errC, "TestRunWithScissorsCleanExit", func(ctx context.Context) error {
- itRan <- true
- return nil
- })
- shouldHaveRun := <-itRan
- require.Equal(t, true, shouldHaveRun)
- // Need to wait a bit to make sure the scissors code completes without hanging.
- time.Sleep(100 * time.Millisecond)
- assert.Equal(t, 0.0, getCounterValue(ScissorsErrorsCaught, "TestRunWithScissorsCleanExit"))
- assert.Equal(t, 0.0, getCounterValue(ScissorsPanicsCaught, "TestRunWithScissorsCleanExit"))
- }
- func TestRunWithScissorsPanicReturned(t *testing.T) {
- ctx := context.Background()
- errC := make(chan error)
- itRan := make(chan bool, 1)
- RunWithScissors(ctx, errC, "TestRunWithScissorsPanicReturned", func(ctx context.Context) error {
- itRan <- true
- panic("Some random panic")
- })
- var err error
- select {
- case <-ctx.Done():
- break
- case err = <-errC:
- break
- }
- shouldHaveRun := <-itRan
- require.Equal(t, true, shouldHaveRun)
- assert.Error(t, err)
- assert.Equal(t, "TestRunWithScissorsPanicReturned: Some random panic", err.Error())
- assert.Equal(t, 0.0, getCounterValue(ScissorsErrorsCaught, "TestRunWithScissorsPanicReturned"))
- assert.Equal(t, 1.0, getCounterValue(ScissorsPanicsCaught, "TestRunWithScissorsPanicReturned"))
- }
- func TestRunWithScissorsPanicDoesNotBlockWhenNoListener(t *testing.T) {
- ctx := context.Background()
- errC := make(chan error)
- itRan := make(chan bool, 1)
- RunWithScissors(ctx, errC, "TestRunWithScissorsPanicDoesNotBlockWhenNoListener", func(ctx context.Context) error {
- itRan <- true
- panic("Some random panic")
- })
- shouldHaveRun := <-itRan
- require.Equal(t, true, shouldHaveRun)
- // Need to wait a bit to make sure the scissors code completes without hanging.
- time.Sleep(100 * time.Millisecond)
- assert.Equal(t, 0.0, getCounterValue(ScissorsErrorsCaught, "TestRunWithScissorsPanicDoesNotBlockWhenNoListener"))
- assert.Equal(t, 1.0, getCounterValue(ScissorsPanicsCaught, "TestRunWithScissorsPanicDoesNotBlockWhenNoListener"))
- }
- func TestRunWithScissorsErrorReturned(t *testing.T) {
- ctx := context.Background()
- errC := make(chan error)
- itRan := make(chan bool, 1)
- RunWithScissors(ctx, errC, "TestRunWithScissorsErrorReturned", func(ctx context.Context) error {
- itRan <- true
- return fmt.Errorf("Some random error")
- })
- var err error
- select {
- case <-ctx.Done():
- break
- case err = <-errC:
- break
- }
- shouldHaveRun := <-itRan
- require.Equal(t, true, shouldHaveRun)
- assert.Error(t, err)
- assert.Equal(t, "Some random error", err.Error())
- assert.Equal(t, 1.0, getCounterValue(ScissorsErrorsCaught, "TestRunWithScissorsErrorReturned"))
- assert.Equal(t, 0.0, getCounterValue(ScissorsPanicsCaught, "TestRunWithScissorsErrorReturned"))
- }
- func TestRunWithScissorsErrorDoesNotBlockWhenNoListener(t *testing.T) {
- ctx := context.Background()
- errC := make(chan error)
- itRan := make(chan bool, 1)
- RunWithScissors(ctx, errC, "TestRunWithScissorsErrorDoesNotBlockWhenNoListener", func(ctx context.Context) error {
- itRan <- true
- return fmt.Errorf("Some random error")
- })
- shouldHaveRun := <-itRan
- require.Equal(t, true, shouldHaveRun)
- // Need to wait a bit to make sure the scissors code completes without hanging.
- time.Sleep(100 * time.Millisecond)
- assert.Equal(t, 1.0, getCounterValue(ScissorsErrorsCaught, "TestRunWithScissorsErrorDoesNotBlockWhenNoListener"))
- assert.Equal(t, 0.0, getCounterValue(ScissorsPanicsCaught, "TestRunWithScissorsErrorDoesNotBlockWhenNoListener"))
- }
- func TestStartRunnable_CleanExit(t *testing.T) {
- ctx := context.Background()
- errC := make(chan error)
- itRan := make(chan bool, 1)
- StartRunnable(ctx, errC, true, "TestStartRunnable_CleanExit", func(ctx context.Context) error {
- itRan <- true
- return nil
- })
- shouldHaveRun := <-itRan
- require.Equal(t, true, shouldHaveRun)
- // Need to wait a bit to make sure the scissors code completes without hanging.
- time.Sleep(100 * time.Millisecond)
- assert.Equal(t, 0.0, getCounterValue(ScissorsErrorsCaught, "TestStartRunnable_CleanExit"))
- assert.Equal(t, 0.0, getCounterValue(ScissorsPanicsCaught, "TestStartRunnable_CleanExit"))
- }
- func TestStartRunnable_OnError(t *testing.T) {
- ctx := context.Background()
- errC := make(chan error)
- itRan := make(chan bool, 1)
- StartRunnable(ctx, errC, true, "TestStartRunnable_OnError", func(ctx context.Context) error {
- itRan <- true
- return fmt.Errorf("Some random error")
- })
- var err error
- select {
- case <-ctx.Done():
- break
- case err = <-errC:
- break
- }
- shouldHaveRun := <-itRan
- require.Equal(t, true, shouldHaveRun)
- assert.Error(t, err)
- assert.Equal(t, "Some random error", err.Error())
- assert.Equal(t, 1.0, getCounterValue(ScissorsErrorsCaught, "TestStartRunnable_OnError"))
- assert.Equal(t, 0.0, getCounterValue(ScissorsPanicsCaught, "TestStartRunnable_OnError"))
- }
- func TestStartRunnable_DontCatchPanics_OnPanic(t *testing.T) {
- ctx := context.Background()
- errC := make(chan error)
- itRan := make(chan bool, 1)
- itPanicked := make(chan bool, 1)
- // We can't use StartRunnable() because we cannot test for a panic in another go routine.
- // This verifies that startRunnable() lets the panic through so it gets caught here, allowing us to test for it.
- func() {
- defer func() {
- if r := recover(); r != nil {
- itPanicked <- true
- }
- itRan <- true
- }()
- startRunnable(ctx, errC, "TestStartRunnable_DontCatchPanics_OnPanic", func(ctx context.Context) error {
- panic("Some random panic")
- })
- }()
- var shouldHaveRun bool
- select {
- case <-ctx.Done():
- break
- case shouldHaveRun = <-itRan:
- break
- }
- require.Equal(t, true, shouldHaveRun)
- require.Equal(t, 1, len(itPanicked))
- shouldHavePanicked := <-itPanicked
- require.Equal(t, true, shouldHavePanicked)
- assert.Equal(t, 0.0, getCounterValue(ScissorsErrorsCaught, "TestStartRunnable_DontCatchPanics_OnPanic"))
- assert.Equal(t, 0.0, getCounterValue(ScissorsPanicsCaught, "TestStartRunnable_DontCatchPanics_OnPanic"))
- }
- func TestStartRunnable_CatchPanics_OnPanic(t *testing.T) {
- ctx := context.Background()
- errC := make(chan error)
- itRan := make(chan bool, 1)
- StartRunnable(ctx, errC, true, "TestStartRunnable_CatchPanics_OnPanic", func(ctx context.Context) error {
- itRan <- true
- panic("Some random panic")
- })
- var err error
- select {
- case <-ctx.Done():
- break
- case err = <-errC:
- break
- }
- shouldHaveRun := <-itRan
- require.Equal(t, true, shouldHaveRun)
- assert.Error(t, err)
- assert.Equal(t, "TestStartRunnable_CatchPanics_OnPanic: Some random panic", err.Error())
- assert.Equal(t, 0.0, getCounterValue(ScissorsErrorsCaught, "TestStartRunnable_CatchPanics_OnPanic"))
- assert.Equal(t, 1.0, getCounterValue(ScissorsPanicsCaught, "TestStartRunnable_CatchPanics_OnPanic"))
- }
- func TestStartRunnable_DoesNotBlockWhenNoListener(t *testing.T) {
- ctx := context.Background()
- errC := make(chan error)
- itRan := make(chan bool, 1)
- StartRunnable(ctx, errC, true, "TestStartRunnable_DoesNotBlockWhenNoListener", func(ctx context.Context) error {
- itRan <- true
- panic("Some random panic")
- })
- shouldHaveRun := <-itRan
- require.Equal(t, true, shouldHaveRun)
- // Need to wait a bit to make sure the scissors code completes without hanging.
- time.Sleep(100 * time.Millisecond)
- assert.Equal(t, 0.0, getCounterValue(ScissorsErrorsCaught, "TestStartRunnable_DoesNotBlockWhenNoListener"))
- assert.Equal(t, 1.0, getCounterValue(ScissorsPanicsCaught, "TestStartRunnable_DoesNotBlockWhenNoListener"))
- }
|