OSDN Git Service

new repo
[bytom/vapor.git] / vendor / github.com / fsnotify / fsnotify / integration_darwin_test.go
1 // Copyright 2016 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 package fsnotify
6
7 import (
8         "os"
9         "path/filepath"
10         "testing"
11         "time"
12
13         "golang.org/x/sys/unix"
14 )
15
16 // testExchangedataForWatcher tests the watcher with the exchangedata operation on macOS.
17 //
18 // This is widely used for atomic saves on macOS, e.g. TextMate and in Apple's NSDocument.
19 //
20 // See https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/exchangedata.2.html
21 // Also see: https://github.com/textmate/textmate/blob/cd016be29489eba5f3c09b7b70b06da134dda550/Frameworks/io/src/swap_file_data.cc#L20
22 func testExchangedataForWatcher(t *testing.T, watchDir bool) {
23         // Create directory to watch
24         testDir1 := tempMkdir(t)
25
26         // For the intermediate file
27         testDir2 := tempMkdir(t)
28
29         defer os.RemoveAll(testDir1)
30         defer os.RemoveAll(testDir2)
31
32         resolvedFilename := "TestFsnotifyEvents.file"
33
34         // TextMate does:
35         //
36         // 1. exchangedata (intermediate, resolved)
37         // 2. unlink intermediate
38         //
39         // Let's try to simulate that:
40         resolved := filepath.Join(testDir1, resolvedFilename)
41         intermediate := filepath.Join(testDir2, resolvedFilename+"~")
42
43         // Make sure we create the file before we start watching
44         createAndSyncFile(t, resolved)
45
46         watcher := newWatcher(t)
47
48         // Test both variants in isolation
49         if watchDir {
50                 addWatch(t, watcher, testDir1)
51         } else {
52                 addWatch(t, watcher, resolved)
53         }
54
55         // Receive errors on the error channel on a separate goroutine
56         go func() {
57                 for err := range watcher.Errors {
58                         t.Fatalf("error received: %s", err)
59                 }
60         }()
61
62         // Receive events on the event channel on a separate goroutine
63         eventstream := watcher.Events
64         var removeReceived counter
65         var createReceived counter
66
67         done := make(chan bool)
68
69         go func() {
70                 for event := range eventstream {
71                         // Only count relevant events
72                         if event.Name == filepath.Clean(resolved) {
73                                 if event.Op&Remove == Remove {
74                                         removeReceived.increment()
75                                 }
76                                 if event.Op&Create == Create {
77                                         createReceived.increment()
78                                 }
79                         }
80                         t.Logf("event received: %s", event)
81                 }
82                 done <- true
83         }()
84
85         // Repeat to make sure the watched file/directory "survives" the REMOVE/CREATE loop.
86         for i := 1; i <= 3; i++ {
87                 // The intermediate file is created in a folder outside the watcher
88                 createAndSyncFile(t, intermediate)
89
90                 // 1. Swap
91                 if err := unix.Exchangedata(intermediate, resolved, 0); err != nil {
92                         t.Fatalf("[%d] exchangedata failed: %s", i, err)
93                 }
94
95                 time.Sleep(50 * time.Millisecond)
96
97                 // 2. Delete the intermediate file
98                 err := os.Remove(intermediate)
99
100                 if err != nil {
101                         t.Fatalf("[%d] remove %s failed: %s", i, intermediate, err)
102                 }
103
104                 time.Sleep(50 * time.Millisecond)
105
106         }
107
108         // We expect this event to be received almost immediately, but let's wait 500 ms to be sure
109         time.Sleep(500 * time.Millisecond)
110
111         // The events will be (CHMOD + REMOVE + CREATE) X 2. Let's focus on the last two:
112         if removeReceived.value() < 3 {
113                 t.Fatal("fsnotify remove events have not been received after 500 ms")
114         }
115
116         if createReceived.value() < 3 {
117                 t.Fatal("fsnotify create events have not been received after 500 ms")
118         }
119
120         watcher.Close()
121         t.Log("waiting for the event channel to become closed...")
122         select {
123         case <-done:
124                 t.Log("event channel closed")
125         case <-time.After(2 * time.Second):
126                 t.Fatal("event stream was not closed after 2 seconds")
127         }
128 }
129
130 // TestExchangedataInWatchedDir test exchangedata operation on file in watched dir.
131 func TestExchangedataInWatchedDir(t *testing.T) {
132         testExchangedataForWatcher(t, true)
133 }
134
135 // TestExchangedataInWatchedDir test exchangedata operation on watched file.
136 func TestExchangedataInWatchedFile(t *testing.T) {
137         testExchangedataForWatcher(t, false)
138 }
139
140 func createAndSyncFile(t *testing.T, filepath string) {
141         f1, err := os.OpenFile(filepath, os.O_WRONLY|os.O_CREATE, 0666)
142         if err != nil {
143                 t.Fatalf("creating %s failed: %s", filepath, err)
144         }
145         f1.Sync()
146         f1.Close()
147 }