87 lines
2.6 KiB
Go
87 lines
2.6 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// A take-in that waits is said once for each reason it waits for, and tried again every doneKeyRetry until it is taken
|
|
// in: never left for a key write that may not come (issue 1945, live on 2026-10-25: mesh-host's take-in at 26bd waited,
|
|
// said nothing, and nothing woke it until another write).
|
|
func TestAWaitingTakeInIsSaidOnceAndTriedAgain(t *testing.T) {
|
|
was, wasRetry, wasSay := doneKeyTakeOnce, doneKeyRetry, doneKeysSay
|
|
t.Cleanup(func() { doneKeyTakeOnce, doneKeyRetry, doneKeysSay = was, wasRetry, wasSay })
|
|
doneKeyRetry = 5 * time.Millisecond
|
|
var mu sync.Mutex
|
|
var said []string
|
|
doneKeysSay = func(s string) { mu.Lock(); said = append(said, s); mu.Unlock() }
|
|
calls := 0
|
|
doneKeyTakeOnce = func(context.Context, *stores, string) error {
|
|
calls++
|
|
switch {
|
|
case calls <= 2:
|
|
return fmt.Errorf("%w: the artifact store could not hold mesh-host's artifact", errKeyWaits)
|
|
case calls == 3:
|
|
return fmt.Errorf("%w: mesh-host's key cannot be read", errKeyWaits)
|
|
}
|
|
return nil
|
|
}
|
|
done := make(chan struct{})
|
|
go func() { takeDoneKey(context.Background(), nil, "mesh-host"); close(done) }()
|
|
select {
|
|
case <-done:
|
|
case <-time.After(5 * time.Second):
|
|
t.Fatal("a waiting take-in was not tried again until it was taken in")
|
|
}
|
|
if calls != 4 {
|
|
t.Fatalf("tried %d times, want 4", calls)
|
|
}
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
waits := 0
|
|
for _, s := range said {
|
|
if strings.Contains(s, "mesh-host waits") {
|
|
waits++
|
|
}
|
|
}
|
|
if waits != 2 {
|
|
t.Fatalf("said %q: want each of the two reasons once", said)
|
|
}
|
|
}
|
|
|
|
// A wake while a take-in of the module already follows it adds no second one: the follower looks again itself.
|
|
func TestAWakeWhileATakeInFollowsAddsNone(t *testing.T) {
|
|
was, wasRetry, wasSay := doneKeyTakeOnce, doneKeyRetry, doneKeysSay
|
|
t.Cleanup(func() { doneKeyTakeOnce, doneKeyRetry, doneKeysSay = was, wasRetry, wasSay })
|
|
doneKeyRetry = 20 * time.Millisecond
|
|
doneKeysSay = func(string) {}
|
|
var mu sync.Mutex
|
|
calls := 0
|
|
release := make(chan struct{})
|
|
doneKeyTakeOnce = func(context.Context, *stores, string) error {
|
|
mu.Lock()
|
|
calls++
|
|
mu.Unlock()
|
|
select {
|
|
case <-release:
|
|
return nil
|
|
default:
|
|
return fmt.Errorf("%w: the key says building", errKeyWaits)
|
|
}
|
|
}
|
|
go takeDoneKey(context.Background(), nil, "board")
|
|
time.Sleep(5 * time.Millisecond)
|
|
returned := make(chan struct{})
|
|
go func() { takeDoneKey(context.Background(), nil, "board"); close(returned) }()
|
|
select {
|
|
case <-returned:
|
|
case <-time.After(time.Second):
|
|
t.Fatal("a second wake waited for the follower")
|
|
}
|
|
close(release)
|
|
}
|