Files

76 lines
1.9 KiB
Go

package usage
import (
"fmt"
"sync"
"sync/atomic"
"testing"
"time"
"github.com/yetone/magpie/internal/sessions"
)
// The sources a query parses are read several at a time, each handed back
// as its own, in any order asked; never more than a window of them waits to
// be taken, and one not wanted is read when it is asked for.
func TestPrefetchSources(t *testing.T) {
var sources []sessions.CallSource
for i := range 200 {
sources = append(sources, sessions.CallSource{Path: fmt.Sprint(i)})
}
var mu sync.Mutex
reads := map[string]int{}
var busy, most atomic.Int32
readSource := func(s sessions.CallSource) []sessions.Call {
n := busy.Add(1)
for {
m := most.Load()
if n <= m || most.CompareAndSwap(m, n) {
break
}
}
time.Sleep(time.Millisecond)
busy.Add(-1)
mu.Lock()
reads[s.Path]++
mu.Unlock()
return []sessions.Call{{Model: s.Path}}
}
want := func(s sessions.CallSource) bool { return s.Path != "7" }
read := prefetchSources(sources, want, readSource)
for i := range sources {
if i == 50 {
// nothing is taken for a while: the readers stop at the window
time.Sleep(50 * time.Millisecond)
mu.Lock()
ahead := len(reads)
mu.Unlock()
if ahead > 50+32+8 {
t.Fatalf("%d read with 50 taken", ahead)
}
}
cs := read(i)
if len(cs) != 1 || cs[0].Model != sources[i].Path {
t.Fatalf("source %d: %+v", i, cs)
}
}
if most.Load() < 2 {
t.Fatalf("at most %d read at once", most.Load())
}
for _, s := range sources {
if reads[s.Path] != 1 {
t.Fatalf("%s read %d times", s.Path, reads[s.Path])
}
}
// one or none wanted: read as asked, nothing ahead
reads = map[string]int{}
read = prefetchSources(sources[:3], func(s sessions.CallSource) bool { return s.Path == "1" }, readSource)
if len(reads) != 0 {
t.Fatalf("read ahead: %v", reads)
}
if cs := read(2); cs[0].Model != "2" {
t.Fatalf("%+v", cs)
}
}