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