package syncext import ( "context" "sync" "testing" "time" ) func TestAtomicGetSet(t *testing.T) { a := NewAtomic(42) if v := a.Get(); v != 42 { t.Errorf("expected 42, got %d", v) } old := a.Set(100) if old != 42 { t.Errorf("expected old value 42, got %d", old) } if v := a.Get(); v != 100 { t.Errorf("expected 100, got %d", v) } } func TestAtomicGetSetString(t *testing.T) { a := NewAtomic("hello") if v := a.Get(); v != "hello" { t.Errorf("expected 'hello', got %q", v) } old := a.Set("world") if old != "hello" { t.Errorf("expected old value 'hello', got %q", old) } if v := a.Get(); v != "world" { t.Errorf("expected 'world', got %q", v) } } func TestAtomicUpdate(t *testing.T) { a := NewAtomic(10) a.Update(func(old int) int { return old * 2 }) if v := a.Get(); v != 20 { t.Errorf("expected 20, got %d", v) } a.Update(func(old int) int { return old + 5 }) if v := a.Get(); v != 25 { t.Errorf("expected 25, got %d", v) } } func TestAtomicCompareAndSwap(t *testing.T) { a := NewAtomic(5) if !a.CompareAndSwap(5, 10) { t.Error("CAS should have succeeded") } if v := a.Get(); v != 10 { t.Errorf("expected 10, got %d", v) } if a.CompareAndSwap(5, 20) { t.Error("CAS should have failed") } if v := a.Get(); v != 10 { t.Errorf("expected 10, got %d", v) } } func TestAtomicWaitAlreadyMatching(t *testing.T) { a := NewAtomic(7) done := make(chan struct{}) go func() { a.Wait(7) close(done) }() select { case <-done: // ok case <-time.After(500 * time.Millisecond): t.Error("Wait should return immediately if value already matches") } } func TestAtomicWaitWithTimeoutNoMatch(t *testing.T) { a := NewAtomic(1) err := a.WaitWithTimeout(50*time.Millisecond, 999) if err == nil { t.Error("expected timeout error") } } func TestAtomicWaitWithTimeoutMatchAfterSet(t *testing.T) { a := NewAtomic(1) go func() { time.Sleep(20 * time.Millisecond) a.Set(99) }() err := a.WaitWithTimeout(500*time.Millisecond, 99) if err != nil { t.Errorf("expected nil, got %v", err) } } func TestAtomicWaitWithContextCancel(t *testing.T) { a := NewAtomic(1) ctx, cancel := context.WithCancel(context.Background()) go func() { time.Sleep(20 * time.Millisecond) cancel() }() err := a.WaitWithContext(ctx, 999) if err == nil { t.Error("expected ctx error") } } func TestAtomicWaitWithContextAlreadyCancelled(t *testing.T) { a := NewAtomic(1) ctx, cancel := context.WithCancel(context.Background()) cancel() err := a.WaitWithContext(ctx, 1) if err == nil { t.Error("expected ctx error") } } func TestAtomicWaitForChange(t *testing.T) { a := NewAtomic(1) ch := a.WaitForChange() go func() { time.Sleep(20 * time.Millisecond) a.Set(2) }() select { case v := <-ch: if v != 2 { t.Errorf("expected 2, got %d", v) } case <-time.After(500 * time.Millisecond): t.Error("WaitForChange did not deliver") } } func TestAtomicConcurrentSet(t *testing.T) { a := NewAtomic(0) var wg sync.WaitGroup for i := 0; i < 50; i++ { wg.Add(1) go func(v int) { defer wg.Done() a.Set(v) }(i) } wg.Wait() v := a.Get() if v < 0 || v >= 50 { t.Errorf("unexpected final value %d", v) } } func TestAtomicConcurrentUpdate(t *testing.T) { a := NewAtomic(0) var wg sync.WaitGroup for i := 0; i < 100; i++ { wg.Add(1) go func() { defer wg.Done() a.Update(func(old int) int { return old + 1 }) }() } wg.Wait() if v := a.Get(); v != 100 { t.Errorf("expected 100, got %d", v) } } func TestAtomicWaitWithTimeoutZero(t *testing.T) { a := NewAtomic(1) err := a.WaitWithTimeout(0, 999) if err == nil { t.Error("expected error for zero timeout with non-matching value") } }