Repository: fsnotify/fsnotify
Pull request #626 (2024-04-27 18:07:08)

Add some more tests
Add some tests I thought of; and enable running the recursive tests.


Patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e40ff64..ae4613f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -73,6 +73,8 @@ with `nowait` to skip this:
 ### Supported commands
 
     watch path          # Watch the path, reporting events for it.
+    unwatch path        # Stop watching the path.
+    watchlist n         # Assert watchlist length.
 
     touch path
     mkdir [-p] dir
diff --git a/backend_windows.go b/backend_windows.go
index 446a890..81d3744 100644
--- a/backend_windows.go
+++ b/backend_windows.go
@@ -495,8 +495,7 @@ func (m watchMap) set(ino *inode, watch *watch) {
 
 // Must run within the I/O thread.
 func (w *Watcher) addWatch(pathname string, flags uint64, bufsize int) error {
-	//pathname, recurse := recursivePath(pathname)
-	recurse := false
+	pathname, recurse := recursivePath(pathname)
 
 	dir, err := w.getDir(pathname)
 	if err != nil {
diff --git a/fsnotify.go b/fsnotify.go
index df699a6..f99245b 100644
--- a/fsnotify.go
+++ b/fsnotify.go
@@ -171,9 +171,14 @@ func WithBufferSize(bytes int) addOpt {
 	return func(opt *withOpts) { opt.bufsize = bytes }
 }
 
+var enableRecurse = false
+
 // Check if this path is recursive (ends with "/..." or "\..."), and return the
 // path with the /... stripped.
 func recursivePath(path string) (string, bool) {
+	if !enableRecurse { // Only enabled in tests for now.
+		return path, false
+	}
 	if filepath.Base(path) == "..." {
 		return filepath.Dir(path), true
 	}


Test patch:
diff --git a/fsnotify_test.go b/fsnotify_test.go
index 41bc47f..62b6526 100644
--- a/fsnotify_test.go
+++ b/fsnotify_test.go
@@ -26,6 +26,7 @@ import (
 // https://go-review.googlesource.com/c/go/+/393354/
 func init() {
 	internal.SetRlimit()
+	enableRecurse = true
 }
 
 func TestScript(t *testing.T) {
diff --git a/helpers_test.go b/helpers_test.go
index 66a7a43..a4fb3a3 100644
--- a/helpers_test.go
+++ b/helpers_test.go
@@ -614,8 +614,8 @@ func isSolaris() bool {
 
 func recurseOnly(t *testing.T) {
 	switch runtime.GOOS {
-	//case "windows":
-	// Run test.
+	case "windows":
+		// Run test.
 	default:
 		t.Skip("recursion not yet supported on " + runtime.GOOS)
 	}
@@ -716,6 +716,8 @@ func parseScript(t *testing.T, in string) {
 		case "skip", "require":
 			mustArg(c, 1)
 			switch c.args[0] {
+			case "always":
+				t.Skip()
 			case "symlink":
 				if !internal.HasPrivilegesForSymlink() {
 					t.Skipf("%s symlink: admin permissions required on Windows", c.cmd)
@@ -753,6 +755,21 @@ func parseScript(t *testing.T, in string) {
 		case "watch":
 			mustArg(c, 1)
 			do = append(do, func() { addWatch(t, w.w, tmppath(tmp, c.args[0])) })
+		case "unwatch":
+			mustArg(c, 1)
+			do = append(do, func() { rmWatch(t, w.w, tmppath(tmp, c.args[0])) })
+		case "watchlist":
+			mustArg(c, 1)
+			n, err := strconv.ParseInt(c.args[0], 10, 0)
+			if err != nil {
+				t.Fatalf("line %d: %s", c.line, err)
+			}
+			do = append(do, func() {
+				wl := w.w.WatchList()
+				if l := int64(len(wl)); l != n {
+					t.Errorf("line %d: watchlist has %d entries, not %d\n%q", c.line, l, n, wl)
+				}
+			})
 		case "touch":
 			mustArg(c, 1)
 			do = append(do, func() { touch(t, tmppath(tmp, c.args[0])) })
diff --git a/testdata/watch-dir/make-file-unreadable b/testdata/watch-dir/make-file-unreadable
new file mode 100644
index 0000000..259e964
--- /dev/null
+++ b/testdata/watch-dir/make-file-unreadable
@@ -0,0 +1,19 @@
+# Make file unreadable after watch.
+skip windows  # TODO: figure out how to make a file unreadable
+
+touch /unreadable
+touch /file
+watch /
+
+chmod 0 /unreadable
+
+echo hello >>/file
+
+rm /file
+rm /unreadable
+
+Output:
+	chmod     /unreadable
+	write     /file
+	remove    /file
+	remove    /unreadable
diff --git a/testdata/watch-dir/remove-symlink b/testdata/watch-dir/remove-symlink
new file mode 100644
index 0000000..a6ec023
--- /dev/null
+++ b/testdata/watch-dir/remove-symlink
@@ -0,0 +1,14 @@
+# Remove a symlink.
+
+touch /file
+ln -s /file /link
+
+watch /
+
+rm /link
+
+Output:
+	remove /link
+
+	kqueue:  # TODO: this is broken.
+		no-events
diff --git a/testdata/watch-dir/rename-symlink b/testdata/watch-dir/rename-symlink
new file mode 100644
index 0000000..36caae6
--- /dev/null
+++ b/testdata/watch-dir/rename-symlink
@@ -0,0 +1,16 @@
+# Rename a symlink.
+require symlink
+
+touch /file
+ln -s /file /link
+
+watch /
+
+mv /link /link-rename
+
+Output:
+	rename /link
+	create /link-rename
+
+	kqueue:  # TODO: this is broken.
+		create /link-rename
diff --git a/testdata/watch-dir/watch-file-inside-watched-dir b/testdata/watch-dir/watch-file-inside-watched-dir
new file mode 100644
index 0000000..3e5e7cb
--- /dev/null
+++ b/testdata/watch-dir/watch-file-inside-watched-dir
@@ -0,0 +1,30 @@
+# Add a new file watch for a directory we're already watching.
+
+watch /
+
+touch /file
+watch /file
+
+echo hello >>/file
+
+rm /file
+
+Output:
+	create /file
+	write  /file
+	remove /file
+
+	linux:  # TODO: double write.
+		create   /file
+		write    /file
+		write    /file
+
+		chmod    /file
+		remove   /file
+	windows:  # TODO: double write and remove.
+		create   /file
+		write    /file
+		write    /file
+
+		remove   /file
+		remove   /file
diff --git a/testdata/watch-recurse/add-dir b/testdata/watch-recurse/add-dir
index 752644f..879af32 100644
--- a/testdata/watch-recurse/add-dir
+++ b/testdata/watch-recurse/add-dir
@@ -1,19 +1,29 @@
-# Add directory
+# Add directory to a recursively watched dir.
 require recurse
 
-mkdir -p /one/two/three/four
+mkdir -p /sub/dir
 watch /...
 
-mkdir -p /one/two/new/dir
-touch /one/two/new/file
-touch /one/two/new/dir/file
+mkdir -p /sub/dir/newdir
+touch /sub/dir/newdir/file
+rm /sub/dir/newdir/file
+
+# Re-create file.
+touch /sub/dir/newdir/file
+rm /sub/dir/newdir/file
+
+# Write to file.
+echo foo >/sub/dir/newdir/file
 
 Output:
-	write     /one/two                # mkdir -p one/two/new/dir
-	create    /one/two/new
-	create    /one/two/new/dir
+	create   /sub/dir/newdir
+	create   /sub/dir/newdir/file
+	remove   /sub/dir/newdir/file
 
-	write     /one/two/new            # touch one/two/new/file
-	create    /one/two/new/file
+	write    /sub/dir/newdir
+	create   /sub/dir/newdir/file
+	remove   /sub/dir/newdir/file
 
-	create    /one/two/new/dir/file   # touch one/two/new/dir/file
+	write    /sub/dir/newdir
+	create   /sub/dir/newdir/file
+	write    /sub/dir/newdir/file
diff --git a/testdata/watch-recurse/add-file b/testdata/watch-recurse/add-file
new file mode 100644
index 0000000..9dd6d0a
--- /dev/null
+++ b/testdata/watch-recurse/add-file
@@ -0,0 +1,21 @@
+# Make a nested directory tree, then write some files there.
+require recurse
+
+mkdir -p /sub/dir
+watch /...
+
+echo asd >/file
+echo asd >/sub/dir/file
+
+rm /file
+rm /sub/dir/file
+
+Output:
+	create  /file
+	write   /file
+
+	create  /sub/dir/file
+	write   /sub/dir/file
+
+	remove  /file
+	remove  /sub/dir/file
diff --git a/testdata/watch-recurse/basic b/testdata/watch-recurse/basic
deleted file mode 100644
index 666c4af..0000000
--- a/testdata/watch-recurse/basic
+++ /dev/null
@@ -1,16 +0,0 @@
-# Make a nested directory tree, then write some files there.
-require recurse
-
-mkdir -p /one/two/three/four
-watch /...
-
-echo asd >/file.txt
-echo asd >/one/two/three/file.txt
-
-Output:
-	create    /file.txt                  # echo asd >file.txt
-	write     /file.txt
-
-	write     /one/two/three             # echo asd >one/two/three/file.txt
-	create    /one/two/three/file.txt
-	write     /one/two/three/file.txt
diff --git a/testdata/watch-recurse/remove-dir b/testdata/watch-recurse/remove-dir
index 7ff177d..6c42bf6 100644
--- a/testdata/watch-recurse/remove-dir
+++ b/testdata/watch-recurse/remove-dir
@@ -1,22 +1,20 @@
 # Remove nested directory
 require recurse
 
-mkdir -p one/two/three/four
+mkdir -p /sub/dir
 watch /...
 
-echo asd >/one/two/three/file.txt
-rm -r one/two
+echo asd >/sub/dir/file
+rm -r /sub
 
 Output:
-	write    /one/two/three            # echo asd >/one/two/three/file.txt
-	create   /one/two/three/file.txt
-	write    /one/two/three/file.txt
+	create   /sub/dir/file
+	write    /sub/dir/file
 
-	write    /one/two                  # rm -r one/two
-	write    /one/two/three
-	remove   /one/two/three/file.txt
-	remove   /one/two/three/four
-	write    /one/two/three
-	remove   /one/two/three
-	write    /one/two
-	remove   /one/two
+	write  /sub
+	write  /sub/dir
+	remove /sub/dir/file
+	write  /sub/dir
+	remove /sub/dir
+	write  /sub
+	remove /sub
diff --git a/testdata/watch-recurse/remove-recursive b/testdata/watch-recurse/remove-recursive
index 4560bf1..06433a8 100644
--- a/testdata/watch-recurse/remove-recursive
+++ b/testdata/watch-recurse/remove-recursive
@@ -8,21 +8,16 @@ touch /dir2/subdir/file
 
 watch /dir1/...
 watch /dir2/...
-echo asd /dir1/subdir/file
-echo asd /dir2/subdir/file
+echo asd >>/dir1/subdir/file
+echo asd >>/dir2/subdir/file
 
-#if err := w.Remove(join(tmp, "dir1")); err != nil {
-#	t.Fatal(err)
-#}
-#if err := w.Remove(join(tmp, "dir2", "...")); err != nil {
-#	t.Fatal(err)
-#}
-#if w := w.WatchList(); len(w) != 0 {
-#	t.Errorf("WatchList not empty: %s", w)
-#}
+watchlist 2
+unwatch /dir1
+unwatch /dir2
+watchlist 0
 
-echo asd /dir1/subdir/file
-echo asd /dir2/subdir/file
+echo asd >>/dir1/subdir/file
+echo asd >>/dir2/subdir/file
 
 Output:
 	write /dir1/subdir
diff --git a/testdata/watch-recurse/remove-watched-dir b/testdata/watch-recurse/remove-watched-dir
index 88fedf3..cd38f0b 100644
--- a/testdata/watch-recurse/remove-watched-dir
+++ b/testdata/watch-recurse/remove-watched-dir
@@ -1,4 +1,4 @@
-# Remove watched directory
+# Remove watched directory.
 require recurse
 
 mkdir /watch
@@ -21,23 +21,23 @@ watch /watch/...
 rm -r /watch
 
 Output:
-	remove      /a
-	remove      /b
-	remove      /c
-	remove      /d
-	remove      /e
-	remove      /f
-	remove      /g
-	write       /h
-	remove      /h/a
-	write       /h
-	remove      /h
-	write       /i
-	remove      /i/a
-	write       /i
-	remove      /i
-	write       /j
-	remove      /j/a
-	write       /j
-	remove      /j
-	remove      /
+	remove      /watch/a
+	remove      /watch/b
+	remove      /watch/c
+	remove      /watch/d
+	remove      /watch/e
+	remove      /watch/f
+	remove      /watch/g
+	write       /watch/h
+	remove      /watch/h/a
+	write       /watch/h
+	remove      /watch/h
+	write       /watch/i
+	remove      /watch/i/a
+	write       /watch/i
+	remove      /watch/i
+	write       /watch/j
+	remove      /watch/j/a
+	write       /watch/j
+	remove      /watch/j
+	remove      /watch
diff --git a/testdata/watch-recurse/rename-dir b/testdata/watch-recurse/rename-dir
index 56b0ad7..5aa8ed4 100644
--- a/testdata/watch-recurse/rename-dir
+++ b/testdata/watch-recurse/rename-dir
@@ -1,19 +1,18 @@
-# Rename nested directory
+# Rename nested directory.
 require recurse
 
-mkdir -p /one/two/three/four
+mkdir -p /sub/dir
 watch /...
 
-mv /one /one-rename
-touch /one-rename/file
-touch /one-rename/two/three/file
+mv /sub /sub-rename
+touch /sub-rename/file
+touch /sub-rename/dir/file
 
 Output:
-	rename     /one                        # mv one one-rename
-	create     /one-rename
+	rename     /sub                  # mv /sub /sub-rename
+	create     /sub-rename
 
-	write      /one-rename                 # touch one-rename/file
-	create     /one-rename/file
+	write      /sub-rename           # touch /sub-rename/file
+	create     /sub-rename/file
 
-	write      /one-rename/two/three       # touch one-rename/two/three/file
-	create     /one-rename/two/three/file
+	create     /sub-rename/dir/file  # touch /sub-rename/dir/file