blob: 9e39f0119c55c7875f3383dc492901bfbe62daab [file] [log] [blame]
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fsnotify
import (
"fmt"
"math/rand"
"os"
"os/exec"
"path/filepath"
"runtime"
"sync/atomic"
"testing"
"time"
)
var r *rand.Rand
func init() {
r = rand.New(rand.NewSource(time.Now().UnixNano()))
}
func testTempDir() string {
osTempDir := os.TempDir()
randDir := fmt.Sprintf("%d", r.Int())
return filepath.Join(osTempDir, randDir)
}
// An atomic counter
type counter struct {
val int32
}
func (c *counter) increment() {
cas := atomic.CompareAndSwapInt32
old := atomic.LoadInt32(&c.val)
for swp := cas(&c.val, old, old+1); !swp; swp = cas(&c.val, old, old+1) {
old = atomic.LoadInt32(&c.val)
}
}
func (c *counter) value() int32 {
return atomic.LoadInt32(&c.val)
}
func TestFsnotifyMultipleOperations(t *testing.T) {
// Create an fsnotify watcher instance and initialize it
watcher, err := NewWatcher()
if err != nil {
t.Fatalf("NewWatcher() failed: %s", err)
}
// Receive errors on the error channel on a separate goroutine
go func() {
for err := range watcher.Error {
t.Fatalf("error received: %s", err)
}
}()
var testDir string = testTempDir()
var testDirToMoveFiles string = testTempDir()
// Create directory to watch
if err := os.Mkdir(testDir, 0777); err != nil {
t.Fatalf("failed to create test directory: %s", err)
}
defer os.RemoveAll(testDir)
// Create directory to that's not watched
if err := os.Mkdir(testDirToMoveFiles, 0777); err != nil {
t.Fatalf("failed to create test directory: %s", err)
}
defer os.RemoveAll(testDirToMoveFiles)
var testFile string = filepath.Join(testDir, "TestFsnotifySeq.testfile")
var testFileRenamed string = filepath.Join(testDirToMoveFiles, "TestFsnotifySeqRename.testfile")
// Add a watch for testDir
err = watcher.Watch(testDir)
if err != nil {
t.Fatalf("watcher.Watch() failed: %s", err)
}
// Receive events on the event channel on a separate goroutine
eventstream := watcher.Event
var createReceived, modifyReceived, deleteReceived, renameReceived counter
done := make(chan bool)
go func() {
for event := range eventstream {
// Only count relevant events
if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) {
t.Logf("event received: %s", event)
if event.IsDelete() {
deleteReceived.increment()
}
if event.IsModify() {
modifyReceived.increment()
}
if event.IsCreate() {
createReceived.increment()
}
if event.IsRename() {
renameReceived.increment()
}
} else {
t.Logf("unexpected event received: %s", event)
}
}
done <- true
}()
// Create a file
// This should add at least one event to the fsnotify event queue
var f *os.File
f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
t.Fatalf("creating test file failed: %s", err)
}
f.Sync()
time.Sleep(time.Millisecond)
f.WriteString("data")
f.Sync()
f.Close()
time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
err = testRename(testFile, testFileRenamed)
if err != nil {
t.Fatalf("rename failed: %s", err)
}
// Modify the file outside of the watched dir
f, err = os.Open(testFileRenamed)
if err != nil {
t.Fatalf("open test renamed file failed: %s", err)
}
f.WriteString("data")
f.Sync()
f.Close()
time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
// Recreate the file that was moved
f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
t.Fatalf("creating test file failed: %s", err)
}
f.Close()
time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
// We expect this event to be received almost immediately, but let's wait 500 ms to be sure
time.Sleep(500 * time.Millisecond)
cReceived := createReceived.value()
if cReceived != 2 {
t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", cReceived, 2)
}
mReceived := modifyReceived.value()
if mReceived != 1 {
t.Fatalf("incorrect number of modify events received after 500 ms (%d vs %d)", mReceived, 1)
}
dReceived := deleteReceived.value()
rReceived := renameReceived.value()
if dReceived+rReceived != 1 {
t.Fatalf("incorrect number of rename+delete events received after 500 ms (%d vs %d)", rReceived+dReceived, 1)
}
// Try closing the fsnotify instance
t.Log("calling Close()")
watcher.Close()
t.Log("waiting for the event channel to become closed...")
select {
case <-done:
t.Log("event channel closed")
case <-time.After(2 * time.Second):
t.Fatal("event stream was not closed after 2 seconds")
}
}
func TestFsnotifyMultipleCreates(t *testing.T) {
// Create an fsnotify watcher instance and initialize it
watcher, err := NewWatcher()
if err != nil {
t.Fatalf("NewWatcher() failed: %s", err)
}
// Receive errors on the error channel on a separate goroutine
go func() {
for err := range watcher.Error {
t.Fatalf("error received: %s", err)
}
}()
var testDir string = testTempDir()
// Create directory to watch
if err := os.Mkdir(testDir, 0777); err != nil {
t.Fatalf("failed to create test directory: %s", err)
}
defer os.RemoveAll(testDir)
var testFile string = filepath.Join(testDir, "TestFsnotifySeq.testfile")
// Add a watch for testDir
err = watcher.Watch(testDir)
if err != nil {
t.Fatalf("watcher.Watch() failed: %s", err)
}
// Receive events on the event channel on a separate goroutine
eventstream := watcher.Event
var createReceived, modifyReceived, deleteReceived counter
done := make(chan bool)
go func() {
for event := range eventstream {
// Only count relevant events
if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) {
t.Logf("event received: %s", event)
if event.IsDelete() {
deleteReceived.increment()
}
if event.IsCreate() {
createReceived.increment()
}
if event.IsModify() {
modifyReceived.increment()
}
} else {
t.Logf("unexpected event received: %s", event)
}
}
done <- true
}()
// Create a file
// This should add at least one event to the fsnotify event queue
var f *os.File
f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
t.Fatalf("creating test file failed: %s", err)
}
f.Sync()
time.Sleep(time.Millisecond)
f.WriteString("data")
f.Sync()
f.Close()
time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
os.Remove(testFile)
time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
// Recreate the file
f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
t.Fatalf("creating test file failed: %s", err)
}
f.Close()
time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
// Modify
f, err = os.OpenFile(testFile, os.O_WRONLY, 0666)
if err != nil {
t.Fatalf("creating test file failed: %s", err)
}
f.Sync()
time.Sleep(time.Millisecond)
f.WriteString("data")
f.Sync()
f.Close()
time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
// Modify
f, err = os.OpenFile(testFile, os.O_WRONLY, 0666)
if err != nil {
t.Fatalf("creating test file failed: %s", err)
}
f.Sync()
time.Sleep(time.Millisecond)
f.WriteString("data")
f.Sync()
f.Close()
time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
// We expect this event to be received almost immediately, but let's wait 500 ms to be sure
time.Sleep(500 * time.Millisecond)
cReceived := createReceived.value()
if cReceived != 2 {
t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", cReceived, 2)
}
mReceived := modifyReceived.value()
if mReceived < 3 {
t.Fatalf("incorrect number of modify events received after 500 ms (%d vs atleast %d)", mReceived, 3)
}
dReceived := deleteReceived.value()
if dReceived != 1 {
t.Fatalf("incorrect number of rename+delete events received after 500 ms (%d vs %d)", dReceived, 1)
}
// Try closing the fsnotify instance
t.Log("calling Close()")
watcher.Close()
t.Log("waiting for the event channel to become closed...")
select {
case <-done:
t.Log("event channel closed")
case <-time.After(2 * time.Second):
t.Fatal("event stream was not closed after 2 seconds")
}
}
func TestFsnotifyDirOnly(t *testing.T) {
// Create an fsnotify watcher instance and initialize it
watcher, err := NewWatcher()
if err != nil {
t.Fatalf("NewWatcher() failed: %s", err)
}
var testDir string = testTempDir()
// Create directory to watch
if err := os.Mkdir(testDir, 0777); err != nil {
t.Fatalf("failed to create test directory: %s", err)
}
defer os.RemoveAll(testDir)
// Create a file before watching directory
// This should NOT add any events to the fsnotify event queue
var testFileAlreadyExists string = filepath.Join(testDir, "TestFsnotifyEventsExisting.testfile")
{
var f *os.File
f, err = os.OpenFile(testFileAlreadyExists, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
t.Fatalf("creating test file failed: %s", err)
}
f.Sync()
f.Close()
}
// Add a watch for testDir
err = watcher.Watch(testDir)
if err != nil {
t.Fatalf("watcher.Watch() failed: %s", err)
}
// Receive errors on the error channel on a separate goroutine
go func() {
for err := range watcher.Error {
t.Fatalf("error received: %s", err)
}
}()
var testFile string = filepath.Join(testDir, "TestFsnotifyDirOnly.testfile")
// Receive events on the event channel on a separate goroutine
eventstream := watcher.Event
var createReceived, modifyReceived, deleteReceived counter
done := make(chan bool)
go func() {
for event := range eventstream {
// Only count relevant events
if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) || event.Name == filepath.Clean(testFileAlreadyExists) {
t.Logf("event received: %s", event)
if event.IsDelete() {
deleteReceived.increment()
}
if event.IsModify() {
modifyReceived.increment()
}
if event.IsCreate() {
createReceived.increment()
}
} else {
t.Logf("unexpected event received: %s", event)
}
}
done <- true
}()
// Create a file
// This should add at least one event to the fsnotify event queue
var f *os.File
f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
t.Fatalf("creating test file failed: %s", err)
}
f.Sync()
time.Sleep(time.Millisecond)
f.WriteString("data")
f.Sync()
f.Close()
time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
os.Remove(testFile)
os.Remove(testFileAlreadyExists)
// We expect this event to be received almost immediately, but let's wait 500 ms to be sure
time.Sleep(500 * time.Millisecond)
cReceived := createReceived.value()
if cReceived != 1 {
t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", cReceived, 1)
}
mReceived := modifyReceived.value()
if mReceived != 1 {
t.Fatalf("incorrect number of modify events received after 500 ms (%d vs %d)", mReceived, 1)
}
dReceived := deleteReceived.value()
if dReceived != 2 {
t.Fatalf("incorrect number of delete events received after 500 ms (%d vs %d)", dReceived, 2)
}
// Try closing the fsnotify instance
t.Log("calling Close()")
watcher.Close()
t.Log("waiting for the event channel to become closed...")
select {
case <-done:
t.Log("event channel closed")
case <-time.After(2 * time.Second):
t.Fatal("event stream was not closed after 2 seconds")
}
}
func TestFsnotifyDeleteWatchedDir(t *testing.T) {
// Create an fsnotify watcher instance and initialize it
watcher, err := NewWatcher()
if err != nil {
t.Fatalf("NewWatcher() failed: %s", err)
}
defer watcher.Close()
var testDir string = testTempDir()
// Create directory to watch
if err := os.Mkdir(testDir, 0777); err != nil {
t.Fatalf("failed to create test directory: %s", err)
}
// Create a file before watching directory
var testFileAlreadyExists string = filepath.Join(testDir, "TestFsnotifyEventsExisting.testfile")
{
var f *os.File
f, err = os.OpenFile(testFileAlreadyExists, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
t.Fatalf("creating test file failed: %s", err)
}
f.Sync()
f.Close()
}
// Add a watch for testDir
err = watcher.Watch(testDir)
if err != nil {
t.Fatalf("watcher.Watch() failed: %s", err)
}
// Add a watch for testFile
err = watcher.Watch(testFileAlreadyExists)
if err != nil {
t.Fatalf("watcher.Watch() failed: %s", err)
}
// Receive errors on the error channel on a separate goroutine
go func() {
for err := range watcher.Error {
t.Fatalf("error received: %s", err)
}
}()
// Receive events on the event channel on a separate goroutine
eventstream := watcher.Event
var deleteReceived counter
go func() {
for event := range eventstream {
// Only count relevant events
if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFileAlreadyExists) {
t.Logf("event received: %s", event)
if event.IsDelete() {
deleteReceived.increment()
}
} else {
t.Logf("unexpected event received: %s", event)
}
}
}()
os.RemoveAll(testDir)
// We expect this event to be received almost immediately, but let's wait 500 ms to be sure
time.Sleep(500 * time.Millisecond)
dReceived := deleteReceived.value()
if dReceived < 2 {
t.Fatalf("did not receive at least %d delete events, received %d after 500 ms", 2, dReceived)
}
}
func TestFsnotifySubDir(t *testing.T) {
// Create an fsnotify watcher instance and initialize it
watcher, err := NewWatcher()
if err != nil {
t.Fatalf("NewWatcher() failed: %s", err)
}
var testDir string = testTempDir()
var testFile1 string = filepath.Join(testDir, "TestFsnotifyFile1.testfile")
var testSubDir string = filepath.Join(testDir, "sub")
var testSubDirFile string = filepath.Join(testDir, "sub/TestFsnotifyFile1.testfile")
// Create directory to watch
if err := os.Mkdir(testDir, 0777); err != nil {
t.Fatalf("failed to create test directory: %s", err)
}
defer os.RemoveAll(testDir)
// Receive errors on the error channel on a separate goroutine
go func() {
for err := range watcher.Error {
t.Fatalf("error received: %s", err)
}
}()
// Receive events on the event channel on a separate goroutine
eventstream := watcher.Event
var createReceived, deleteReceived counter
done := make(chan bool)
go func() {
for event := range eventstream {
// Only count relevant events
if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testSubDir) || event.Name == filepath.Clean(testFile1) {
t.Logf("event received: %s", event)
if event.IsCreate() {
createReceived.increment()
}
if event.IsDelete() {
deleteReceived.increment()
}
} else {
t.Logf("unexpected event received: %s", event)
}
}
done <- true
}()
// Add a watch for testDir
err = watcher.Watch(testDir)
if err != nil {
t.Fatalf("watcher.Watch() failed: %s", err)
}
// Create sub-directory
if err := os.Mkdir(testSubDir, 0777); err != nil {
t.Fatalf("failed to create test sub-directory: %s", err)
}
// Create a file
var f *os.File
f, err = os.OpenFile(testFile1, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
t.Fatalf("creating test file failed: %s", err)
}
f.Sync()
f.Close()
// Create a file (Should not see this! we are not watching subdir)
var fs *os.File
fs, err = os.OpenFile(testSubDirFile, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
t.Fatalf("creating test file failed: %s", err)
}
fs.Sync()
fs.Close()
time.Sleep(200 * time.Millisecond)
// Make sure receive deletes for both file and sub-directory
os.RemoveAll(testSubDir)
os.Remove(testFile1)
// We expect this event to be received almost immediately, but let's wait 500 ms to be sure
time.Sleep(500 * time.Millisecond)
cReceived := createReceived.value()
if cReceived != 2 {
t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", cReceived, 2)
}
dReceived := deleteReceived.value()
if dReceived != 2 {
t.Fatalf("incorrect number of delete events received after 500 ms (%d vs %d)", dReceived, 2)
}
// Try closing the fsnotify instance
t.Log("calling Close()")
watcher.Close()
t.Log("waiting for the event channel to become closed...")
select {
case <-done:
t.Log("event channel closed")
case <-time.After(2 * time.Second):
t.Fatal("event stream was not closed after 2 seconds")
}
}
func TestFsnotifyRename(t *testing.T) {
// Create an fsnotify watcher instance and initialize it
watcher, err := NewWatcher()
if err != nil {
t.Fatalf("NewWatcher() failed: %s", err)
}
var testDir string = testTempDir()
// Create directory to watch
if err := os.Mkdir(testDir, 0777); err != nil {
t.Fatalf("failed to create test directory: %s", err)
}
defer os.RemoveAll(testDir)
// Add a watch for testDir
err = watcher.Watch(testDir)
if err != nil {
t.Fatalf("watcher.Watch() failed: %s", err)
}
// Receive errors on the error channel on a separate goroutine
go func() {
for err := range watcher.Error {
t.Fatalf("error received: %s", err)
}
}()
var testFile string = filepath.Join(testDir, "TestFsnotifyEvents.testfile")
var testFileRenamed string = filepath.Join(testDir, "TestFsnotifyEvents.testfileRenamed")
// Receive events on the event channel on a separate goroutine
eventstream := watcher.Event
var renameReceived counter
done := make(chan bool)
go func() {
for event := range eventstream {
// Only count relevant events
if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) || event.Name == filepath.Clean(testFileRenamed) {
if event.IsRename() {
renameReceived.increment()
}
t.Logf("event received: %s", event)
} else {
t.Logf("unexpected event received: %s", event)
}
}
done <- true
}()
// Create a file
// This should add at least one event to the fsnotify event queue
var f *os.File
f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
t.Fatalf("creating test file failed: %s", err)
}
f.Sync()
f.WriteString("data")
f.Sync()
f.Close()
// Add a watch for testFile
err = watcher.Watch(testFile)
if err != nil {
t.Fatalf("watcher.Watch() failed: %s", err)
}
err = testRename(testFile, testFileRenamed)
if err != nil {
t.Fatalf("rename failed: %s", err)
}
// We expect this event to be received almost immediately, but let's wait 500 ms to be sure
time.Sleep(500 * time.Millisecond)
if renameReceived.value() == 0 {
t.Fatal("fsnotify rename events have not been received after 500 ms")
}
// Try closing the fsnotify instance
t.Log("calling Close()")
watcher.Close()
t.Log("waiting for the event channel to become closed...")
select {
case <-done:
t.Log("event channel closed")
case <-time.After(2 * time.Second):
t.Fatal("event stream was not closed after 2 seconds")
}
os.Remove(testFileRenamed)
}
func TestFsnotifyRenameToCreate(t *testing.T) {
// Create an fsnotify watcher instance and initialize it
watcher, err := NewWatcher()
if err != nil {
t.Fatalf("NewWatcher() failed: %s", err)
}
var testDir string = testTempDir()
var testDirFrom string = testTempDir()
// Create directory to watch
if err := os.Mkdir(testDir, 0777); err != nil {
t.Fatalf("failed to create test directory: %s", err)
}
defer os.RemoveAll(testDir)
// Create directory to get file
if err := os.Mkdir(testDirFrom, 0777); err != nil {
t.Fatalf("failed to create test directory: %s", err)
}
defer os.RemoveAll(testDirFrom)
// Add a watch for testDir
err = watcher.Watch(testDir)
if err != nil {
t.Fatalf("watcher.Watch() failed: %s", err)
}
// Receive errors on the error channel on a separate goroutine
go func() {
for err := range watcher.Error {
t.Fatalf("error received: %s", err)
}
}()
var testFile string = filepath.Join(testDirFrom, "TestFsnotifyEvents.testfile")
var testFileRenamed string = filepath.Join(testDir, "TestFsnotifyEvents.testfileRenamed")
// Receive events on the event channel on a separate goroutine
eventstream := watcher.Event
var createReceived counter
done := make(chan bool)
go func() {
for event := range eventstream {
// Only count relevant events
if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) || event.Name == filepath.Clean(testFileRenamed) {
if event.IsCreate() {
createReceived.increment()
}
t.Logf("event received: %s", event)
} else {
t.Logf("unexpected event received: %s", event)
}
}
done <- true
}()
// Create a file
// This should add at least one event to the fsnotify event queue
var f *os.File
f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
t.Fatalf("creating test file failed: %s", err)
}
f.Sync()
f.Close()
err = testRename(testFile, testFileRenamed)
if err != nil {
t.Fatalf("rename failed: %s", err)
}
// We expect this event to be received almost immediately, but let's wait 500 ms to be sure
time.Sleep(500 * time.Millisecond)
if createReceived.value() == 0 {
t.Fatal("fsnotify create events have not been received after 500 ms")
}
// Try closing the fsnotify instance
t.Log("calling Close()")
watcher.Close()
t.Log("waiting for the event channel to become closed...")
select {
case <-done:
t.Log("event channel closed")
case <-time.After(2 * time.Second):
t.Fatal("event stream was not closed after 2 seconds")
}
os.Remove(testFileRenamed)
}
func TestFsnotifyRenameToOverwrite(t *testing.T) {
switch runtime.GOOS {
case "plan9", "windows":
t.Skipf("skipping test on %q (os.Rename over existing file does not create event).", runtime.GOOS)
}
// Create an fsnotify watcher instance and initialize it
watcher, err := NewWatcher()
if err != nil {
t.Fatalf("NewWatcher() failed: %s", err)
}
var testDir string = testTempDir()
var testDirFrom string = testTempDir()
var testFile string = filepath.Join(testDirFrom, "TestFsnotifyEvents.testfile")
var testFileRenamed string = filepath.Join(testDir, "TestFsnotifyEvents.testfileRenamed")
// Create directory to watch
if err := os.Mkdir(testDir, 0777); err != nil {
t.Fatalf("failed to create test directory: %s", err)
}
defer os.RemoveAll(testDir)
// Create directory to get file
if err := os.Mkdir(testDirFrom, 0777); err != nil {
t.Fatalf("failed to create test directory: %s", err)
}
defer os.RemoveAll(testDirFrom)
// Create a file
var fr *os.File
fr, err = os.OpenFile(testFileRenamed, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
t.Fatalf("creating test file failed: %s", err)
}
fr.Sync()
fr.Close()
// Add a watch for testDir
err = watcher.Watch(testDir)
if err != nil {
t.Fatalf("watcher.Watch() failed: %s", err)
}
// Receive errors on the error channel on a separate goroutine
go func() {
for err := range watcher.Error {
t.Fatalf("error received: %s", err)
}
}()
// Receive events on the event channel on a separate goroutine
eventstream := watcher.Event
var eventReceived counter
done := make(chan bool)
go func() {
for event := range eventstream {
// Only count relevant events
if event.Name == filepath.Clean(testFileRenamed) {
eventReceived.increment()
t.Logf("event received: %s", event)
} else {
t.Logf("unexpected event received: %s", event)
}
}
done <- true
}()
// Create a file
// This should add at least one event to the fsnotify event queue
var f *os.File
f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
t.Fatalf("creating test file failed: %s", err)
}
f.Sync()
f.Close()
err = testRename(testFile, testFileRenamed)
if err != nil {
t.Fatalf("rename failed: %s", err)
}
// We expect this event to be received almost immediately, but let's wait 500 ms to be sure
time.Sleep(500 * time.Millisecond)
if eventReceived.value() == 0 {
t.Fatal("fsnotify events have not been received after 500 ms")
}
// Try closing the fsnotify instance
t.Log("calling Close()")
watcher.Close()
t.Log("waiting for the event channel to become closed...")
select {
case <-done:
t.Log("event channel closed")
case <-time.After(2 * time.Second):
t.Fatal("event stream was not closed after 2 seconds")
}
os.Remove(testFileRenamed)
}
func TestRemovalOfWatch(t *testing.T) {
var testDir string = testTempDir()
// Create directory to watch
if err := os.Mkdir(testDir, 0777); err != nil {
t.Fatalf("failed to create test directory: %s", err)
}
defer os.RemoveAll(testDir)
// Create a file before watching directory
var testFileAlreadyExists string = filepath.Join(testDir, "TestFsnotifyEventsExisting.testfile")
{
var f *os.File
f, err := os.OpenFile(testFileAlreadyExists, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
t.Fatalf("creating test file failed: %s", err)
}
f.Sync()
f.Close()
}
watcher, err := NewWatcher()
if err != nil {
t.Fatalf("NewWatcher() failed: %s", err)
}
defer watcher.Close()
watcher.Watch(testDir)
err = watcher.RemoveWatch(testDir)
if err != nil {
t.Fatalf("Could not remove the watch: %v\n", err)
}
go func() {
select {
case ev := <-watcher.Event:
t.Fatalf("We received event: %v\n", ev)
case <-time.After(500 * time.Millisecond):
t.Log("No event received, as expected.")
}
}()
time.Sleep(200 * time.Millisecond)
// Modify the file outside of the watched dir
f, err := os.Open(testFileAlreadyExists)
if err != nil {
t.Fatalf("Open test file failed: %s", err)
}
f.WriteString("data")
f.Sync()
f.Close()
err = os.Chmod(testFileAlreadyExists, 0700)
if err != nil {
t.Fatalf("chmod failed: %s", err)
}
time.Sleep(400 * time.Millisecond)
}
func TestFsnotifyAttrib(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("attributes don't work on Windows.")
}
// Create an fsnotify watcher instance and initialize it
watcher, err := NewWatcher()
if err != nil {
t.Fatalf("NewWatcher() failed: %s", err)
}
var testDir string = testTempDir()
// Create directory to watch
if err := os.Mkdir(testDir, 0777); err != nil {
t.Fatalf("failed to create test directory: %s", err)
}
defer os.RemoveAll(testDir)
// Receive errors on the error channel on a separate goroutine
go func() {
for err := range watcher.Error {
t.Fatalf("error received: %s", err)
}
}()
var testFile string = filepath.Join(testDir, "TestFsnotifyAttrib.testfile")
// Receive events on the event channel on a separate goroutine
eventstream := watcher.Event
var attribReceived counter
done := make(chan bool)
go func() {
for event := range eventstream {
// Only count relevant events
if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) {
if event.IsModify() {
attribReceived.increment()
}
t.Logf("event received: %s", event)
} else {
t.Logf("unexpected event received: %s", event)
}
}
done <- true
}()
// Create a file
// This should add at least one event to the fsnotify event queue
var f *os.File
f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
t.Fatalf("creating test file failed: %s", err)
}
f.Sync()
f.WriteString("data")
f.Sync()
f.Close()
// Add a watch for testFile
err = watcher.Watch(testFile)
if err != nil {
t.Fatalf("watcher.Watch() failed: %s", err)
}
err = os.Chmod(testFile, 0700)
if err != nil {
t.Fatalf("chmod failed: %s", err)
}
// We expect this event to be received almost immediately, but let's wait 500 ms to be sure
time.Sleep(500 * time.Millisecond)
if attribReceived.value() == 0 {
t.Fatal("fsnotify attribute events have not received after 500 ms")
}
// Try closing the fsnotify instance
t.Log("calling Close()")
watcher.Close()
t.Log("waiting for the event channel to become closed...")
select {
case <-done:
t.Log("event channel closed")
case <-time.After(1e9):
t.Fatal("event stream was not closed after 1 second")
}
os.Remove(testFile)
}
func TestFsnotifyClose(t *testing.T) {
watcher, _ := NewWatcher()
watcher.Close()
var done int32
go func() {
watcher.Close()
atomic.StoreInt32(&done, 1)
}()
time.Sleep(50e6) // 50 ms
if atomic.LoadInt32(&done) == 0 {
t.Fatal("double Close() test failed: second Close() call didn't return")
}
err := watcher.Watch(testTempDir())
if err == nil {
t.Fatal("expected error on Watch() after Close(), got nil")
}
}
func testRename(file1, file2 string) error {
switch runtime.GOOS {
case "windows", "plan9":
return os.Rename(file1, file2)
default:
cmd := exec.Command("mv", file1, file2)
return cmd.Run()
}
}