github.com/misseven0/notify@v0.0.0-20230519123055-c1422e46da05/notify_test.go (about) 1 // Copyright (c) 2014-2015 The Notify Authors. All rights reserved. 2 // Use of this source code is governed by the MIT license that can be 3 // found in the LICENSE file. 4 5 //go:build darwin || linux || freebsd || dragonfly || netbsd || openbsd || windows || solaris 6 // +build darwin linux freebsd dragonfly netbsd openbsd windows solaris 7 8 package notify 9 10 import ( 11 "errors" 12 "os" 13 "path/filepath" 14 "testing" 15 "time" 16 ) 17 18 func TestNotifyExample(t *testing.T) { 19 n := NewNotifyTest(t, "testdata/vfs.txt") 20 21 ch := NewChans(3) 22 23 // Watch-points can be set explicitly via Watch/Stop calls... 24 n.Watch("src/github.com/rjeczalik/fs", ch[0], Write) 25 n.Watch("src/github.com/pblaszczyk/qttu", ch[0], Write) 26 n.Watch("src/github.com/pblaszczyk/qttu/...", ch[1], Create) 27 n.Watch("src/github.com/rjeczalik/fs/cmd/...", ch[2], Remove) 28 29 cases := []NCase{ 30 // i=0 31 { 32 Event: write(n.W(), "src/github.com/rjeczalik/fs/fs.go", []byte("XD")), 33 Receiver: Chans{ch[0]}, 34 }, 35 // TODO(rjeczalik): #62 36 // i=1 37 // { 38 // Event: write(n.W(), "src/github.com/pblaszczyk/qttu/README.md", []byte("XD")), 39 // Receiver: Chans{ch[0]}, 40 // }, 41 // i=2 42 { 43 Event: write(n.W(), "src/github.com/rjeczalik/fs/cmd/gotree/go.go", []byte("XD")), 44 Receiver: nil, 45 }, 46 // i=3 47 { 48 Event: create(n.W(), "src/github.com/pblaszczyk/qttu/src/.main.cc.swp"), 49 Receiver: Chans{ch[1]}, 50 }, 51 // i=4 52 { 53 Event: create(n.W(), "src/github.com/pblaszczyk/qttu/src/.main.cc.swo"), 54 Receiver: Chans{ch[1]}, 55 }, 56 // i=5 57 { 58 Event: remove(n.W(), "src/github.com/rjeczalik/fs/cmd/gotree/go.go"), 59 Receiver: Chans{ch[2]}, 60 }, 61 } 62 63 n.ExpectNotifyEvents(cases, ch) 64 65 // ...or using Call structures. 66 stops := [...]Call{ 67 // i=0 68 { 69 F: FuncStop, 70 C: ch[0], 71 }, 72 // i=1 73 { 74 F: FuncStop, 75 C: ch[1], 76 }, 77 } 78 79 n.Call(stops[:]...) 80 81 cases = []NCase{ 82 // i=0 83 { 84 Event: write(n.W(), "src/github.com/rjeczalik/fs/fs.go", []byte("XD")), 85 Receiver: nil, 86 }, 87 // i=1 88 { 89 Event: write(n.W(), "src/github.com/pblaszczyk/qttu/README.md", []byte("XD")), 90 Receiver: nil, 91 }, 92 // i=2 93 { 94 Event: create(n.W(), "src/github.com/pblaszczyk/qttu/src/.main.cc.swr"), 95 Receiver: nil, 96 }, 97 // i=3 98 { 99 Event: remove(n.W(), "src/github.com/rjeczalik/fs/cmd/gotree/main.go"), 100 Receiver: Chans{ch[2]}, 101 }, 102 } 103 104 n.ExpectNotifyEvents(cases, ch) 105 } 106 107 func TestStop(t *testing.T) { 108 t.Skip("TODO(rjeczalik)") 109 } 110 111 func TestRenameInRoot(t *testing.T) { 112 tmpDir := t.TempDir() 113 114 c := make(chan EventInfo, 100) 115 first := filepath.Join(tmpDir, "foo") 116 second := filepath.Join(tmpDir, "bar") 117 file := filepath.Join(second, "file") 118 119 mustT(t, os.Mkdir(first, 0777)) 120 121 if err := Watch(tmpDir+"/...", c, All); err != nil { 122 t.Fatal(err) 123 } 124 defer Stop(c) 125 126 mustT(t, os.Rename(first, second)) 127 time.Sleep(50 * time.Millisecond) // Need some time to process rename. 128 fd, err := os.Create(file) 129 mustT(t, err) 130 fd.Close() 131 132 timeout := time.After(time.Second) 133 for { 134 select { 135 case ev := <-c: 136 if samefile(t, ev.Path(), file) { 137 return 138 } 139 t.Log(ev.Path()) 140 case <-timeout: 141 t.Fatal("timed out before receiving event") 142 } 143 } 144 } 145 146 func TestRecreated(t *testing.T) { 147 tmpDir := t.TempDir() 148 149 dir := filepath.Join(tmpDir, "folder") 150 file := filepath.Join(dir, "file") 151 152 // Start watching 153 eventChan := make(chan EventInfo, 1000) 154 mustT(t, Watch(tmpDir+"/...", eventChan, All)) 155 defer Stop(eventChan) 156 157 recreateFolder := func() { 158 // Give the sync some time to process events 159 mustT(t, os.RemoveAll(dir)) 160 mustT(t, os.Mkdir(dir, 0777)) 161 time.Sleep(100 * time.Millisecond) 162 163 // Create a file 164 mustT(t, os.WriteFile(file, []byte("abc"), 0666)) 165 } 166 timeout := time.After(5 * time.Second) 167 checkCreated := func() { 168 for { 169 select { 170 case ev := <-eventChan: 171 t.Log(ev.Path(), ev.Event()) 172 if samefile(t, ev.Path(), file) && ev.Event() == Create { 173 return 174 } 175 case <-timeout: 176 t.Fatal("timed out before receiving event") 177 } 178 } 179 } 180 181 // 1. Create a folder and a file within it 182 // This will create a node in the internal tree for the subfolder test/folder 183 // Will create a new inotify watch for the folder 184 t.Log("######## First ########") 185 recreateFolder() 186 checkCreated() 187 188 // 2. Create a folder and a file within it again 189 // This will set the events for the subfolder test/folder in the internal tree 190 // Will create a new inotify watch for the folder because events differ 191 t.Log("######## Second ########") 192 recreateFolder() 193 checkCreated() 194 195 // 3. Create a folder and a file within it yet again 196 // This time no new inotify watch will be created, because the events 197 // and node already exist in the internal tree and all subsequent events 198 // are lost, hence there is no event for the created file here anymore 199 t.Log("######## Third ########") 200 recreateFolder() 201 checkCreated() 202 } 203 204 func mustT(t testing.TB, err error) { 205 t.Helper() 206 if err != nil { 207 t.Fatal(err) 208 } 209 } 210 211 func samefile(t *testing.T, p1, p2 string) bool { 212 // The tests sometimes delete files shortly after creating them. 213 // That's expected; ignore stat failures. 214 fi1, err := os.Stat(p1) 215 if errors.Is(err, os.ErrNotExist) { 216 return false 217 } 218 mustT(t, err) 219 fi2, err := os.Stat(p2) 220 if errors.Is(err, os.ErrNotExist) { 221 return false 222 } 223 mustT(t, err) 224 return os.SameFile(fi1, fi2) 225 }