blob: 43db5217ca0ccd394aa4ed316b638dfabb32de00 [file] [log] [blame]
//go:build linux
package fsnotify
import (
"errors"
"os"
"strconv"
"strings"
"sync"
"testing"
"time"
)
func TestRemoveState(t *testing.T) {
var (
tmp = t.TempDir()
dir = join(tmp, "dir")
file = join(dir, "file")
)
mkdir(t, dir)
touch(t, file)
w := newWatcher(t, tmp)
addWatch(t, w, tmp)
addWatch(t, w, file)
check := func(want int) {
t.Helper()
if w.b.(*inotify).watches.len() != want {
t.Error(w.b.(*inotify).watches)
}
}
check(2)
if err := w.Remove(file); err != nil {
t.Fatal(err)
}
check(1)
if err := w.Remove(tmp); err != nil {
t.Fatal(err)
}
check(0)
}
// Ensure that the correct error is returned on overflows.
func TestInotifyOverflow(t *testing.T) {
t.Parallel()
tmp := t.TempDir()
w := newWatcher(t)
defer w.Close()
// We need to generate many more events than the
// fs.inotify.max_queued_events sysctl setting.
numDirs, numFiles := 128, 1024
// All events need to be in the inotify queue before pulling events off it
// to trigger this error.
var wg sync.WaitGroup
for i := 0; i < numDirs; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
dir := join(tmp, strconv.Itoa(i))
mkdir(t, dir, noWait)
addWatch(t, w, dir)
createFiles(t, dir, "", numFiles, 10*time.Second)
}(i)
}
wg.Wait()
var (
creates = 0
overflows = 0
)
for overflows == 0 && creates < numDirs*numFiles {
select {
case <-time.After(10 * time.Second):
t.Fatalf("Not done")
case err := <-w.Errors:
if !errors.Is(err, ErrEventOverflow) {
t.Fatalf("unexpected error from watcher: %v", err)
}
overflows++
case e := <-w.Events:
if !strings.HasPrefix(e.Name, tmp) {
t.Fatalf("Event for unknown file: %s", e.Name)
}
if e.Op == Create {
creates++
}
}
}
if creates == numDirs*numFiles {
t.Fatalf("could not trigger overflow")
}
if overflows == 0 {
t.Fatalf("no overflow and not enough CREATE events (expected %d, got %d)",
numDirs*numFiles, creates)
}
}
// Test inotify's "we don't send REMOVE until all file descriptors are removed"
// behaviour.
func TestInotifyDeleteOpenFile(t *testing.T) {
t.Parallel()
tmp := t.TempDir()
file := join(tmp, "file")
touch(t, file)
fp, err := os.Open(file)
if err != nil {
t.Fatalf("Create failed: %v", err)
}
defer fp.Close()
w := newCollector(t, file)
w.collect(t)
rm(t, file)
waitForEvents()
e := w.events(t)
cmpEvents(t, tmp, e, newEvents(t, `chmod /file`))
fp.Close()
e = w.stop(t)
cmpEvents(t, tmp, e, newEvents(t, `remove /file`))
}