platform
This commit is contained in:
1 parent
e706f7d907
commit
21bcb63440
42 files changed
+5903
No files matched your search
@@ -0,0 +1,177 @@
|
||||
package catalog
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// fakeCloser counts how many times it's been called. Used to assert the
|
||||
// drain-grace semantics of SnapshotHolder.
|
||||
type fakeCloser struct{ hits int }
|
||||
|
||||
func (f *fakeCloser) Close() error { f.hits++; return nil }
|
||||
|
||||
// withFakeCloser swaps SnapshotReader's closer for our counter. Returns the
|
||||
// reader and the counter; tests then call holder.Set(reloaded) and assert
|
||||
// that close is invoked after the drain grace.
|
||||
func withFakeCloser(t *testing.T, path string, fc *fakeCloser) *SnapshotReader {
|
||||
t.Helper()
|
||||
r, err := ReadSnapshotFile(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// replace real closer with our counter; the test asserts only via fc
|
||||
r.closer = fc.Close
|
||||
return r
|
||||
}
|
||||
|
||||
// TestSnapshotHolderReloadDrainsOld — Set swaps the current reader; the
|
||||
// previous reader is queued. Before grace: still Get-able. After grace:
|
||||
// reaped and Close fires.
|
||||
//
|
||||
// We use a very short grace so the test doesn't sleep long.
|
||||
func TestSnapshotHolderReloadDrainsOld(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
// 1) Build a valid first snapshot (skus A-1 / A-2).
|
||||
w := NewSnapshotWriter()
|
||||
if err := w.Append(makeProjection("A-1", 100)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := w.Append(makeProjection("A-2", 200)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := w.Write(dir, "first.bin"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
firstCloser := &fakeCloser{}
|
||||
first := withFakeCloser(t, filepath.Join(dir, "first.bin"), firstCloser)
|
||||
defer first.Close()
|
||||
|
||||
// 2) Build a second snapshot (skus B-1).
|
||||
w2 := NewSnapshotWriter()
|
||||
if err := w2.Append(makeProjection("B-1", 300)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := w2.Write(dir, "second.bin"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
secondCloser := &fakeCloser{}
|
||||
second := withFakeCloser(t, filepath.Join(dir, "second.bin"), secondCloser)
|
||||
defer second.Close()
|
||||
|
||||
h := NewSnapshotHolder(50 * time.Millisecond)
|
||||
defer h.Close()
|
||||
h.Set(first)
|
||||
if h.Len() != 2 {
|
||||
t.Errorf("Len after first Set: got %d want 2", h.Len())
|
||||
}
|
||||
if _, ok := h.Get("A-1"); !ok {
|
||||
t.Error("A-1 not found in initial state")
|
||||
}
|
||||
|
||||
// 3) Swap to second — first is now queued.
|
||||
h.Set(second)
|
||||
if h.Len() != 1 {
|
||||
t.Errorf("Len after second Set: got %d want 1", h.Len())
|
||||
}
|
||||
if firstCloser.hits != 0 {
|
||||
t.Errorf("firstCloser hits: got %d want 0 (still in drain grace)", firstCloser.hits)
|
||||
}
|
||||
if h.DrainCount() != 1 {
|
||||
t.Errorf("DrainCount: got %d want 1", h.DrainCount())
|
||||
}
|
||||
|
||||
// 4) Wait past grace, force a reap tick.
|
||||
time.Sleep(80 * time.Millisecond)
|
||||
h.reapNow()
|
||||
if firstCloser.hits != 1 {
|
||||
t.Errorf("firstCloser hits: got %d want 1 (should have reaped)", firstCloser.hits)
|
||||
}
|
||||
if h.DrainCount() != 0 {
|
||||
t.Errorf("DrainCount after reap: got %d want 0", h.DrainCount())
|
||||
}
|
||||
}
|
||||
|
||||
// TestSnapshotHolderEmptyGet — holder with no current serves empty.
|
||||
func TestSnapshotHolderEmptyGet(t *testing.T) {
|
||||
h := NewSnapshotHolder(100 * time.Millisecond)
|
||||
defer h.Close()
|
||||
if _, ok := h.Get("anything"); ok {
|
||||
t.Error("empty holder should return false")
|
||||
}
|
||||
if h.Len() != 0 {
|
||||
t.Errorf("Len: got %d want 0", h.Len())
|
||||
}
|
||||
}
|
||||
|
||||
// TestSnapshotHolderCloseImmediatelyReaps — Close with pending drains
|
||||
// should close them now (no grace wait). Confirms the drain queue is
|
||||
// drained on Close.
|
||||
func TestSnapshotHolderCloseImmediatelyReaps(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
|
||||
w := NewSnapshotWriter()
|
||||
if err := w.Append(makeProjection("X-1", 1)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := w.Write(dir, "x.bin"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fc1 := &fakeCloser{}
|
||||
r1 := withFakeCloser(t, filepath.Join(dir, "x.bin"), fc1)
|
||||
|
||||
w2 := NewSnapshotWriter()
|
||||
if err := w2.Append(makeProjection("Y-1", 2)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := w2.Write(dir, "y.bin"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fc2 := &fakeCloser{}
|
||||
r2 := withFakeCloser(t, filepath.Join(dir, "y.bin"), fc2)
|
||||
|
||||
h := NewSnapshotHolder(10 * time.Second) // long grace so reap wouldn't happen on its own
|
||||
h.Set(r1)
|
||||
h.Set(r2)
|
||||
|
||||
if fc1.hits != 0 || fc2.hits != 0 {
|
||||
t.Errorf("premature Close hits: fc1=%d fc2=%d", fc1.hits, fc2.hits)
|
||||
}
|
||||
if err := h.Close(); err != nil {
|
||||
t.Errorf("Close: %v", err)
|
||||
}
|
||||
if fc1.hits != 1 || fc2.hits != 1 {
|
||||
t.Errorf("after Close: fc1=%d fc2=%d want 1 each", fc1.hits, fc2.hits)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSnapshotHolderIdentityReload — Open the same path twice; current reader
|
||||
// is replaced; DrainCount returns 1 (old queued). Old's data still served
|
||||
// until grace elapses.
|
||||
func TestSnapshotHolderIdentityReload(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
w := NewSnapshotWriter()
|
||||
if err := w.Append(makeProjection("ID-1", 1)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := w.Write(dir, "id.bin"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
h := NewSnapshotHolder(10 * time.Second)
|
||||
defer h.Close()
|
||||
if err := h.Reload(filepath.Join(dir, "id.bin")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := h.Reload(filepath.Join(dir, "id.bin")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if h.DrainCount() != 1 {
|
||||
t.Errorf("DrainCount after two reloads of same path: got %d want 1", h.DrainCount())
|
||||
}
|
||||
if _, ok := h.Get("ID-1"); !ok {
|
||||
t.Error("current reader did not surface ID-1")
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user