github.com/mutagen-io/mutagen@v0.18.0-rc1/pkg/filesystem/watching/watch_test.go (about) 1 package watching 2 3 import ( 4 "os" 5 "path/filepath" 6 "runtime" 7 "testing" 8 "time" 9 ) 10 11 const ( 12 // maximumEventWaitTime is the maximum amount of time that verifyWatchEvent 13 // will wait for an event to be received. 14 maximumEventWaitTime = 5 * time.Second 15 ) 16 17 // verifyWatchEvent is a helper function to verify that events are received by a 18 // watcher. It accepts a RecursiveWatcher, but can also be used for a 19 // NonRecursiveWatcher (since the interface semantics are compatible). The paths 20 // map may be modified by this function and should thus not be reused. 21 func verifyWatchEvent(t *testing.T, watcher RecursiveWatcher, paths map[string]bool) { 22 // Indicate that this is a helper function. 23 t.Helper() 24 25 // Create a deadline for event reception and ensure its cancellation. 26 deadline := time.NewTimer(maximumEventWaitTime) 27 defer deadline.Stop() 28 29 // Perform the waiting operation. 30 for len(paths) > 0 { 31 select { 32 case path := <-watcher.Events(): 33 delete(paths, path) 34 case err := <-watcher.Errors(): 35 t.Fatal("watcher error:", err) 36 case <-deadline.C: 37 t.Fatal("event reception deadline exceeded:", paths) 38 } 39 } 40 } 41 42 // TestRecursiveWatcher tests the platform's RecursiveWatcher implementation (if 43 // any) with a simple set of filesystem operations. 44 func TestRecursiveWatcher(t *testing.T) { 45 // Skip this test if recursive watchig is unsupported. 46 if !RecursiveWatchingSupported { 47 t.Skip() 48 } 49 50 // Create a temporary directory (that will be automatically removed). 51 directory := t.TempDir() 52 53 // Create the watcher and defer its termination. 54 watcher, err := NewRecursiveWatcher(directory) 55 if err != nil { 56 t.Fatal("unable to establish watch:", err) 57 } 58 defer watcher.Terminate() 59 60 // Create a subdirectory. 61 subdirectoryRelative := "subdirectory" 62 subdirectoryAbsolute := filepath.Join(directory, subdirectoryRelative) 63 if err := os.Mkdir(subdirectoryAbsolute, 0700); err != nil { 64 t.Fatal("unable to create subdirectory:", err) 65 } 66 verifyWatchEvent(t, watcher, map[string]bool{subdirectoryRelative: true}) 67 68 // Create a file inside the subdirectory. 69 fileRelative := "subdirectory/file" 70 fileAbsolute := filepath.Join(directory, fileRelative) 71 if err := os.WriteFile(fileAbsolute, nil, 0600); err != nil { 72 t.Fatal("unable to create test file:", err) 73 } 74 verifyWatchEvent(t, watcher, map[string]bool{fileRelative: true}) 75 76 // Modify the test file. 77 if err := os.WriteFile(fileAbsolute, []byte("data"), 0600); err != nil { 78 t.Fatal("unable to modify test file:", err) 79 } 80 verifyWatchEvent(t, watcher, map[string]bool{fileRelative: true}) 81 82 // If we're not on Windows, test that we detect permissions changes. 83 if runtime.GOOS != "windows" { 84 if err := os.Chmod(fileAbsolute, 0700); err != nil { 85 t.Fatal("unable to change file permissions:", err) 86 } 87 verifyWatchEvent(t, watcher, map[string]bool{fileRelative: true}) 88 } 89 90 // Remove the test file. 91 if err := os.Remove(fileAbsolute); err != nil { 92 t.Fatal("unable to remove test file:", err) 93 } 94 verifyWatchEvent(t, watcher, map[string]bool{fileRelative: true}) 95 } 96 97 // TestNonRecursiveWatcher tests the platform's NonRecursiveWatcher 98 // implementation (if any) with a simple set of filesystem operations. 99 func TestNonRecursiveWatcher(t *testing.T) { 100 // Skip this test if non-recursive watchig is unsupported. 101 if !NonRecursiveWatchingSupported { 102 t.Skip() 103 } 104 105 // Create a temporary directory (that will be automatically removed). 106 directory := t.TempDir() 107 108 // Create the watcher and defer its termination. 109 watcher, err := NewNonRecursiveWatcher() 110 if err != nil { 111 t.Fatal("unable to create watcher:", err) 112 } 113 watcher.Watch(directory) 114 defer watcher.Terminate() 115 116 // Create a subdirectory. 117 subdirectoryPath := filepath.Join(directory, "subdirectory") 118 if err := os.Mkdir(subdirectoryPath, 0700); err != nil { 119 t.Fatal("unable to create subdirectory:", err) 120 } 121 verifyWatchEvent(t, watcher, map[string]bool{subdirectoryPath: true}) 122 123 // Create a file. 124 filePath := filepath.Join(directory, "file") 125 if err := os.WriteFile(filePath, nil, 0600); err != nil { 126 t.Fatal("unable to create test file:", err) 127 } 128 verifyWatchEvent(t, watcher, map[string]bool{filePath: true}) 129 130 // Modify the test file. 131 if err := os.WriteFile(filePath, []byte("data"), 0600); err != nil { 132 t.Fatal("unable to modify test file:", err) 133 } 134 verifyWatchEvent(t, watcher, map[string]bool{filePath: true}) 135 136 // If we're not on Windows, test that we detect permissions changes. 137 if runtime.GOOS != "windows" { 138 if err := os.Chmod(filePath, 0700); err != nil { 139 t.Fatal("unable to change file permissions:", err) 140 } 141 verifyWatchEvent(t, watcher, map[string]bool{filePath: true}) 142 } 143 144 // Remove the test file. 145 if err := os.Remove(filePath); err != nil { 146 t.Fatal("unable to remove test file:", err) 147 } 148 verifyWatchEvent(t, watcher, map[string]bool{filePath: true}) 149 }