github.com/xmidt-org/webpa-common@v1.11.9/concurrent/runnable_test.go (about) 1 package concurrent 2 3 import ( 4 "errors" 5 "os" 6 "sync" 7 "sync/atomic" 8 "testing" 9 "time" 10 ) 11 12 // success returns a closure that simulates a successfully started task 13 func success(t *testing.T, runCount *uint32) Runnable { 14 return RunnableFunc(func(waitGroup *sync.WaitGroup, shutdown <-chan struct{}) error { 15 atomic.AddUint32(runCount, 1) 16 waitGroup.Add(1) 17 18 // simulates some longrunning task ... 19 go func() { 20 defer waitGroup.Done() 21 <-shutdown 22 }() 23 24 return nil 25 }) 26 } 27 28 // fail returns a closure that simulates a task that failed to start 29 func fail(t *testing.T, runCount *uint32) Runnable { 30 return RunnableFunc(func(waitGroup *sync.WaitGroup, shutdown <-chan struct{}) error { 31 atomic.AddUint32(runCount, 1) 32 return errors.New("Expected error") 33 }) 34 } 35 36 func TestRunnableSetRun(t *testing.T) { 37 var actualRunCount uint32 38 success := success(t, &actualRunCount) 39 fail := fail(t, &actualRunCount) 40 41 var testData = []struct { 42 runnable RunnableSet 43 expectedRunCount uint32 44 }{ 45 {nil, 0}, 46 {RunnableSet{}, 0}, 47 {RunnableSet{success}, 1}, 48 {RunnableSet{fail}, 1}, 49 {RunnableSet{success, success}, 2}, 50 {RunnableSet{success, fail}, 2}, 51 {RunnableSet{success, fail, success}, 2}, 52 {RunnableSet{success, fail, fail}, 2}, 53 {RunnableSet{success, fail, success}, 2}, 54 {RunnableSet{success, success, fail, success, success, fail}, 3}, 55 {RunnableSet{success, success, success, success, fail}, 5}, 56 {RunnableSet{success, success, success, success, success}, 5}, 57 } 58 59 for _, record := range testData { 60 actualRunCount = 0 61 waitGroup := &sync.WaitGroup{} 62 shutdown := make(chan struct{}) 63 record.runnable.Run(waitGroup, shutdown) 64 close(shutdown) 65 66 if !WaitTimeout(waitGroup, time.Second*2) { 67 t.Errorf("Blocked on WaitGroup longer than the timeout") 68 } 69 70 if record.expectedRunCount != actualRunCount { 71 t.Errorf( 72 "Expected Run to be called %d time(s), but instead was called %d time(s)", 73 record.expectedRunCount, 74 actualRunCount, 75 ) 76 } 77 } 78 } 79 80 func TestExecuteSuccess(t *testing.T) { 81 var actualRunCount uint32 82 success := success(t, &actualRunCount) 83 waitGroup, shutdown, err := Execute(success) 84 if actualRunCount != 1 { 85 t.Error("Execute() did not invoke Run()") 86 } 87 88 if err != nil { 89 t.Fatalf("Execute() failed: %v", err) 90 } 91 92 if waitGroup == nil { 93 t.Fatal("Execute() returned a nil WaitGroup") 94 } 95 96 if shutdown == nil { 97 t.Fatal("Execute() returned a nil shutdown channel") 98 } 99 100 close(shutdown) 101 102 if !WaitTimeout(waitGroup, time.Second*2) { 103 t.Errorf("Blocked on WaitGroup longer than the timeout") 104 } 105 } 106 107 func TestExecuteFail(t *testing.T) { 108 var actualRunCount uint32 109 fail := fail(t, &actualRunCount) 110 waitGroup, shutdown, err := Execute(fail) 111 if actualRunCount != 1 { 112 t.Error("Execute() did not invoke Run()") 113 } 114 115 if err == nil { 116 t.Error("Execute() should have returned an error") 117 } 118 119 if waitGroup == nil { 120 t.Fatal("Execute() returned a nil WaitGroup") 121 } 122 123 if shutdown == nil { 124 t.Fatal("Execute() returned a nil shutdown channel") 125 } 126 127 close(shutdown) 128 129 if !WaitTimeout(waitGroup, time.Second*2) { 130 t.Errorf("Blocked on WaitGroup longer than the timeout") 131 } 132 } 133 134 func TestAwaitSuccess(t *testing.T) { 135 testWaitGroup := &sync.WaitGroup{} 136 testWaitGroup.Add(1) 137 success := RunnableFunc(func(waitGroup *sync.WaitGroup, shutdown <-chan struct{}) error { 138 waitGroup.Add(1) 139 go func() { 140 defer waitGroup.Done() 141 defer testWaitGroup.Done() 142 <-shutdown 143 }() 144 145 return nil 146 }) 147 148 signals := make(chan os.Signal, 1) 149 go func() { 150 Await(success, signals) 151 }() 152 153 // simulate a ctrl+c 154 signals <- os.Interrupt 155 156 if !WaitTimeout(testWaitGroup, time.Second*2) { 157 t.Errorf("Blocked on WaitGroup longer than the timeout") 158 } 159 } 160 161 func TestAwaitFail(t *testing.T) { 162 testWaitGroup := &sync.WaitGroup{} 163 testWaitGroup.Add(1) 164 fail := RunnableFunc(func(waitGroup *sync.WaitGroup, shutdown <-chan struct{}) error { 165 defer testWaitGroup.Done() 166 return errors.New("Expected error") 167 }) 168 169 signals := make(chan os.Signal, 1) 170 go func() { 171 Await(fail, signals) 172 }() 173 174 // simulate a ctrl+c 175 signals <- os.Interrupt 176 177 if !WaitTimeout(testWaitGroup, time.Second*2) { 178 t.Errorf("Blocked on WaitGroup longer than the timeout") 179 } 180 }