package flock import ( "errors" "os" "testing" "time" ) func TestLock_basic(t *testing.T) { const path = "/tmp/fsutil-test-lock" f, err := Lock(path) if err != nil { t.Fatal(err) } // locked is closed once the goroutine acquires the lock. locked := make(chan struct{}) go func() { f2, err := Lock(path) // blocks until f is unlocked if err != nil { t.Error(err) return } close(locked) Unlock(f2) }() // Goroutine should be blocked while we hold the lock. select { case <-locked: t.Fatal("goroutine acquired lock while we hold it") case <-time.After(50 * time.Millisecond): } Unlock(f) // Now the goroutine should unblock and acquire the lock. select { case <-locked: case <-time.After(time.Second): t.Fatal("goroutine never acquired lock after release") } } func TestLock_badPath(t *testing.T) { _, err := Lock("./dne/file.lock") if err == nil { t.Fatal("expected error for bad path") } } func TestTryLock(t *testing.T) { const path = "/tmp/fsutil-test-lock" f, err := TryLock(path) if err != nil { t.Fatal(err) } defer Unlock(f) // While f holds the lock, TryLock should return ErrLocked. _, err = TryLock(path) if !errors.Is(err, ErrLocked) { t.Fatalf("expected ErrLocked, got %v", err) } // After unlocking, TryLock should succeed. if err := Unlock(f); err != nil { t.Fatal(err) } f2, err := TryLock(path) if err != nil { t.Fatalf("expected lock after unlock, got %v", err) } Unlock(f2) } func TestTryLockFile(t *testing.T) { const path = "/tmp/fsutil-test-lockfile" f, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0600) if err != nil { t.Fatal(err) } defer f.Close() if err := TryLockFile(f); err != nil { t.Fatal(err) } f2, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0600) if err != nil { t.Fatal(err) } defer f2.Close() if err := TryLockFile(f2); !errors.Is(err, ErrLocked) { t.Fatalf("expected ErrLocked, got %v", err) } if err := Unlock(f); err != nil { t.Fatal(err) } if err := TryLockFile(f2); err != nil { t.Fatalf("expected lock after unlock, got %v", err) } }