This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
package watcher
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestWatcherDetectsChange(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "test.json")
|
||||
if err := os.WriteFile(path, []byte(`{"version": 1}`), 0644); err != nil {
|
||||
t.Fatalf("failed to write test file: %v", err)
|
||||
}
|
||||
|
||||
changeDetected := make(chan bool, 1)
|
||||
onChange := func() {
|
||||
select {
|
||||
case changeDetected <- true:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
fw := NewFileWatcher(path, 100*time.Millisecond, onChange)
|
||||
fw.Start()
|
||||
defer fw.Stop()
|
||||
|
||||
// Wait for initial hash to be computed
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
// Modify the file
|
||||
if err := os.WriteFile(path, []byte(`{"version": 2}`), 0644); err != nil {
|
||||
t.Fatalf("failed to modify test file: %v", err)
|
||||
}
|
||||
|
||||
// Wait for change detection
|
||||
select {
|
||||
case detected := <-changeDetected:
|
||||
if !detected {
|
||||
t.Error("expected change detection, got false")
|
||||
}
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Error("timeout waiting for file change detection")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWatcherNoChange(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "test.json")
|
||||
if err := os.WriteFile(path, []byte(`{"version": 1}`), 0644); err != nil {
|
||||
t.Fatalf("failed to write test file: %v", err)
|
||||
}
|
||||
|
||||
changeDetected := make(chan bool, 1)
|
||||
onChange := func() {
|
||||
select {
|
||||
case changeDetected <- true:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
fw := NewFileWatcher(path, 100*time.Millisecond, onChange)
|
||||
fw.Start()
|
||||
defer fw.Stop()
|
||||
|
||||
// Wait without modifying the file
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
|
||||
// Should not detect a change
|
||||
select {
|
||||
case <-changeDetected:
|
||||
t.Error("unexpected change detection without file modification")
|
||||
default:
|
||||
// Expected: no change detected
|
||||
}
|
||||
}
|
||||
|
||||
func TestWatcherMultipleChanges(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "test.json")
|
||||
if err := os.WriteFile(path, []byte(`{"v": 1}`), 0644); err != nil {
|
||||
t.Fatalf("failed to write test file: %v", err)
|
||||
}
|
||||
|
||||
changeCount := 0
|
||||
var mu chan struct{}
|
||||
|
||||
onChange := func() {
|
||||
changeCount++
|
||||
if mu != nil {
|
||||
mu <- struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
fw := NewFileWatcher(path, 50*time.Millisecond, onChange)
|
||||
fw.Start()
|
||||
defer fw.Stop()
|
||||
|
||||
// Wait for initial hash
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// Make 3 changes
|
||||
for i := 2; i <= 4; i++ {
|
||||
if err := os.WriteFile(path, []byte(`{"v": `+string(rune('0'+i))+`}`), 0644); err != nil {
|
||||
t.Fatalf("failed to modify file: %v", err)
|
||||
}
|
||||
time.Sleep(150 * time.Millisecond)
|
||||
}
|
||||
|
||||
if changeCount < 2 {
|
||||
t.Errorf("expected at least 2 change detections, got %d", changeCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWatcherStop(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "test.json")
|
||||
if err := os.WriteFile(path, []byte(`{"v": 1}`), 0644); err != nil {
|
||||
t.Fatalf("failed to write test file: %v", err)
|
||||
}
|
||||
|
||||
fw := NewFileWatcher(path, 50*time.Millisecond, func() {})
|
||||
fw.Start()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
fw.Stop()
|
||||
|
||||
// Should not panic on double stop or further operations
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
func TestWatcherNonexistentFile(t *testing.T) {
|
||||
onChange := func() {
|
||||
t.Error("onChange should not be called for nonexistent file")
|
||||
}
|
||||
|
||||
fw := NewFileWatcher("/nonexistent/file.json", 100*time.Millisecond, onChange)
|
||||
fw.Start()
|
||||
defer fw.Stop()
|
||||
|
||||
// Wait — should not call onChange since file doesn't exist
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
}
|
||||
|
||||
func TestWatcherReproducible(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "test.json")
|
||||
if err := os.WriteFile(path, []byte(`{"data": "value"}`), 0644); err != nil {
|
||||
t.Fatalf("failed to write test file: %v", err)
|
||||
}
|
||||
|
||||
// Create two watchers on the same file
|
||||
var detected1, detected2 bool
|
||||
|
||||
fw1 := NewFileWatcher(path, 50*time.Millisecond, func() { detected1 = true })
|
||||
fw2 := NewFileWatcher(path, 50*time.Millisecond, func() { detected2 = true })
|
||||
|
||||
fw1.Start()
|
||||
fw2.Start()
|
||||
defer fw1.Stop()
|
||||
defer fw2.Stop()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// Modify file
|
||||
if err := os.WriteFile(path, []byte(`{"data": "changed"}`), 0644); err != nil {
|
||||
t.Fatalf("failed to modify file: %v", err)
|
||||
}
|
||||
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
|
||||
if !detected1 {
|
||||
t.Error("watcher 1 did not detect change")
|
||||
}
|
||||
if !detected2 {
|
||||
t.Error("watcher 2 did not detect change")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user