package cache import ( "io" "s1d3sw1ped/steamcache2/vfs/memory" "sync" "sync/atomic" "testing" "time" ) func TestTieredCache_PromotionFallback(t *testing.T) { t.Parallel() fast, err := memory.New(1 * 1024 * 1024) if err != nil { t.Fatal(err) } slow, err := memory.New(10 * 1024 * 1024) // use mem for "disk" in test if err != nil { t.Fatal(err) } tc := New() tc.SetFast(fast) tc.SetSlow(slow) // write to slow (disk) w, err := tc.Create("p1", 1024) if err != nil { t.Fatal(err) } w.Write(make([]byte, 1024)) w.Close() // open should hit slow, trigger promote goroutine r, err := tc.Open("p1") if err != nil { t.Fatal(err) } io.Copy(io.Discard, r) r.Close() // Replace fixed sleep with bounded poll for promotion completion (robust vs load/CI variance; addresses issue7) deadline := time.Now().Add(500 * time.Millisecond) promoted := false for time.Now().Before(deadline) { if _, err := fast.Stat("p1"); err == nil { promoted = true break } time.Sleep(5 * time.Millisecond) } if !promoted { // Still allow slow tier stat as fallback (promotion is best-effort) if _, err := tc.Stat("p1"); err != nil { t.Errorf("stat after promote attempt: %v", err) } } // size total if tc.Size() < 1024 { t.Error("total size under") } } func TestTieredCache_DeleteAllTiers(t *testing.T) { t.Parallel() fast, err := memory.New(1024) if err != nil { t.Fatal(err) } slow, err := memory.New(1024) if err != nil { t.Fatal(err) } tc := New() tc.SetFast(fast) tc.SetSlow(slow) w, _ := tc.Create("delme", 100) w.Write([]byte{1}) w.Close() tc.Delete("delme") if _, err := tc.Open("delme"); err == nil { t.Error("deleted key still openable from tiers") } } func TestTieredCache_Concurrent(t *testing.T) { if testing.Short() { t.Skip() } t.Parallel() fast, err := memory.New(5 * 1024 * 1024) if err != nil { t.Fatal(err) } slow, err := memory.New(20 * 1024 * 1024) if err != nil { t.Fatal(err) } tc := New() tc.SetFast(fast) tc.SetSlow(slow) var wg sync.WaitGroup var hits int64 for i := 0; i < 6; i++ { wg.Add(1) go func(id int) { defer wg.Done() for j := 0; j < 20; j++ { k := "ct" + string(rune(id)) + string(rune(j%5)) if w, e := tc.Create(k, 256); e == nil { w.Write(make([]byte, 256)) w.Close() } if r, e := tc.Open(k); e == nil { io.Copy(io.Discard, r) r.Close() atomic.AddInt64(&hits, 1) } tc.Delete(k) } }(i) } wg.Wait() if hits < 10 { t.Errorf("low tier hits %d", hits) } }