1 // Copyright 2015 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
18 func TestInotifyCloseRightAway(t *testing.T) {
19 w, err := NewWatcher()
21 t.Fatalf("Failed to create watcher")
24 // Close immediately; it won't even reach the first unix.Read.
27 // Wait for the close to complete.
28 <-time.After(50 * time.Millisecond)
29 isWatcherReallyClosed(t, w)
32 func TestInotifyCloseSlightlyLater(t *testing.T) {
33 w, err := NewWatcher()
35 t.Fatalf("Failed to create watcher")
38 // Wait until readEvents has reached unix.Read, and Close.
39 <-time.After(50 * time.Millisecond)
42 // Wait for the close to complete.
43 <-time.After(50 * time.Millisecond)
44 isWatcherReallyClosed(t, w)
47 func TestInotifyCloseSlightlyLaterWithWatch(t *testing.T) {
48 testDir := tempMkdir(t)
49 defer os.RemoveAll(testDir)
51 w, err := NewWatcher()
53 t.Fatalf("Failed to create watcher")
57 // Wait until readEvents has reached unix.Read, and Close.
58 <-time.After(50 * time.Millisecond)
61 // Wait for the close to complete.
62 <-time.After(50 * time.Millisecond)
63 isWatcherReallyClosed(t, w)
66 func TestInotifyCloseAfterRead(t *testing.T) {
67 testDir := tempMkdir(t)
68 defer os.RemoveAll(testDir)
70 w, err := NewWatcher()
72 t.Fatalf("Failed to create watcher")
77 t.Fatalf("Failed to add .")
81 os.Create(filepath.Join(testDir, "somethingSOMETHINGsomethingSOMETHING"))
83 // Wait for readEvents to read the event, then close the watcher.
84 <-time.After(50 * time.Millisecond)
87 // Wait for the close to complete.
88 <-time.After(50 * time.Millisecond)
89 isWatcherReallyClosed(t, w)
92 func isWatcherReallyClosed(t *testing.T, w *Watcher) {
94 case err, ok := <-w.Errors:
96 t.Fatalf("w.Errors is not closed; readEvents is still alive after closing (error: %v)", err)
99 t.Fatalf("w.Errors would have blocked; readEvents is still alive!")
103 case _, ok := <-w.Events:
105 t.Fatalf("w.Events is not closed; readEvents is still alive after closing")
108 t.Fatalf("w.Events would have blocked; readEvents is still alive!")
112 func TestInotifyCloseCreate(t *testing.T) {
113 testDir := tempMkdir(t)
114 defer os.RemoveAll(testDir)
116 w, err := NewWatcher()
118 t.Fatalf("Failed to create watcher: %v", err)
124 t.Fatalf("Failed to add testDir: %v", err)
126 h, err := os.Create(filepath.Join(testDir, "testfile"))
128 t.Fatalf("Failed to create file in testdir: %v", err)
133 case err := <-w.Errors:
134 t.Fatalf("Error from watcher: %v", err)
135 case <-time.After(50 * time.Millisecond):
136 t.Fatalf("Took too long to wait for event")
139 // At this point, we've received one event, so the goroutine is ready.
140 // It's also blocking on unix.Read.
141 // Now we try to swap the file descriptor under its nose.
143 w, err = NewWatcher()
146 t.Fatalf("Failed to create second watcher: %v", err)
149 <-time.After(50 * time.Millisecond)
152 t.Fatalf("Error adding testDir again: %v", err)
156 // This test verifies the watcher can keep up with file creations/deletions
158 func TestInotifyStress(t *testing.T) {
159 maxNumToCreate := 1000
161 testDir := tempMkdir(t)
162 defer os.RemoveAll(testDir)
163 testFilePrefix := filepath.Join(testDir, "testfile")
165 w, err := NewWatcher()
167 t.Fatalf("Failed to create watcher: %v", err)
173 t.Fatalf("Failed to add testDir: %v", err)
176 doneChan := make(chan struct{})
177 // The buffer ensures that the file generation goroutine is never blocked.
178 errChan := make(chan error, 2*maxNumToCreate)
181 for i := 0; i < maxNumToCreate; i++ {
182 testFile := fmt.Sprintf("%s%d", testFilePrefix, i)
184 handle, err := os.Create(testFile)
186 errChan <- fmt.Errorf("Create failed: %v", err)
192 errChan <- fmt.Errorf("Close failed: %v", err)
197 // If we delete a newly created file too quickly, inotify will skip the
198 // create event and only send the delete event.
199 time.Sleep(100 * time.Millisecond)
201 for i := 0; i < maxNumToCreate; i++ {
202 testFile := fmt.Sprintf("%s%d", testFilePrefix, i)
203 err = os.Remove(testFile)
205 errChan <- fmt.Errorf("Remove failed: %v", err)
216 after := time.After(10 * time.Second)
223 case err := <-errChan:
224 t.Fatalf("Got an error from file creator goroutine: %v", err)
225 case err := <-w.Errors:
226 t.Fatalf("Got an error from watcher: %v", err)
227 case evt := <-w.Events:
228 if !strings.HasPrefix(evt.Name, testFilePrefix) {
229 t.Fatalf("Got an event for an unknown file: %s", evt.Name)
231 if evt.Op == Create {
234 if evt.Op == Remove {
240 // Drain remaining events from channels
244 case err := <-errChan:
245 t.Fatalf("Got an error from file creator goroutine: %v", err)
246 case err := <-w.Errors:
247 t.Fatalf("Got an error from watcher: %v", err)
248 case evt := <-w.Events:
249 if !strings.HasPrefix(evt.Name, testFilePrefix) {
250 t.Fatalf("Got an event for an unknown file: %s", evt.Name)
252 if evt.Op == Create {
255 if evt.Op == Remove {
261 // Give the watcher chances to fill the channels.
262 time.Sleep(time.Millisecond)
266 if creates-removes > 1 || creates-removes < -1 {
267 t.Fatalf("Creates and removes should not be off by more than one: %d creates, %d removes", creates, removes)
270 t.Fatalf("Expected at least 50 creates, got %d", creates)
274 func TestInotifyRemoveTwice(t *testing.T) {
275 testDir := tempMkdir(t)
276 defer os.RemoveAll(testDir)
277 testFile := filepath.Join(testDir, "testfile")
279 handle, err := os.Create(testFile)
281 t.Fatalf("Create failed: %v", err)
285 w, err := NewWatcher()
287 t.Fatalf("Failed to create watcher: %v", err)
291 err = w.Add(testFile)
293 t.Fatalf("Failed to add testFile: %v", err)
296 err = w.Remove(testFile)
298 t.Fatalf("wanted successful remove but got:", err)
301 err = w.Remove(testFile)
303 t.Fatalf("no error on removing invalid file")
308 if len(w.watches) != 0 {
309 t.Fatalf("Expected watches len is 0, but got: %d, %v", len(w.watches), w.watches)
311 if len(w.paths) != 0 {
312 t.Fatalf("Expected paths len is 0, but got: %d, %v", len(w.paths), w.paths)
316 func TestInotifyInnerMapLength(t *testing.T) {
317 testDir := tempMkdir(t)
318 defer os.RemoveAll(testDir)
319 testFile := filepath.Join(testDir, "testfile")
321 handle, err := os.Create(testFile)
323 t.Fatalf("Create failed: %v", err)
327 w, err := NewWatcher()
329 t.Fatalf("Failed to create watcher: %v", err)
333 err = w.Add(testFile)
335 t.Fatalf("Failed to add testFile: %v", err)
338 for err := range w.Errors {
339 t.Fatalf("error received: %s", err)
343 err = os.Remove(testFile)
345 t.Fatalf("Failed to remove testFile: %v", err)
347 _ = <-w.Events // consume Remove event
348 <-time.After(50 * time.Millisecond) // wait IN_IGNORE propagated
352 if len(w.watches) != 0 {
353 t.Fatalf("Expected watches len is 0, but got: %d, %v", len(w.watches), w.watches)
355 if len(w.paths) != 0 {
356 t.Fatalf("Expected paths len is 0, but got: %d, %v", len(w.paths), w.paths)
360 func TestInotifyOverflow(t *testing.T) {
361 // We need to generate many more events than the
362 // fs.inotify.max_queued_events sysctl setting.
363 // We use multiple goroutines (one per directory)
364 // to speed up file creation.
368 testDir := tempMkdir(t)
369 defer os.RemoveAll(testDir)
371 w, err := NewWatcher()
373 t.Fatalf("Failed to create watcher: %v", err)
377 for dn := 0; dn < numDirs; dn++ {
378 testSubdir := fmt.Sprintf("%s/%d", testDir, dn)
380 err := os.Mkdir(testSubdir, 0777)
382 t.Fatalf("Cannot create subdir: %v", err)
385 err = w.Add(testSubdir)
387 t.Fatalf("Failed to add subdir: %v", err)
391 errChan := make(chan error, numDirs*numFiles)
393 for dn := 0; dn < numDirs; dn++ {
394 testSubdir := fmt.Sprintf("%s/%d", testDir, dn)
397 for fn := 0; fn < numFiles; fn++ {
398 testFile := fmt.Sprintf("%s/%d", testSubdir, fn)
400 handle, err := os.Create(testFile)
402 errChan <- fmt.Errorf("Create failed: %v", err)
408 errChan <- fmt.Errorf("Close failed: %v", err)
418 after := time.After(10 * time.Second)
419 for overflows == 0 && creates < numDirs*numFiles {
423 case err := <-errChan:
424 t.Fatalf("Got an error from file creator goroutine: %v", err)
425 case err := <-w.Errors:
426 if err == ErrEventOverflow {
429 t.Fatalf("Got an error from watcher: %v", err)
431 case evt := <-w.Events:
432 if !strings.HasPrefix(evt.Name, testDir) {
433 t.Fatalf("Got an event for an unknown file: %s", evt.Name)
435 if evt.Op == Create {
441 if creates == numDirs*numFiles {
442 t.Fatalf("Could not trigger overflow")
446 t.Fatalf("No overflow and not enough creates (expected %d, got %d)",
447 numDirs*numFiles, creates)