github.com/gitbundle/modules@v0.0.0-20231025071548-85b91c5c3b01/queue/queue_channel_test.go (about) 1 // Copyright 2023 The GitBundle Inc. All rights reserved. 2 // Copyright 2017 The Gitea Authors. All rights reserved. 3 // Use of this source code is governed by a MIT-style 4 // license that can be found in the LICENSE file. 5 6 package queue 7 8 import ( 9 "sync" 10 "testing" 11 "time" 12 13 "github.com/gitbundle/modules/log" 14 15 "github.com/stretchr/testify/assert" 16 ) 17 18 func TestChannelQueue(t *testing.T) { 19 handleChan := make(chan *testData) 20 handle := func(data ...Data) []Data { 21 for _, datum := range data { 22 testDatum := datum.(*testData) 23 handleChan <- testDatum 24 } 25 return nil 26 } 27 28 nilFn := func(_ func()) {} 29 30 queue, err := NewChannelQueue(handle, 31 ChannelQueueConfiguration{ 32 WorkerPoolConfiguration: WorkerPoolConfiguration{ 33 QueueLength: 0, 34 MaxWorkers: 10, 35 BlockTimeout: 1 * time.Second, 36 BoostTimeout: 5 * time.Minute, 37 BoostWorkers: 5, 38 Name: "TestChannelQueue", 39 }, 40 Workers: 0, 41 }, &testData{}) 42 assert.NoError(t, err) 43 44 assert.Equal(t, 5, queue.(*ChannelQueue).WorkerPool.boostWorkers) 45 46 go queue.Run(nilFn, nilFn) 47 48 test1 := testData{"A", 1} 49 go queue.Push(&test1) 50 result1 := <-handleChan 51 assert.Equal(t, test1.TestString, result1.TestString) 52 assert.Equal(t, test1.TestInt, result1.TestInt) 53 54 err = queue.Push(test1) 55 assert.Error(t, err) 56 } 57 58 func TestChannelQueue_Batch(t *testing.T) { 59 handleChan := make(chan *testData) 60 handle := func(data ...Data) []Data { 61 assert.True(t, len(data) == 2) 62 for _, datum := range data { 63 testDatum := datum.(*testData) 64 handleChan <- testDatum 65 } 66 return nil 67 } 68 69 nilFn := func(_ func()) {} 70 71 queue, err := NewChannelQueue(handle, 72 ChannelQueueConfiguration{ 73 WorkerPoolConfiguration: WorkerPoolConfiguration{ 74 QueueLength: 20, 75 BatchLength: 2, 76 BlockTimeout: 0, 77 BoostTimeout: 0, 78 BoostWorkers: 0, 79 MaxWorkers: 10, 80 }, 81 Workers: 1, 82 }, &testData{}) 83 assert.NoError(t, err) 84 85 go queue.Run(nilFn, nilFn) 86 87 test1 := testData{"A", 1} 88 test2 := testData{"B", 2} 89 90 queue.Push(&test1) 91 go queue.Push(&test2) 92 93 result1 := <-handleChan 94 assert.Equal(t, test1.TestString, result1.TestString) 95 assert.Equal(t, test1.TestInt, result1.TestInt) 96 97 result2 := <-handleChan 98 assert.Equal(t, test2.TestString, result2.TestString) 99 assert.Equal(t, test2.TestInt, result2.TestInt) 100 101 err = queue.Push(test1) 102 assert.Error(t, err) 103 } 104 105 func TestChannelQueue_Pause(t *testing.T) { 106 lock := sync.Mutex{} 107 var queue Queue 108 var err error 109 pushBack := false 110 handleChan := make(chan *testData) 111 handle := func(data ...Data) []Data { 112 lock.Lock() 113 if pushBack { 114 if pausable, ok := queue.(Pausable); ok { 115 pausable.Pause() 116 } 117 lock.Unlock() 118 return data 119 } 120 lock.Unlock() 121 122 for _, datum := range data { 123 testDatum := datum.(*testData) 124 handleChan <- testDatum 125 } 126 return nil 127 } 128 129 queueShutdown := []func(){} 130 queueTerminate := []func(){} 131 132 terminated := make(chan struct{}) 133 134 queue, err = NewChannelQueue(handle, 135 ChannelQueueConfiguration{ 136 WorkerPoolConfiguration: WorkerPoolConfiguration{ 137 QueueLength: 20, 138 BatchLength: 1, 139 BlockTimeout: 0, 140 BoostTimeout: 0, 141 BoostWorkers: 0, 142 MaxWorkers: 10, 143 }, 144 Workers: 1, 145 }, &testData{}) 146 assert.NoError(t, err) 147 148 go func() { 149 queue.Run(func(shutdown func()) { 150 lock.Lock() 151 defer lock.Unlock() 152 queueShutdown = append(queueShutdown, shutdown) 153 }, func(terminate func()) { 154 lock.Lock() 155 defer lock.Unlock() 156 queueTerminate = append(queueTerminate, terminate) 157 }) 158 close(terminated) 159 }() 160 161 // Shutdown and Terminate in defer 162 defer func() { 163 lock.Lock() 164 callbacks := make([]func(), len(queueShutdown)) 165 copy(callbacks, queueShutdown) 166 lock.Unlock() 167 for _, callback := range callbacks { 168 callback() 169 } 170 lock.Lock() 171 log.Info("Finally terminating") 172 callbacks = make([]func(), len(queueTerminate)) 173 copy(callbacks, queueTerminate) 174 lock.Unlock() 175 for _, callback := range callbacks { 176 callback() 177 } 178 }() 179 180 test1 := testData{"A", 1} 181 test2 := testData{"B", 2} 182 queue.Push(&test1) 183 184 pausable, ok := queue.(Pausable) 185 if !assert.True(t, ok) { 186 return 187 } 188 result1 := <-handleChan 189 assert.Equal(t, test1.TestString, result1.TestString) 190 assert.Equal(t, test1.TestInt, result1.TestInt) 191 192 pausable.Pause() 193 194 paused, _ := pausable.IsPausedIsResumed() 195 196 select { 197 case <-paused: 198 case <-time.After(100 * time.Millisecond): 199 assert.Fail(t, "Queue is not paused") 200 return 201 } 202 203 queue.Push(&test2) 204 205 var result2 *testData 206 select { 207 case result2 = <-handleChan: 208 assert.Fail(t, "handler chan should be empty") 209 case <-time.After(100 * time.Millisecond): 210 } 211 212 assert.Nil(t, result2) 213 214 pausable.Resume() 215 _, resumed := pausable.IsPausedIsResumed() 216 217 select { 218 case <-resumed: 219 case <-time.After(100 * time.Millisecond): 220 assert.Fail(t, "Queue should be resumed") 221 } 222 223 select { 224 case result2 = <-handleChan: 225 case <-time.After(500 * time.Millisecond): 226 assert.Fail(t, "handler chan should contain test2") 227 } 228 229 assert.Equal(t, test2.TestString, result2.TestString) 230 assert.Equal(t, test2.TestInt, result2.TestInt) 231 232 lock.Lock() 233 pushBack = true 234 lock.Unlock() 235 236 _, resumed = pausable.IsPausedIsResumed() 237 238 select { 239 case <-resumed: 240 case <-time.After(100 * time.Millisecond): 241 assert.Fail(t, "Queue is not resumed") 242 return 243 } 244 245 queue.Push(&test1) 246 paused, _ = pausable.IsPausedIsResumed() 247 248 select { 249 case <-paused: 250 case <-handleChan: 251 assert.Fail(t, "handler chan should not contain test1") 252 return 253 case <-time.After(100 * time.Millisecond): 254 assert.Fail(t, "queue should be paused") 255 return 256 } 257 258 lock.Lock() 259 pushBack = false 260 lock.Unlock() 261 262 paused, _ = pausable.IsPausedIsResumed() 263 264 select { 265 case <-paused: 266 case <-time.After(100 * time.Millisecond): 267 assert.Fail(t, "Queue is not paused") 268 return 269 } 270 271 pausable.Resume() 272 _, resumed = pausable.IsPausedIsResumed() 273 274 select { 275 case <-resumed: 276 case <-time.After(100 * time.Millisecond): 277 assert.Fail(t, "Queue should be resumed") 278 } 279 280 select { 281 case result1 = <-handleChan: 282 case <-time.After(500 * time.Millisecond): 283 assert.Fail(t, "handler chan should contain test1") 284 } 285 assert.Equal(t, test1.TestString, result1.TestString) 286 assert.Equal(t, test1.TestInt, result1.TestInt) 287 288 lock.Lock() 289 callbacks := make([]func(), len(queueShutdown)) 290 copy(callbacks, queueShutdown) 291 queueShutdown = queueShutdown[:0] 292 lock.Unlock() 293 // Now shutdown the queue 294 for _, callback := range callbacks { 295 callback() 296 } 297 298 // terminate the queue 299 lock.Lock() 300 callbacks = make([]func(), len(queueTerminate)) 301 copy(callbacks, queueTerminate) 302 queueShutdown = queueTerminate[:0] 303 lock.Unlock() 304 for _, callback := range callbacks { 305 callback() 306 } 307 select { 308 case <-terminated: 309 case <-time.After(10 * time.Second): 310 assert.Fail(t, "Queue should have terminated") 311 return 312 } 313 }