You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

137 lines
2.8 KiB

  1. package service
  2. import (
  3. "context"
  4. "sync"
  5. "testing"
  6. "time"
  7. "github.com/fortytw2/leaktest"
  8. "github.com/stretchr/testify/assert"
  9. "github.com/stretchr/testify/require"
  10. "github.com/tendermint/tendermint/libs/log"
  11. )
  12. type testService struct {
  13. started bool
  14. stopped bool
  15. multiStopped bool
  16. mu sync.Mutex
  17. BaseService
  18. }
  19. func (t *testService) OnStop() {
  20. t.mu.Lock()
  21. defer t.mu.Unlock()
  22. if t.stopped == true {
  23. t.multiStopped = true
  24. }
  25. t.stopped = true
  26. }
  27. func (t *testService) OnStart(context.Context) error {
  28. t.mu.Lock()
  29. defer t.mu.Unlock()
  30. t.started = true
  31. return nil
  32. }
  33. func (t *testService) isStarted() bool {
  34. t.mu.Lock()
  35. defer t.mu.Unlock()
  36. return t.started
  37. }
  38. func (t *testService) isStopped() bool {
  39. t.mu.Lock()
  40. defer t.mu.Unlock()
  41. return t.stopped
  42. }
  43. func (t *testService) isMultiStopped() bool {
  44. t.mu.Lock()
  45. defer t.mu.Unlock()
  46. return t.multiStopped
  47. }
  48. func TestBaseService(t *testing.T) {
  49. t.Cleanup(leaktest.Check(t))
  50. ctx, cancel := context.WithCancel(context.Background())
  51. defer cancel()
  52. logger := log.NewNopLogger()
  53. t.Run("Wait", func(t *testing.T) {
  54. wctx, wcancel := context.WithCancel(ctx)
  55. defer wcancel()
  56. ts := &testService{}
  57. ts.BaseService = *NewBaseService(logger, t.Name(), ts)
  58. err := ts.Start(wctx)
  59. require.NoError(t, err)
  60. require.True(t, ts.isStarted())
  61. waitFinished := make(chan struct{})
  62. wcancel()
  63. go func() {
  64. ts.Wait()
  65. close(waitFinished)
  66. }()
  67. select {
  68. case <-waitFinished:
  69. assert.True(t, ts.isStopped(), "failed to stop")
  70. assert.False(t, ts.IsRunning(), "is not running")
  71. case <-time.After(100 * time.Millisecond):
  72. t.Fatal("expected Wait() to finish within 100 ms.")
  73. }
  74. })
  75. t.Run("ManualStop", func(t *testing.T) {
  76. ts := &testService{}
  77. ts.BaseService = *NewBaseService(logger, t.Name(), ts)
  78. require.False(t, ts.IsRunning())
  79. require.False(t, ts.isStarted())
  80. require.NoError(t, ts.Start(ctx))
  81. require.True(t, ts.isStarted())
  82. ts.Stop()
  83. require.True(t, ts.isStopped())
  84. require.False(t, ts.IsRunning())
  85. })
  86. t.Run("MultiStop", func(t *testing.T) {
  87. t.Run("SingleThreaded", func(t *testing.T) {
  88. ts := &testService{}
  89. ts.BaseService = *NewBaseService(logger, t.Name(), ts)
  90. require.NoError(t, ts.Start(ctx))
  91. require.True(t, ts.isStarted())
  92. ts.Stop()
  93. require.True(t, ts.isStopped())
  94. require.False(t, ts.isMultiStopped())
  95. ts.Stop()
  96. require.False(t, ts.isMultiStopped())
  97. })
  98. t.Run("MultiThreaded", func(t *testing.T) {
  99. ctx, cancel := context.WithCancel(context.Background())
  100. defer cancel()
  101. ts := &testService{}
  102. ts.BaseService = *NewBaseService(logger, t.Name(), ts)
  103. require.NoError(t, ts.Start(ctx))
  104. require.True(t, ts.isStarted())
  105. go ts.Stop()
  106. go cancel()
  107. ts.Wait()
  108. require.True(t, ts.isStopped())
  109. require.False(t, ts.isMultiStopped())
  110. })
  111. })
  112. }