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  }