github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/module/lifecycle/lifecycle_test.go (about) 1 package lifecycle_test 2 3 import ( 4 "sync/atomic" 5 "testing" 6 "time" 7 8 "github.com/stretchr/testify/suite" 9 10 "github.com/onflow/flow-go/module/lifecycle" 11 "github.com/onflow/flow-go/utils/unittest" 12 ) 13 14 type LifecycleManagerSuite struct { 15 suite.Suite 16 lm *lifecycle.LifecycleManager 17 } 18 19 func (suite *LifecycleManagerSuite) SetupTest() { 20 suite.lm = lifecycle.NewLifecycleManager() 21 } 22 23 // TestConcurrentStart tests that calling OnStart multiple times concurrently only 24 // results in startup being performed once 25 func (suite *LifecycleManagerSuite) TestConcurrentStart() { 26 var numStarts uint32 27 28 lm := suite.lm 29 for i := 0; i < 10; i++ { 30 go func() { 31 lm.OnStart(func() { 32 atomic.AddUint32(&numStarts, 1) 33 }) 34 }() 35 } 36 37 unittest.RequireCloseBefore(suite.T(), suite.lm.Started(), time.Second, "timed out waiting for startup") 38 suite.Assert().EqualValues(1, numStarts) 39 suite.Assert().Neverf(func() bool { return numStarts != 1 }, 100*time.Millisecond, 10*time.Millisecond, "lifecycle manager started more than once") 40 } 41 42 // TestConcurrentStop tests that calling OnStop multiple times concurrently only 43 // results in shutdown being performed once 44 func (suite *LifecycleManagerSuite) TestConcurrentStop() { 45 suite.lm.OnStart() 46 unittest.RequireCloseBefore(suite.T(), suite.lm.Started(), time.Second, "timed out waiting for startup") 47 48 var numStops uint32 49 50 lm := suite.lm 51 for i := 0; i < 10; i++ { 52 go func() { 53 lm.OnStop(func() { 54 atomic.AddUint32(&numStops, 1) 55 }) 56 }() 57 } 58 59 unittest.RequireCloseBefore(suite.T(), suite.lm.Stopped(), time.Second, "timed out waiting for shutdown") 60 suite.Assert().EqualValues(1, numStops) 61 suite.Assert().Neverf(func() bool { return numStops != 1 }, 100*time.Millisecond, 10*time.Millisecond, "lifecycle manager stopped more than once") 62 } 63 64 // TestStopBeforeStart tests that calling OnStop before OnStart results in startup never 65 // being performed, and the returned channel never closing. 66 func (suite *LifecycleManagerSuite) TestStopBeforeStart() { 67 suite.lm.OnStop(func() { 68 suite.FailNow("shutdown should not occur") 69 }) 70 71 suite.lm.OnStart(func() { 72 suite.FailNow("startup should not occur") 73 }) 74 75 unittest.RequireCloseBefore(suite.T(), suite.lm.Stopped(), time.Second, "timed out waiting for shutdown") 76 unittest.RequireNotClosed(suite.T(), suite.lm.Started(), "Started channel should never close") 77 } 78 79 // TestStopAfterStart tests that if OnStop is called right after OnStart, shutdown will 80 // only be performed after startup has finished. 81 func (suite *LifecycleManagerSuite) TestStopAfterStart() { 82 var started uint32 = 0 83 84 suite.lm.OnStart(func() { 85 // simulate startup processing 86 time.Sleep(100 * time.Millisecond) 87 atomic.StoreUint32(&started, 1) 88 }) 89 90 suite.lm.OnStop(func() { 91 suite.Assert().EqualValues(atomic.LoadUint32(&started), 1) 92 }) 93 94 unittest.RequireCloseBefore(suite.T(), suite.lm.Stopped(), time.Second, "timed out waiting for shutdown") 95 unittest.RequireClosed(suite.T(), suite.lm.Started(), "Started channel should be closed") 96 } 97 98 // TestHappyPath tests a normal start-stop lifecycle. 99 func (suite *LifecycleManagerSuite) TestHappyPath() { 100 suite.lm.OnStart() 101 102 unittest.AssertClosesBefore(suite.T(), suite.lm.Started(), time.Second) 103 unittest.RequireNotClosed(suite.T(), suite.lm.Stopped(), "Stopped channel should not close") 104 105 suite.lm.OnStop() 106 107 unittest.AssertClosesBefore(suite.T(), suite.lm.Stopped(), time.Second) 108 } 109 110 func TestLifecycleManager(t *testing.T) { 111 t.Parallel() 112 suite.Run(t, new(LifecycleManagerSuite)) 113 }