1package fswatch
2
3import (
4 "context"
5 "os"
6 "path/filepath"
7 "testing"
8 "time"
9)
10
11func waitSignal(ch <-chan struct{}, d time.Duration) bool {
12 select {
13 case <-ch:
14 return true
15 case <-time.After(d):
16 return false
17 }
18}
19
20// start runs WatchDir and returns once the watch is surely in place.
21func start(t *testing.T, dir string) <-chan struct{} {
22 t.Helper()
23 ctx, cancel := context.WithCancel(context.Background())
24 t.Cleanup(cancel)
25 ch := make(chan struct{}, 1)
26 go func() { _ = WatchDir(ctx, dir, 30*time.Millisecond, ch) }()
27 time.Sleep(150 * time.Millisecond)
28 return ch
29}
30
31func TestWatchDirReportsAnyFileOfTheDirectory(t *testing.T) {
32 dir := t.TempDir()
33 ch := start(t, dir)
34 if err := os.WriteFile(filepath.Join(dir, "lib.sh"), []byte("x"), 0o644); err != nil {
35 t.Fatal(err)
36 }
37 if !waitSignal(ch, 2*time.Second) {
38 t.Fatal("a new file in the directory was not reported")
39 }
40 // An edit of an existing file, and a replacement by rename, are reported too.
41 if err := os.WriteFile(filepath.Join(dir, "lib.sh"), []byte("y"), 0o644); err != nil {
42 t.Fatal(err)
43 }
44 if !waitSignal(ch, 2*time.Second) {
45 t.Fatal("an edit was not reported")
46 }
47 tmp := filepath.Join(dir, "lib.sh.new")
48 _ = os.WriteFile(tmp, []byte("z"), 0o644)
49 waitSignal(ch, 2*time.Second) // the .new file itself
50 if err := os.Rename(tmp, filepath.Join(dir, "lib.sh")); err != nil {
51 t.Fatal(err)
52 }
53 if !waitSignal(ch, 2*time.Second) {
54 t.Fatal("a rename over a file was not reported")
55 }
56}
57
58func TestWatchDirIgnoresHiddenAndBackupFiles(t *testing.T) {
59 dir := t.TempDir()
60 ch := start(t, dir)
61 for _, name := range []string{".left.sh.swp", "left.sh~"} {
62 if err := os.WriteFile(filepath.Join(dir, name), []byte("x"), 0o644); err != nil {
63 t.Fatal(err)
64 }
65 }
66 if waitSignal(ch, 400*time.Millisecond) {
67 t.Fatal("a swap or backup file was reported")
68 }
69}
70
71func TestWatchDirWaitsForAMissingDirectory(t *testing.T) {
72 dir := filepath.Join(t.TempDir(), "later")
73 ctx, cancel := context.WithCancel(context.Background())
74 t.Cleanup(cancel)
75 ch := make(chan struct{}, 1)
76 go func() { _ = WatchDir(ctx, dir, 30*time.Millisecond, ch) }()
77 time.Sleep(100 * time.Millisecond)
78 if err := os.Mkdir(dir, 0o755); err != nil {
79 t.Fatal(err)
80 }
81 time.Sleep(2500 * time.Millisecond) // the missing directory is looked for every 2 s
82 if err := os.WriteFile(filepath.Join(dir, "a.sh"), []byte("x"), 0o644); err != nil {
83 t.Fatal(err)
84 }
85 if !waitSignal(ch, 2*time.Second) {
86 t.Fatal("a directory that appeared later was not watched")
87 }
88}