kvm2: apply the milestone review
Shorter start wait with immediate verdicts for dead or paused domains, ListByTag without state or address RPCs, Timeout for an expired check round, no adoption of unmarked domains, reference-counted keyed locks, dead options removed, HEAD fallback on any non-2xx. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -254,6 +255,86 @@ func TestWithDefaults_doesNotAliasInput(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func lockCount(k *keyedLocks) int {
|
||||
k.mu.Lock()
|
||||
defer k.mu.Unlock()
|
||||
return len(k.locks)
|
||||
}
|
||||
|
||||
func TestKeyedLocks_entryRemovedWhenUncontended(t *testing.T) {
|
||||
t.Parallel()
|
||||
k := newKeyedLocks()
|
||||
release, err := k.acquire(context.Background(), "a")
|
||||
if err != nil {
|
||||
t.Fatalf("acquire error = %v", err)
|
||||
}
|
||||
if lockCount(k) != 1 {
|
||||
t.Errorf("entries while held = %d, want 1", lockCount(k))
|
||||
}
|
||||
release()
|
||||
if lockCount(k) != 0 {
|
||||
t.Errorf("entries after release = %d, want 0", lockCount(k))
|
||||
}
|
||||
if _, err := k.acquire(context.Background(), "a"); err != nil {
|
||||
t.Fatalf("acquire after release error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyedLocks_waitersShareOneLock(t *testing.T) {
|
||||
t.Parallel()
|
||||
k := newKeyedLocks()
|
||||
const n = 20
|
||||
var inside, overlaps atomic.Int32
|
||||
var wg sync.WaitGroup
|
||||
for range n {
|
||||
wg.Go(func() {
|
||||
release, err := k.acquire(context.Background(), "shared")
|
||||
if err != nil {
|
||||
t.Errorf("acquire error = %v", err)
|
||||
return
|
||||
}
|
||||
if inside.Add(1) > 1 {
|
||||
overlaps.Add(1)
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
inside.Add(-1)
|
||||
release()
|
||||
})
|
||||
}
|
||||
wg.Wait()
|
||||
if overlaps.Load() != 0 {
|
||||
t.Errorf("%d holders overlapped on one key", overlaps.Load())
|
||||
}
|
||||
if lockCount(k) != 0 {
|
||||
t.Errorf("entries after every holder released = %d, want 0", lockCount(k))
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyedLocks_waiterGivesUpWithContext(t *testing.T) {
|
||||
t.Parallel()
|
||||
k := newKeyedLocks()
|
||||
release, err := k.acquire(context.Background(), "a")
|
||||
if err != nil {
|
||||
t.Fatalf("acquire error = %v", err)
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Millisecond)
|
||||
defer cancel()
|
||||
start := time.Now()
|
||||
if _, err := k.acquire(ctx, "a"); !errors.Is(err, context.DeadlineExceeded) {
|
||||
t.Fatalf("waiter error = %v, want DeadlineExceeded", err)
|
||||
}
|
||||
if time.Since(start) > time.Second {
|
||||
t.Errorf("waiter took %v, want to give up with its ctx", time.Since(start))
|
||||
}
|
||||
if lockCount(k) != 1 {
|
||||
t.Errorf("entries with the holder still in = %d, want 1: the waiter's exit must not drop the holder's entry", lockCount(k))
|
||||
}
|
||||
release()
|
||||
if lockCount(k) != 0 {
|
||||
t.Errorf("entries after the holder released = %d, want 0", lockCount(k))
|
||||
}
|
||||
}
|
||||
|
||||
func TestWrapErr_carriesProviderNameOpAndID(t *testing.T) {
|
||||
t.Parallel()
|
||||
p := newProvider("kvm2-home", twoHosts(), &stubDialer{})
|
||||
|
||||
Reference in New Issue
Block a user