1// Copyright 2010 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.
4
5// +build !plan9,!solaris
6
7package fsnotify
8
9import (
10	"io/ioutil"
11	"os"
12	"os/exec"
13	"path"
14	"path/filepath"
15	"runtime"
16	"sync/atomic"
17	"testing"
18	"time"
19)
20
21// An atomic counter
22type counter struct {
23	val int32
24}
25
26func (c *counter) increment() {
27	atomic.AddInt32(&c.val, 1)
28}
29
30func (c *counter) value() int32 {
31	return atomic.LoadInt32(&c.val)
32}
33
34func (c *counter) reset() {
35	atomic.StoreInt32(&c.val, 0)
36}
37
38// tempMkdir makes a temporary directory
39func tempMkdir(t *testing.T) string {
40	dir, err := ioutil.TempDir("", "fsnotify")
41	if err != nil {
42		t.Fatalf("failed to create test directory: %s", err)
43	}
44	return dir
45}
46
47// tempMkFile makes a temporary file.
48func tempMkFile(t *testing.T, dir string) string {
49	f, err := ioutil.TempFile(dir, "fsnotify")
50	if err != nil {
51		t.Fatalf("failed to create test file: %v", err)
52	}
53	defer f.Close()
54	return f.Name()
55}
56
57// newWatcher initializes an fsnotify Watcher instance.
58func newWatcher(t *testing.T) *Watcher {
59	watcher, err := NewWatcher()
60	if err != nil {
61		t.Fatalf("NewWatcher() failed: %s", err)
62	}
63	return watcher
64}
65
66// addWatch adds a watch for a directory
67func addWatch(t *testing.T, watcher *Watcher, dir string) {
68	if err := watcher.Add(dir); err != nil {
69		t.Fatalf("watcher.Add(%q) failed: %s", dir, err)
70	}
71}
72
73func TestFsnotifyMultipleOperations(t *testing.T) {
74	watcher := newWatcher(t)
75
76	// Receive errors on the error channel on a separate goroutine
77	go func() {
78		for err := range watcher.Errors {
79			t.Fatalf("error received: %s", err)
80		}
81	}()
82
83	// Create directory to watch
84	testDir := tempMkdir(t)
85	defer os.RemoveAll(testDir)
86
87	// Create directory that's not watched
88	testDirToMoveFiles := tempMkdir(t)
89	defer os.RemoveAll(testDirToMoveFiles)
90
91	testFile := filepath.Join(testDir, "TestFsnotifySeq.testfile")
92	testFileRenamed := filepath.Join(testDirToMoveFiles, "TestFsnotifySeqRename.testfile")
93
94	addWatch(t, watcher, testDir)
95
96	// Receive events on the event channel on a separate goroutine
97	eventstream := watcher.Events
98	var createReceived, modifyReceived, deleteReceived, renameReceived counter
99	done := make(chan bool)
100	go func() {
101		for event := range eventstream {
102			// Only count relevant events
103			if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) {
104				t.Logf("event received: %s", event)
105				if event.Op&Remove == Remove {
106					deleteReceived.increment()
107				}
108				if event.Op&Write == Write {
109					modifyReceived.increment()
110				}
111				if event.Op&Create == Create {
112					createReceived.increment()
113				}
114				if event.Op&Rename == Rename {
115					renameReceived.increment()
116				}
117			} else {
118				t.Logf("unexpected event received: %s", event)
119			}
120		}
121		done <- true
122	}()
123
124	// Create a file
125	// This should add at least one event to the fsnotify event queue
126	var f *os.File
127	f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
128	if err != nil {
129		t.Fatalf("creating test file failed: %s", err)
130	}
131	f.Sync()
132
133	time.Sleep(time.Millisecond)
134	f.WriteString("data")
135	f.Sync()
136	f.Close()
137
138	time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
139
140	if err := testRename(testFile, testFileRenamed); err != nil {
141		t.Fatalf("rename failed: %s", err)
142	}
143
144	// Modify the file outside of the watched dir
145	f, err = os.Open(testFileRenamed)
146	if err != nil {
147		t.Fatalf("open test renamed file failed: %s", err)
148	}
149	f.WriteString("data")
150	f.Sync()
151	f.Close()
152
153	time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
154
155	// Recreate the file that was moved
156	f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
157	if err != nil {
158		t.Fatalf("creating test file failed: %s", err)
159	}
160	f.Close()
161	time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
162
163	// We expect this event to be received almost immediately, but let's wait 500 ms to be sure
164	time.Sleep(500 * time.Millisecond)
165	cReceived := createReceived.value()
166	if cReceived != 2 {
167		t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", cReceived, 2)
168	}
169	mReceived := modifyReceived.value()
170	if mReceived != 1 {
171		t.Fatalf("incorrect number of modify events received after 500 ms (%d vs %d)", mReceived, 1)
172	}
173	dReceived := deleteReceived.value()
174	rReceived := renameReceived.value()
175	if dReceived+rReceived != 1 {
176		t.Fatalf("incorrect number of rename+delete events received after 500 ms (%d vs %d)", rReceived+dReceived, 1)
177	}
178
179	// Try closing the fsnotify instance
180	t.Log("calling Close()")
181	watcher.Close()
182	t.Log("waiting for the event channel to become closed...")
183	select {
184	case <-done:
185		t.Log("event channel closed")
186	case <-time.After(2 * time.Second):
187		t.Fatal("event stream was not closed after 2 seconds")
188	}
189}
190
191func TestFsnotifyMultipleCreates(t *testing.T) {
192	watcher := newWatcher(t)
193
194	// Receive errors on the error channel on a separate goroutine
195	go func() {
196		for err := range watcher.Errors {
197			t.Errorf("error received: %s", err)
198		}
199	}()
200
201	// Create directory to watch
202	testDir := tempMkdir(t)
203	defer os.RemoveAll(testDir)
204
205	testFile := filepath.Join(testDir, "TestFsnotifySeq.testfile")
206
207	addWatch(t, watcher, testDir)
208
209	// Receive events on the event channel on a separate goroutine
210	eventstream := watcher.Events
211	var createReceived, modifyReceived, deleteReceived counter
212	done := make(chan bool)
213	go func() {
214		for event := range eventstream {
215			// Only count relevant events
216			if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) {
217				t.Logf("event received: %s", event)
218				if event.Op&Remove == Remove {
219					deleteReceived.increment()
220				}
221				if event.Op&Create == Create {
222					createReceived.increment()
223				}
224				if event.Op&Write == Write {
225					modifyReceived.increment()
226				}
227			} else {
228				t.Logf("unexpected event received: %s", event)
229			}
230		}
231		done <- true
232	}()
233
234	// Create a file
235	// This should add at least one event to the fsnotify event queue
236	var f *os.File
237	f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
238	if err != nil {
239		t.Fatalf("creating test file failed: %s", err)
240	}
241	f.Sync()
242
243	time.Sleep(time.Millisecond)
244	f.WriteString("data")
245	f.Sync()
246	f.Close()
247
248	time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
249
250	os.Remove(testFile)
251
252	time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
253
254	// Recreate the file
255	f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
256	if err != nil {
257		t.Fatalf("creating test file failed: %s", err)
258	}
259	f.Close()
260	time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
261
262	// Modify
263	f, err = os.OpenFile(testFile, os.O_WRONLY, 0666)
264	if err != nil {
265		t.Fatalf("creating test file failed: %s", err)
266	}
267	f.Sync()
268
269	time.Sleep(time.Millisecond)
270	f.WriteString("data")
271	f.Sync()
272	f.Close()
273
274	time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
275
276	// Modify
277	f, err = os.OpenFile(testFile, os.O_WRONLY, 0666)
278	if err != nil {
279		t.Fatalf("creating test file failed: %s", err)
280	}
281	f.Sync()
282
283	time.Sleep(time.Millisecond)
284	f.WriteString("data")
285	f.Sync()
286	f.Close()
287
288	time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
289
290	// We expect this event to be received almost immediately, but let's wait 500 ms to be sure
291	time.Sleep(500 * time.Millisecond)
292	cReceived := createReceived.value()
293	if cReceived != 2 {
294		t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", cReceived, 2)
295	}
296	mReceived := modifyReceived.value()
297	if mReceived < 3 {
298		t.Fatalf("incorrect number of modify events received after 500 ms (%d vs atleast %d)", mReceived, 3)
299	}
300	dReceived := deleteReceived.value()
301	if dReceived != 1 {
302		t.Fatalf("incorrect number of rename+delete events received after 500 ms (%d vs %d)", dReceived, 1)
303	}
304
305	// Try closing the fsnotify instance
306	t.Log("calling Close()")
307	watcher.Close()
308	t.Log("waiting for the event channel to become closed...")
309	select {
310	case <-done:
311		t.Log("event channel closed")
312	case <-time.After(2 * time.Second):
313		t.Fatal("event stream was not closed after 2 seconds")
314	}
315}
316
317func TestFsnotifyDirOnly(t *testing.T) {
318	watcher := newWatcher(t)
319
320	// Create directory to watch
321	testDir := tempMkdir(t)
322	defer os.RemoveAll(testDir)
323
324	// Create a file before watching directory
325	// This should NOT add any events to the fsnotify event queue
326	testFileAlreadyExists := filepath.Join(testDir, "TestFsnotifyEventsExisting.testfile")
327	{
328		var f *os.File
329		f, err := os.OpenFile(testFileAlreadyExists, os.O_WRONLY|os.O_CREATE, 0666)
330		if err != nil {
331			t.Fatalf("creating test file failed: %s", err)
332		}
333		f.Sync()
334		f.Close()
335	}
336
337	addWatch(t, watcher, testDir)
338
339	// Receive errors on the error channel on a separate goroutine
340	go func() {
341		for err := range watcher.Errors {
342			t.Errorf("error received: %s", err)
343		}
344	}()
345
346	testFile := filepath.Join(testDir, "TestFsnotifyDirOnly.testfile")
347
348	// Receive events on the event channel on a separate goroutine
349	eventstream := watcher.Events
350	var createReceived, modifyReceived, deleteReceived counter
351	done := make(chan bool)
352	go func() {
353		for event := range eventstream {
354			// Only count relevant events
355			if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) || event.Name == filepath.Clean(testFileAlreadyExists) {
356				t.Logf("event received: %s", event)
357				if event.Op&Remove == Remove {
358					deleteReceived.increment()
359				}
360				if event.Op&Write == Write {
361					modifyReceived.increment()
362				}
363				if event.Op&Create == Create {
364					createReceived.increment()
365				}
366			} else {
367				t.Logf("unexpected event received: %s", event)
368			}
369		}
370		done <- true
371	}()
372
373	// Create a file
374	// This should add at least one event to the fsnotify event queue
375	var f *os.File
376	f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
377	if err != nil {
378		t.Fatalf("creating test file failed: %s", err)
379	}
380	f.Sync()
381
382	time.Sleep(time.Millisecond)
383	f.WriteString("data")
384	f.Sync()
385	f.Close()
386
387	time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete
388
389	os.Remove(testFile)
390	os.Remove(testFileAlreadyExists)
391
392	// We expect this event to be received almost immediately, but let's wait 500 ms to be sure
393	time.Sleep(500 * time.Millisecond)
394	cReceived := createReceived.value()
395	if cReceived != 1 {
396		t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", cReceived, 1)
397	}
398	mReceived := modifyReceived.value()
399	if mReceived != 1 {
400		t.Fatalf("incorrect number of modify events received after 500 ms (%d vs %d)", mReceived, 1)
401	}
402	dReceived := deleteReceived.value()
403	if dReceived != 2 {
404		t.Fatalf("incorrect number of delete events received after 500 ms (%d vs %d)", dReceived, 2)
405	}
406
407	// Try closing the fsnotify instance
408	t.Log("calling Close()")
409	watcher.Close()
410	t.Log("waiting for the event channel to become closed...")
411	select {
412	case <-done:
413		t.Log("event channel closed")
414	case <-time.After(2 * time.Second):
415		t.Fatal("event stream was not closed after 2 seconds")
416	}
417}
418
419func TestFsnotifyDeleteWatchedDir(t *testing.T) {
420	watcher := newWatcher(t)
421	defer watcher.Close()
422
423	// Create directory to watch
424	testDir := tempMkdir(t)
425	defer os.RemoveAll(testDir)
426
427	// Create a file before watching directory
428	testFileAlreadyExists := filepath.Join(testDir, "TestFsnotifyEventsExisting.testfile")
429	{
430		var f *os.File
431		f, err := os.OpenFile(testFileAlreadyExists, os.O_WRONLY|os.O_CREATE, 0666)
432		if err != nil {
433			t.Fatalf("creating test file failed: %s", err)
434		}
435		f.Sync()
436		f.Close()
437	}
438
439	addWatch(t, watcher, testDir)
440
441	// Add a watch for testFile
442	addWatch(t, watcher, testFileAlreadyExists)
443
444	// Receive errors on the error channel on a separate goroutine
445	go func() {
446		for err := range watcher.Errors {
447			t.Errorf("error received: %s", err)
448		}
449	}()
450
451	// Receive events on the event channel on a separate goroutine
452	eventstream := watcher.Events
453	var deleteReceived counter
454	go func() {
455		for event := range eventstream {
456			// Only count relevant events
457			if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFileAlreadyExists) {
458				t.Logf("event received: %s", event)
459				if event.Op&Remove == Remove {
460					deleteReceived.increment()
461				}
462			} else {
463				t.Logf("unexpected event received: %s", event)
464			}
465		}
466	}()
467
468	os.RemoveAll(testDir)
469
470	// We expect this event to be received almost immediately, but let's wait 500 ms to be sure
471	time.Sleep(500 * time.Millisecond)
472	dReceived := deleteReceived.value()
473	if dReceived < 2 {
474		t.Fatalf("did not receive at least %d delete events, received %d after 500 ms", 2, dReceived)
475	}
476}
477
478func TestFsnotifySubDir(t *testing.T) {
479	watcher := newWatcher(t)
480
481	// Create directory to watch
482	testDir := tempMkdir(t)
483	defer os.RemoveAll(testDir)
484
485	testFile1 := filepath.Join(testDir, "TestFsnotifyFile1.testfile")
486	testSubDir := filepath.Join(testDir, "sub")
487	testSubDirFile := filepath.Join(testDir, "sub/TestFsnotifyFile1.testfile")
488
489	// Receive errors on the error channel on a separate goroutine
490	go func() {
491		for err := range watcher.Errors {
492			t.Errorf("error received: %s", err)
493		}
494	}()
495
496	// Receive events on the event channel on a separate goroutine
497	eventstream := watcher.Events
498	var createReceived, deleteReceived counter
499	done := make(chan bool)
500	go func() {
501		for event := range eventstream {
502			// Only count relevant events
503			if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testSubDir) || event.Name == filepath.Clean(testFile1) {
504				t.Logf("event received: %s", event)
505				if event.Op&Create == Create {
506					createReceived.increment()
507				}
508				if event.Op&Remove == Remove {
509					deleteReceived.increment()
510				}
511			} else {
512				t.Logf("unexpected event received: %s", event)
513			}
514		}
515		done <- true
516	}()
517
518	addWatch(t, watcher, testDir)
519
520	// Create sub-directory
521	if err := os.Mkdir(testSubDir, 0777); err != nil {
522		t.Fatalf("failed to create test sub-directory: %s", err)
523	}
524
525	// Create a file
526	var f *os.File
527	f, err := os.OpenFile(testFile1, os.O_WRONLY|os.O_CREATE, 0666)
528	if err != nil {
529		t.Fatalf("creating test file failed: %s", err)
530	}
531	f.Sync()
532	f.Close()
533
534	// Create a file (Should not see this! we are not watching subdir)
535	var fs *os.File
536	fs, err = os.OpenFile(testSubDirFile, os.O_WRONLY|os.O_CREATE, 0666)
537	if err != nil {
538		t.Fatalf("creating test file failed: %s", err)
539	}
540	fs.Sync()
541	fs.Close()
542
543	time.Sleep(200 * time.Millisecond)
544
545	// Make sure receive deletes for both file and sub-directory
546	os.RemoveAll(testSubDir)
547	os.Remove(testFile1)
548
549	// We expect this event to be received almost immediately, but let's wait 500 ms to be sure
550	time.Sleep(500 * time.Millisecond)
551	cReceived := createReceived.value()
552	if cReceived != 2 {
553		t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", cReceived, 2)
554	}
555	dReceived := deleteReceived.value()
556	if dReceived != 2 {
557		t.Fatalf("incorrect number of delete events received after 500 ms (%d vs %d)", dReceived, 2)
558	}
559
560	// Try closing the fsnotify instance
561	t.Log("calling Close()")
562	watcher.Close()
563	t.Log("waiting for the event channel to become closed...")
564	select {
565	case <-done:
566		t.Log("event channel closed")
567	case <-time.After(2 * time.Second):
568		t.Fatal("event stream was not closed after 2 seconds")
569	}
570}
571
572func TestFsnotifyRename(t *testing.T) {
573	watcher := newWatcher(t)
574
575	// Create directory to watch
576	testDir := tempMkdir(t)
577	defer os.RemoveAll(testDir)
578
579	addWatch(t, watcher, testDir)
580
581	// Receive errors on the error channel on a separate goroutine
582	go func() {
583		for err := range watcher.Errors {
584			t.Errorf("error received: %s", err)
585		}
586	}()
587
588	testFile := filepath.Join(testDir, "TestFsnotifyEvents.testfile")
589	testFileRenamed := filepath.Join(testDir, "TestFsnotifyEvents.testfileRenamed")
590
591	// Receive events on the event channel on a separate goroutine
592	eventstream := watcher.Events
593	var renameReceived counter
594	done := make(chan bool)
595	go func() {
596		for event := range eventstream {
597			// Only count relevant events
598			if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) || event.Name == filepath.Clean(testFileRenamed) {
599				if event.Op&Rename == Rename {
600					renameReceived.increment()
601				}
602				t.Logf("event received: %s", event)
603			} else {
604				t.Logf("unexpected event received: %s", event)
605			}
606		}
607		done <- true
608	}()
609
610	// Create a file
611	// This should add at least one event to the fsnotify event queue
612	var f *os.File
613	f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
614	if err != nil {
615		t.Fatalf("creating test file failed: %s", err)
616	}
617	f.Sync()
618
619	f.WriteString("data")
620	f.Sync()
621	f.Close()
622
623	// Add a watch for testFile
624	addWatch(t, watcher, testFile)
625
626	if err := testRename(testFile, testFileRenamed); err != nil {
627		t.Fatalf("rename failed: %s", err)
628	}
629
630	// We expect this event to be received almost immediately, but let's wait 500 ms to be sure
631	time.Sleep(500 * time.Millisecond)
632	if renameReceived.value() == 0 {
633		t.Fatal("fsnotify rename events have not been received after 500 ms")
634	}
635
636	// Try closing the fsnotify instance
637	t.Log("calling Close()")
638	watcher.Close()
639	t.Log("waiting for the event channel to become closed...")
640	select {
641	case <-done:
642		t.Log("event channel closed")
643	case <-time.After(2 * time.Second):
644		t.Fatal("event stream was not closed after 2 seconds")
645	}
646
647	os.Remove(testFileRenamed)
648}
649
650func TestFsnotifyRenameToCreate(t *testing.T) {
651	watcher := newWatcher(t)
652
653	// Create directory to watch
654	testDir := tempMkdir(t)
655	defer os.RemoveAll(testDir)
656
657	// Create directory to get file
658	testDirFrom := tempMkdir(t)
659	defer os.RemoveAll(testDirFrom)
660
661	addWatch(t, watcher, testDir)
662
663	// Receive errors on the error channel on a separate goroutine
664	go func() {
665		for err := range watcher.Errors {
666			t.Errorf("error received: %s", err)
667		}
668	}()
669
670	testFile := filepath.Join(testDirFrom, "TestFsnotifyEvents.testfile")
671	testFileRenamed := filepath.Join(testDir, "TestFsnotifyEvents.testfileRenamed")
672
673	// Receive events on the event channel on a separate goroutine
674	eventstream := watcher.Events
675	var createReceived counter
676	done := make(chan bool)
677	go func() {
678		for event := range eventstream {
679			// Only count relevant events
680			if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) || event.Name == filepath.Clean(testFileRenamed) {
681				if event.Op&Create == Create {
682					createReceived.increment()
683				}
684				t.Logf("event received: %s", event)
685			} else {
686				t.Logf("unexpected event received: %s", event)
687			}
688		}
689		done <- true
690	}()
691
692	// Create a file
693	// This should add at least one event to the fsnotify event queue
694	var f *os.File
695	f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
696	if err != nil {
697		t.Fatalf("creating test file failed: %s", err)
698	}
699	f.Sync()
700	f.Close()
701
702	if err := testRename(testFile, testFileRenamed); err != nil {
703		t.Fatalf("rename failed: %s", err)
704	}
705
706	// We expect this event to be received almost immediately, but let's wait 500 ms to be sure
707	time.Sleep(500 * time.Millisecond)
708	if createReceived.value() == 0 {
709		t.Fatal("fsnotify create events have not been received after 500 ms")
710	}
711
712	// Try closing the fsnotify instance
713	t.Log("calling Close()")
714	watcher.Close()
715	t.Log("waiting for the event channel to become closed...")
716	select {
717	case <-done:
718		t.Log("event channel closed")
719	case <-time.After(2 * time.Second):
720		t.Fatal("event stream was not closed after 2 seconds")
721	}
722
723	os.Remove(testFileRenamed)
724}
725
726func TestFsnotifyRenameToOverwrite(t *testing.T) {
727	switch runtime.GOOS {
728	case "plan9", "windows":
729		t.Skipf("skipping test on %q (os.Rename over existing file does not create event).", runtime.GOOS)
730	}
731
732	watcher := newWatcher(t)
733
734	// Create directory to watch
735	testDir := tempMkdir(t)
736	defer os.RemoveAll(testDir)
737
738	// Create directory to get file
739	testDirFrom := tempMkdir(t)
740	defer os.RemoveAll(testDirFrom)
741
742	testFile := filepath.Join(testDirFrom, "TestFsnotifyEvents.testfile")
743	testFileRenamed := filepath.Join(testDir, "TestFsnotifyEvents.testfileRenamed")
744
745	// Create a file
746	var fr *os.File
747	fr, err := os.OpenFile(testFileRenamed, os.O_WRONLY|os.O_CREATE, 0666)
748	if err != nil {
749		t.Fatalf("creating test file failed: %s", err)
750	}
751	fr.Sync()
752	fr.Close()
753
754	addWatch(t, watcher, testDir)
755
756	// Receive errors on the error channel on a separate goroutine
757	go func() {
758		for err := range watcher.Errors {
759			t.Errorf("error received: %s", err)
760		}
761	}()
762
763	// Receive events on the event channel on a separate goroutine
764	eventstream := watcher.Events
765	var eventReceived counter
766	done := make(chan bool)
767	go func() {
768		for event := range eventstream {
769			// Only count relevant events
770			if event.Name == filepath.Clean(testFileRenamed) {
771				eventReceived.increment()
772				t.Logf("event received: %s", event)
773			} else {
774				t.Logf("unexpected event received: %s", event)
775			}
776		}
777		done <- true
778	}()
779
780	// Create a file
781	// This should add at least one event to the fsnotify event queue
782	var f *os.File
783	f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
784	if err != nil {
785		t.Fatalf("creating test file failed: %s", err)
786	}
787	f.Sync()
788	f.Close()
789
790	if err := testRename(testFile, testFileRenamed); err != nil {
791		t.Fatalf("rename failed: %s", err)
792	}
793
794	// We expect this event to be received almost immediately, but let's wait 500 ms to be sure
795	time.Sleep(500 * time.Millisecond)
796	if eventReceived.value() == 0 {
797		t.Fatal("fsnotify events have not been received after 500 ms")
798	}
799
800	// Try closing the fsnotify instance
801	t.Log("calling Close()")
802	watcher.Close()
803	t.Log("waiting for the event channel to become closed...")
804	select {
805	case <-done:
806		t.Log("event channel closed")
807	case <-time.After(2 * time.Second):
808		t.Fatal("event stream was not closed after 2 seconds")
809	}
810
811	os.Remove(testFileRenamed)
812}
813
814func TestRemovalOfWatch(t *testing.T) {
815	// Create directory to watch
816	testDir := tempMkdir(t)
817	defer os.RemoveAll(testDir)
818
819	// Create a file before watching directory
820	testFileAlreadyExists := filepath.Join(testDir, "TestFsnotifyEventsExisting.testfile")
821	{
822		var f *os.File
823		f, err := os.OpenFile(testFileAlreadyExists, os.O_WRONLY|os.O_CREATE, 0666)
824		if err != nil {
825			t.Fatalf("creating test file failed: %s", err)
826		}
827		f.Sync()
828		f.Close()
829	}
830
831	watcher := newWatcher(t)
832	defer watcher.Close()
833
834	addWatch(t, watcher, testDir)
835	if err := watcher.Remove(testDir); err != nil {
836		t.Fatalf("Could not remove the watch: %v\n", err)
837	}
838
839	go func() {
840		select {
841		case ev := <-watcher.Events:
842			t.Fatalf("We received event: %v\n", ev)
843		case <-time.After(500 * time.Millisecond):
844			t.Log("No event received, as expected.")
845		}
846	}()
847
848	time.Sleep(200 * time.Millisecond)
849	// Modify the file outside of the watched dir
850	f, err := os.Open(testFileAlreadyExists)
851	if err != nil {
852		t.Fatalf("Open test file failed: %s", err)
853	}
854	f.WriteString("data")
855	f.Sync()
856	f.Close()
857	if err := os.Chmod(testFileAlreadyExists, 0700); err != nil {
858		t.Fatalf("chmod failed: %s", err)
859	}
860	time.Sleep(400 * time.Millisecond)
861}
862
863func TestFsnotifyAttrib(t *testing.T) {
864	if runtime.GOOS == "windows" {
865		t.Skip("attributes don't work on Windows.")
866	}
867
868	watcher := newWatcher(t)
869
870	// Create directory to watch
871	testDir := tempMkdir(t)
872	defer os.RemoveAll(testDir)
873
874	// Receive errors on the error channel on a separate goroutine
875	go func() {
876		for err := range watcher.Errors {
877			t.Errorf("error received: %s", err)
878		}
879	}()
880
881	testFile := filepath.Join(testDir, "TestFsnotifyAttrib.testfile")
882
883	// Receive events on the event channel on a separate goroutine
884	eventstream := watcher.Events
885	// The modifyReceived counter counts IsModify events that are not IsAttrib,
886	// and the attribReceived counts IsAttrib events (which are also IsModify as
887	// a consequence).
888	var modifyReceived counter
889	var attribReceived counter
890	done := make(chan bool)
891	go func() {
892		for event := range eventstream {
893			// Only count relevant events
894			if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) {
895				if event.Op&Write == Write {
896					modifyReceived.increment()
897				}
898				if event.Op&Chmod == Chmod {
899					attribReceived.increment()
900				}
901				t.Logf("event received: %s", event)
902			} else {
903				t.Logf("unexpected event received: %s", event)
904			}
905		}
906		done <- true
907	}()
908
909	// Create a file
910	// This should add at least one event to the fsnotify event queue
911	var f *os.File
912	f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
913	if err != nil {
914		t.Fatalf("creating test file failed: %s", err)
915	}
916	f.Sync()
917
918	f.WriteString("data")
919	f.Sync()
920	f.Close()
921
922	// Add a watch for testFile
923	addWatch(t, watcher, testFile)
924
925	if err := os.Chmod(testFile, 0700); err != nil {
926		t.Fatalf("chmod failed: %s", err)
927	}
928
929	// We expect this event to be received almost immediately, but let's wait 500 ms to be sure
930	// Creating/writing a file changes also the mtime, so IsAttrib should be set to true here
931	time.Sleep(500 * time.Millisecond)
932	if modifyReceived.value() != 0 {
933		t.Fatal("received an unexpected modify event when creating a test file")
934	}
935	if attribReceived.value() == 0 {
936		t.Fatal("fsnotify attribute events have not received after 500 ms")
937	}
938
939	// Modifying the contents of the file does not set the attrib flag (although eg. the mtime
940	// might have been modified).
941	modifyReceived.reset()
942	attribReceived.reset()
943
944	f, err = os.OpenFile(testFile, os.O_WRONLY, 0)
945	if err != nil {
946		t.Fatalf("reopening test file failed: %s", err)
947	}
948
949	f.WriteString("more data")
950	f.Sync()
951	f.Close()
952
953	time.Sleep(500 * time.Millisecond)
954
955	if modifyReceived.value() != 1 {
956		t.Fatal("didn't receive a modify event after changing test file contents")
957	}
958
959	if attribReceived.value() != 0 {
960		t.Fatal("did receive an unexpected attrib event after changing test file contents")
961	}
962
963	modifyReceived.reset()
964	attribReceived.reset()
965
966	// Doing a chmod on the file should trigger an event with the "attrib" flag set (the contents
967	// of the file are not changed though)
968	if err := os.Chmod(testFile, 0600); err != nil {
969		t.Fatalf("chmod failed: %s", err)
970	}
971
972	time.Sleep(500 * time.Millisecond)
973
974	if attribReceived.value() != 1 {
975		t.Fatal("didn't receive an attribute change after 500ms")
976	}
977
978	// Try closing the fsnotify instance
979	t.Log("calling Close()")
980	watcher.Close()
981	t.Log("waiting for the event channel to become closed...")
982	select {
983	case <-done:
984		t.Log("event channel closed")
985	case <-time.After(1e9):
986		t.Fatal("event stream was not closed after 1 second")
987	}
988
989	os.Remove(testFile)
990}
991
992func TestFsnotifyClose(t *testing.T) {
993	watcher := newWatcher(t)
994	watcher.Close()
995
996	var done int32
997	go func() {
998		watcher.Close()
999		atomic.StoreInt32(&done, 1)
1000	}()
1001
1002	time.Sleep(50e6) // 50 ms
1003	if atomic.LoadInt32(&done) == 0 {
1004		t.Fatal("double Close() test failed: second Close() call didn't return")
1005	}
1006
1007	testDir := tempMkdir(t)
1008	defer os.RemoveAll(testDir)
1009
1010	if err := watcher.Add(testDir); err == nil {
1011		t.Fatal("expected error on Watch() after Close(), got nil")
1012	}
1013}
1014
1015func TestFsnotifyFakeSymlink(t *testing.T) {
1016	if runtime.GOOS == "windows" {
1017		t.Skip("symlinks don't work on Windows.")
1018	}
1019
1020	watcher := newWatcher(t)
1021
1022	// Create directory to watch
1023	testDir := tempMkdir(t)
1024	defer os.RemoveAll(testDir)
1025
1026	var errorsReceived counter
1027	// Receive errors on the error channel on a separate goroutine
1028	go func() {
1029		for errors := range watcher.Errors {
1030			t.Logf("Received error: %s", errors)
1031			errorsReceived.increment()
1032		}
1033	}()
1034
1035	// Count the CREATE events received
1036	var createEventsReceived, otherEventsReceived counter
1037	go func() {
1038		for ev := range watcher.Events {
1039			t.Logf("event received: %s", ev)
1040			if ev.Op&Create == Create {
1041				createEventsReceived.increment()
1042			} else {
1043				otherEventsReceived.increment()
1044			}
1045		}
1046	}()
1047
1048	addWatch(t, watcher, testDir)
1049
1050	if err := os.Symlink(filepath.Join(testDir, "zzz"), filepath.Join(testDir, "zzznew")); err != nil {
1051		t.Fatalf("Failed to create bogus symlink: %s", err)
1052	}
1053	t.Logf("Created bogus symlink")
1054
1055	// We expect this event to be received almost immediately, but let's wait 500 ms to be sure
1056	time.Sleep(500 * time.Millisecond)
1057
1058	// Should not be error, just no events for broken links (watching nothing)
1059	if errorsReceived.value() > 0 {
1060		t.Fatal("fsnotify errors have been received.")
1061	}
1062	if otherEventsReceived.value() > 0 {
1063		t.Fatal("fsnotify other events received on the broken link")
1064	}
1065
1066	// Except for 1 create event (for the link itself)
1067	if createEventsReceived.value() == 0 {
1068		t.Fatal("fsnotify create events were not received after 500 ms")
1069	}
1070	if createEventsReceived.value() > 1 {
1071		t.Fatal("fsnotify more create events received than expected")
1072	}
1073
1074	// Try closing the fsnotify instance
1075	t.Log("calling Close()")
1076	watcher.Close()
1077}
1078
1079func TestCyclicSymlink(t *testing.T) {
1080	if runtime.GOOS == "windows" {
1081		t.Skip("symlinks don't work on Windows.")
1082	}
1083
1084	watcher := newWatcher(t)
1085
1086	testDir := tempMkdir(t)
1087	defer os.RemoveAll(testDir)
1088
1089	link := path.Join(testDir, "link")
1090	if err := os.Symlink(".", link); err != nil {
1091		t.Fatalf("could not make symlink: %v", err)
1092	}
1093	addWatch(t, watcher, testDir)
1094
1095	var createEventsReceived counter
1096	go func() {
1097		for ev := range watcher.Events {
1098			if ev.Op&Create == Create {
1099				createEventsReceived.increment()
1100			}
1101		}
1102	}()
1103
1104	if err := os.Remove(link); err != nil {
1105		t.Fatalf("Error removing link: %v", err)
1106	}
1107
1108	// It would be nice to be able to expect a delete event here, but kqueue has
1109	// no way for us to get events on symlinks themselves, because opening them
1110	// opens an fd to the file to which they point.
1111
1112	if err := ioutil.WriteFile(link, []byte("foo"), 0700); err != nil {
1113		t.Fatalf("could not make symlink: %v", err)
1114	}
1115
1116	// We expect this event to be received almost immediately, but let's wait 500 ms to be sure
1117	time.Sleep(500 * time.Millisecond)
1118
1119	if got := createEventsReceived.value(); got == 0 {
1120		t.Errorf("want at least 1 create event got %v", got)
1121	}
1122
1123	watcher.Close()
1124}
1125
1126// TestConcurrentRemovalOfWatch tests that concurrent calls to RemoveWatch do not race.
1127// See https://codereview.appspot.com/103300045/
1128// go test -test.run=TestConcurrentRemovalOfWatch -test.cpu=1,1,1,1,1 -race
1129func TestConcurrentRemovalOfWatch(t *testing.T) {
1130	if runtime.GOOS != "darwin" {
1131		t.Skip("regression test for race only present on darwin")
1132	}
1133
1134	// Create directory to watch
1135	testDir := tempMkdir(t)
1136	defer os.RemoveAll(testDir)
1137
1138	// Create a file before watching directory
1139	testFileAlreadyExists := filepath.Join(testDir, "TestFsnotifyEventsExisting.testfile")
1140	{
1141		var f *os.File
1142		f, err := os.OpenFile(testFileAlreadyExists, os.O_WRONLY|os.O_CREATE, 0666)
1143		if err != nil {
1144			t.Fatalf("creating test file failed: %s", err)
1145		}
1146		f.Sync()
1147		f.Close()
1148	}
1149
1150	watcher := newWatcher(t)
1151	defer watcher.Close()
1152
1153	addWatch(t, watcher, testDir)
1154
1155	// Test that RemoveWatch can be invoked concurrently, with no data races.
1156	removed1 := make(chan struct{})
1157	go func() {
1158		defer close(removed1)
1159		watcher.Remove(testDir)
1160	}()
1161	removed2 := make(chan struct{})
1162	go func() {
1163		close(removed2)
1164		watcher.Remove(testDir)
1165	}()
1166	<-removed1
1167	<-removed2
1168}
1169
1170func TestClose(t *testing.T) {
1171	// Regression test for #59 bad file descriptor from Close
1172	testDir := tempMkdir(t)
1173	defer os.RemoveAll(testDir)
1174
1175	watcher := newWatcher(t)
1176	if err := watcher.Add(testDir); err != nil {
1177		t.Fatalf("Expected no error on Add, got %v", err)
1178	}
1179	err := watcher.Close()
1180	if err != nil {
1181		t.Fatalf("Expected no error on Close, got %v.", err)
1182	}
1183}
1184
1185// TestRemoveWithClose tests if one can handle Remove events and, at the same
1186// time, close Watcher object without any data races.
1187func TestRemoveWithClose(t *testing.T) {
1188	testDir := tempMkdir(t)
1189	defer os.RemoveAll(testDir)
1190
1191	const fileN = 200
1192	tempFiles := make([]string, 0, fileN)
1193	for i := 0; i < fileN; i++ {
1194		tempFiles = append(tempFiles, tempMkFile(t, testDir))
1195	}
1196	watcher := newWatcher(t)
1197	if err := watcher.Add(testDir); err != nil {
1198		t.Fatalf("Expected no error on Add, got %v", err)
1199	}
1200	startC, stopC := make(chan struct{}), make(chan struct{})
1201	errC := make(chan error)
1202	go func() {
1203		for {
1204			select {
1205			case <-watcher.Errors:
1206			case <-watcher.Events:
1207			case <-stopC:
1208				return
1209			}
1210		}
1211	}()
1212	go func() {
1213		<-startC
1214		for _, fileName := range tempFiles {
1215			os.Remove(fileName)
1216		}
1217	}()
1218	go func() {
1219		<-startC
1220		errC <- watcher.Close()
1221	}()
1222	close(startC)
1223	defer close(stopC)
1224	if err := <-errC; err != nil {
1225		t.Fatalf("Expected no error on Close, got %v.", err)
1226	}
1227}
1228
1229func testRename(file1, file2 string) error {
1230	switch runtime.GOOS {
1231	case "windows", "plan9":
1232		return os.Rename(file1, file2)
1233	default:
1234		cmd := exec.Command("mv", file1, file2)
1235		return cmd.Run()
1236	}
1237}
1238