package pool import "testing" // TestNewPoolSizes pins NewPool's contract: Len is exactly n, every slot starts // idle, and Busy is writable by the caller (the pool only owns the bookkeeping). func TestNewPoolSizes(t *testing.T) { zero := NewPool(0) if zero.Len() != 0 { t.Fatalf("NewPool(0).Len() = %d", zero.Len()) } if zero.IdleIndex() != -1 { t.Fatalf("NewPool(0).IdleIndex() = %d", zero.IdleIndex()) } one := NewPool(1) if one.Len() != 1 { t.Fatalf("NewPool(1).Len() = %d", one.Len()) } if one.Busy[0] { t.Fatal("NewPool(1) slot 0 must start idle") } if one.IdleIndex() != 0 { t.Fatalf("NewPool(1).IdleIndex() = %d", one.IdleIndex()) } three := NewPool(3) if three.Len() != 3 { t.Fatalf("NewPool(3).Len() = %d", three.Len()) } for i := 0; i < 3; i++ { if three.Busy[i] { t.Fatalf("NewPool(3) slot %d must start idle", i) } } } // TestIdleIndexScanOrder pins that IdleIndex returns the lowest idle index and // advances as slots are marked busy, then recovers when one is released. func TestIdleIndexScanOrder(t *testing.T) { p := NewPool(3) if p.IdleIndex() != 0 { t.Fatalf("all idle -> %d, want 0", p.IdleIndex()) } p.Busy[0] = true if p.IdleIndex() != 1 { t.Fatalf("slot 0 busy -> %d, want 1", p.IdleIndex()) } p.Busy[1] = true if p.IdleIndex() != 2 { t.Fatalf("slots 0,1 busy -> %d, want 2", p.IdleIndex()) } p.Busy[2] = true if p.IdleIndex() != -1 { t.Fatalf("all busy -> %d, want -1", p.IdleIndex()) } // Freeing a lower slot wins the scan again. p.Busy[1] = false if p.IdleIndex() != 1 { t.Fatalf("slot 1 freed -> %d, want 1", p.IdleIndex()) } } // TestSingleWorkerBusyFlips covers the 1-worker pool: either the only slot is // idle (index 0) or the pool reports full (-1). func TestSingleWorkerBusyFlips(t *testing.T) { p := NewPool(1) if p.IdleIndex() != 0 { t.Fatalf("1 worker idle -> %d, want 0", p.IdleIndex()) } p.Busy[0] = true if p.IdleIndex() != -1 { t.Fatalf("1 worker busy -> %d, want -1", p.IdleIndex()) } p.Busy[0] = false if p.IdleIndex() != 0 { t.Fatalf("1 worker released -> %d, want 0", p.IdleIndex()) } }