mirror of
https://github.com/samuelncui/yatm.git
synced 2026-11-08 04:18:03 +00:00
Unify file queries and Scan execution, retain indexed Identical results, improve Job and Preview workflows, and verify release packaging.
95 lines
2.3 KiB
Go
95 lines
2.3 KiB
Go
package executor
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestResourceQueueAdmitsWaitersInArrivalOrder(t *testing.T) {
|
|
q := newResourceQueue()
|
|
require.NoError(t, q.acquire(context.Background(), "scan:1", nil))
|
|
|
|
admitted := make(chan string, 2)
|
|
var waiting sync.WaitGroup
|
|
var lock sync.Mutex
|
|
queued := 0
|
|
enqueue := func(name string) {
|
|
waiting.Add(1)
|
|
go func() {
|
|
defer waiting.Done()
|
|
err := q.acquire(context.Background(), "scan:1", func() {
|
|
lock.Lock()
|
|
queued++
|
|
lock.Unlock()
|
|
})
|
|
if err != nil {
|
|
t.Errorf("acquire %s failed, %v", name, err)
|
|
return
|
|
}
|
|
admitted <- name
|
|
}()
|
|
}
|
|
queuedWaiters := func(count int) {
|
|
require.Eventually(t, func() bool {
|
|
lock.Lock()
|
|
defer lock.Unlock()
|
|
return queued == count
|
|
}, 5*time.Second, time.Millisecond)
|
|
}
|
|
enqueue("second")
|
|
queuedWaiters(1)
|
|
enqueue("third")
|
|
queuedWaiters(2)
|
|
|
|
// Each release hands the resource to the waiter that arrived first.
|
|
q.release("scan:1")
|
|
require.Equal(t, "second", <-admitted)
|
|
q.release("scan:1")
|
|
require.Equal(t, "third", <-admitted)
|
|
q.release("scan:1")
|
|
waiting.Wait()
|
|
}
|
|
|
|
func TestResourceQueueDropsCanceledWaiterWithoutLosingTheResource(t *testing.T) {
|
|
q := newResourceQueue()
|
|
require.NoError(t, q.acquire(context.Background(), "scan:1", nil))
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
waited := make(chan struct{})
|
|
abandoned := make(chan error, 1)
|
|
go func() { abandoned <- q.acquire(ctx, "scan:1", func() { close(waited) }) }()
|
|
<-waited
|
|
cancel()
|
|
require.ErrorIs(t, <-abandoned, context.Canceled)
|
|
|
|
// The holder still owns the resource, and a later waiter receives it on release.
|
|
admitted := make(chan struct{})
|
|
go func() {
|
|
if err := q.acquire(context.Background(), "scan:1", nil); err != nil {
|
|
t.Errorf("later acquire failed, %v", err)
|
|
return
|
|
}
|
|
close(admitted)
|
|
}()
|
|
require.Eventually(t, func() bool {
|
|
q.lock.Lock()
|
|
defer q.lock.Unlock()
|
|
return len(q.waiters["scan:1"]) == 1
|
|
}, 5*time.Second, time.Millisecond)
|
|
q.release("scan:1")
|
|
select {
|
|
case <-admitted:
|
|
case <-time.After(5 * time.Second):
|
|
t.Fatal("later waiter never received the released resource")
|
|
}
|
|
q.release("scan:1")
|
|
|
|
// A fully released key is free again.
|
|
require.NoError(t, q.acquire(context.Background(), "scan:1", nil))
|
|
q.release("scan:1")
|
|
}
|