85 lines
2.5 KiB
Go
85 lines
2.5 KiB
Go
package app
|
|
|
|
import (
|
|
"log/slog"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/fsnotify/fsnotify"
|
|
)
|
|
|
|
func TestFileWatcherFiltersProcessableEvents(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
root := t.TempDir()
|
|
watcher := &FileWatcher{rootDir: root, logger: slog.Default()}
|
|
|
|
tests := []struct {
|
|
name string
|
|
path string
|
|
op fsnotify.Op
|
|
want bool
|
|
}{
|
|
{name: "markdown write", path: "guide.md", op: fsnotify.Write, want: true},
|
|
{name: "markdown create", path: "nested/guide.md", op: fsnotify.Create, want: true},
|
|
{name: "case insensitive markdown", path: "guide.MD", op: fsnotify.Write, want: true},
|
|
{name: "markdown delete", path: "guide.md", op: fsnotify.Remove, want: true},
|
|
{name: "non markdown write", path: "logo.webp", op: fsnotify.Write, want: false},
|
|
{name: "non markdown delete", path: "logo.webp", op: fsnotify.Remove, want: false},
|
|
{name: "hidden markdown file", path: ".guide.md", op: fsnotify.Write, want: false},
|
|
{name: "hidden directory", path: ".cache/guide.md", op: fsnotify.Write, want: false},
|
|
{name: "backup file", path: "guide.md~", op: fsnotify.Write, want: false},
|
|
{name: "vim swap", path: "guide.md.swp", op: fsnotify.Write, want: false},
|
|
{name: "emacs lock file", path: "#guide.md#", op: fsnotify.Write, want: false},
|
|
{name: "chmod only", path: "guide.md", op: fsnotify.Chmod, want: false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
event := fsnotify.Event{Name: filepath.Join(root, tt.path), Op: tt.op}
|
|
if got := watcher.isProcessableEvent(event); got != tt.want {
|
|
t.Fatalf("isProcessableEvent() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFileWatcherDebouncesPendingSyncs(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
root := t.TempDir()
|
|
watcher := &FileWatcher{
|
|
rootDir: root,
|
|
logger: slog.Default(),
|
|
debounce: 10 * time.Millisecond,
|
|
}
|
|
|
|
var changed []string
|
|
watcher.SetOnChange(func(path string) {
|
|
changed = append(changed, path)
|
|
})
|
|
|
|
pending := make(map[string]struct{})
|
|
var timer *time.Timer
|
|
var timerC <-chan time.Time
|
|
defer watcher.stopTimer(timer)
|
|
|
|
watcher.scheduleSync(filepath.Join(root, "one.md"), pending, &timer, &timerC)
|
|
watcher.scheduleSync(filepath.Join(root, "two.md"), pending, &timer, &timerC)
|
|
|
|
select {
|
|
case <-timerC:
|
|
watcher.flushPending(pending)
|
|
case <-time.After(250 * time.Millisecond):
|
|
t.Fatal("debounce timer did not fire")
|
|
}
|
|
|
|
if len(changed) != 1 {
|
|
t.Fatalf("onChange calls = %d, want 1", len(changed))
|
|
}
|
|
if len(pending) != 0 {
|
|
t.Fatalf("pending changes = %d, want 0", len(pending))
|
|
}
|
|
}
|