package keyedmutex import ( "sync" "testing" "time" ) func TestLockSerializesSameKey(t *testing.T) { var m Mutex unlock := m.Lock("a") acquired := make(chan struct{}) go func() { u := m.Lock("a") close(acquired) u() }() select { case <-acquired: t.Fatal("second Lock on the same key acquired while the first was held") case <-time.After(50 * time.Millisecond): // expected: blocked } unlock() select { case <-acquired: // expected: now acquired case <-time.After(time.Second): t.Fatal("second Lock did not acquire after release") } } func TestLockIndependentKeys(t *testing.T) { var m Mutex unlockA := m.Lock("a") defer unlockA() // A different key must not block. done := make(chan struct{}) go func() { u := m.Lock("b"); u(); close(done) }() select { case <-done: case <-time.After(time.Second): t.Fatal("Lock on an independent key blocked") } } func TestTryLock(t *testing.T) { var m Mutex unlock, ok := m.TryLock("a") if !ok { t.Fatal("TryLock should succeed on a free key") } if _, ok := m.TryLock("a"); ok { t.Fatal("TryLock should fail while the key is held") } unlock() u2, ok := m.TryLock("a") if !ok { t.Fatal("TryLock should succeed after release") } u2() } func TestConcurrentLockNoRace(t *testing.T) { var m Mutex var wg sync.WaitGroup counter := 0 for i := 0; i < 50; i++ { wg.Add(1) go func() { defer wg.Done() u := m.Lock("shared") counter++ // protected by the keyed lock u() }() } wg.Wait() if counter != 50 { t.Errorf("counter = %d, want 50 (lost updates ⇒ lock not serializing)", counter) } }