Fix intermittent CI failure in EmptyQueue (#23753)

The ordering of the final token causing a close of the queue in this
test may be out of sync due to concurrency. Instead just use ensure that
the queue is closed when everything expected is done.

Fixes: https://github.com/go-gitea/gitea/issues/23608
Fixes: https://github.com/go-gitea/gitea/issues/23977

---------

Signed-off-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
This commit is contained in:
zeripath 2023-05-04 02:37:30 +01:00 committed by GitHub
parent 402df1d6b4
commit ad8631c069
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -4,9 +4,9 @@
package queue package queue
import ( import (
"os"
"strconv" "strconv"
"sync" "sync"
"sync/atomic"
"testing" "testing"
"time" "time"
@ -16,10 +16,7 @@ import (
) )
func TestPersistableChannelUniqueQueue(t *testing.T) { func TestPersistableChannelUniqueQueue(t *testing.T) {
if os.Getenv("CI") != "" { // Create a temporary directory for the queue
t.Skip("Skipping because test is flaky on CI")
}
tmpDir := t.TempDir() tmpDir := t.TempDir()
_ = log.NewLogger(1000, "console", "console", `{"level":"warn","stacktracelevel":"NONE","stderr":true}`) _ = log.NewLogger(1000, "console", "console", `{"level":"warn","stacktracelevel":"NONE","stderr":true}`)
@ -100,7 +97,7 @@ func TestPersistableChannelUniqueQueue(t *testing.T) {
executedInitial := map[string][]string{} executedInitial := map[string][]string{}
hasInitial := map[string][]string{} hasInitial := map[string][]string{}
fillQueue := func(name string, done chan struct{}) { fillQueue := func(name string, done chan int64) {
t.Run("Initial Filling: "+name, func(t *testing.T) { t.Run("Initial Filling: "+name, func(t *testing.T) {
lock := sync.Mutex{} lock := sync.Mutex{}
@ -157,33 +154,39 @@ func TestPersistableChannelUniqueQueue(t *testing.T) {
assert.Equal(t, 101, len(executedInitial[name])+len(hasInitial[name])) assert.Equal(t, 101, len(executedInitial[name])+len(hasInitial[name]))
mapLock.Unlock() mapLock.Unlock()
}) })
mapLock.Lock()
count := int64(len(hasInitial[name]))
mapLock.Unlock()
done <- count
close(done) close(done)
} }
doneA := make(chan struct{}) hasQueueAChan := make(chan int64)
doneB := make(chan struct{}) hasQueueBChan := make(chan int64)
go fillQueue("QueueA", doneA) go fillQueue("QueueA", hasQueueAChan)
go fillQueue("QueueB", doneB) go fillQueue("QueueB", hasQueueBChan)
<-doneA hasA := <-hasQueueAChan
<-doneB hasB := <-hasQueueBChan
executedEmpty := map[string][]string{} executedEmpty := map[string][]string{}
hasEmpty := map[string][]string{} hasEmpty := map[string][]string{}
emptyQueue := func(name string, done chan struct{}) { emptyQueue := func(name string, numInQueue int64, done chan struct{}) {
t.Run("Empty Queue: "+name, func(t *testing.T) { t.Run("Empty Queue: "+name, func(t *testing.T) {
lock := sync.Mutex{} lock := sync.Mutex{}
stop := make(chan struct{}) stop := make(chan struct{})
// collect the tasks that have been executed // collect the tasks that have been executed
atomicCount := int64(0)
handle := func(data ...Data) []Data { handle := func(data ...Data) []Data {
lock.Lock() lock.Lock()
for _, datum := range data { for _, datum := range data {
mapLock.Lock() mapLock.Lock()
executedEmpty[name] = append(executedEmpty[name], datum.(string)) executedEmpty[name] = append(executedEmpty[name], datum.(string))
mapLock.Unlock() mapLock.Unlock()
if datum.(string) == "final" { count := atomic.AddInt64(&atomicCount, 1)
if count >= numInQueue {
close(stop) close(stop)
} }
} }
@ -217,11 +220,11 @@ func TestPersistableChannelUniqueQueue(t *testing.T) {
close(done) close(done)
} }
doneA = make(chan struct{}) doneA := make(chan struct{})
doneB = make(chan struct{}) doneB := make(chan struct{})
go emptyQueue("QueueA", doneA) go emptyQueue("QueueA", hasA, doneA)
go emptyQueue("QueueB", doneB) go emptyQueue("QueueB", hasB, doneB)
<-doneA <-doneA
<-doneB <-doneB
@ -237,20 +240,20 @@ func TestPersistableChannelUniqueQueue(t *testing.T) {
hasEmpty = map[string][]string{} hasEmpty = map[string][]string{}
mapLock.Unlock() mapLock.Unlock()
doneA = make(chan struct{}) hasQueueAChan = make(chan int64)
doneB = make(chan struct{}) hasQueueBChan = make(chan int64)
go fillQueue("QueueA", doneA) go fillQueue("QueueA", hasQueueAChan)
go fillQueue("QueueB", doneB) go fillQueue("QueueB", hasQueueBChan)
<-doneA hasA = <-hasQueueAChan
<-doneB hasB = <-hasQueueBChan
doneA = make(chan struct{}) doneA = make(chan struct{})
doneB = make(chan struct{}) doneB = make(chan struct{})
go emptyQueue("QueueA", doneA) go emptyQueue("QueueA", hasA, doneA)
go emptyQueue("QueueB", doneB) go emptyQueue("QueueB", hasB, doneB)
<-doneA <-doneA
<-doneB <-doneB