notify_test.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. package watch
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "os"
  6. "path/filepath"
  7. "runtime"
  8. "strings"
  9. "testing"
  10. "time"
  11. )
  12. // Each implementation of the notify interface should have the same basic
  13. // behavior.
  14. func TestNoEvents(t *testing.T) {
  15. f := newNotifyFixture(t)
  16. defer f.tearDown()
  17. f.assertEvents()
  18. }
  19. func TestEventOrdering(t *testing.T) {
  20. f := newNotifyFixture(t)
  21. defer f.tearDown()
  22. count := 8
  23. dirs := make([]string, count)
  24. for i, _ := range dirs {
  25. dir, err := f.root.NewDir("watched")
  26. if err != nil {
  27. t.Fatal(err)
  28. }
  29. dirs[i] = dir.Path()
  30. err = f.notify.Add(dir.Path())
  31. if err != nil {
  32. t.Fatal(err)
  33. }
  34. }
  35. f.fsync()
  36. f.events = nil
  37. var expected []string
  38. for i, dir := range dirs {
  39. base := fmt.Sprintf("%d.txt", i)
  40. p := filepath.Join(dir, base)
  41. err := ioutil.WriteFile(p, []byte(base), os.FileMode(0777))
  42. if err != nil {
  43. t.Fatal(err)
  44. }
  45. expected = append(expected, filepath.Join(dir, base))
  46. }
  47. f.assertEvents(expected...)
  48. }
  49. func TestWatchesAreRecursive(t *testing.T) {
  50. f := newNotifyFixture(t)
  51. defer f.tearDown()
  52. root, err := f.root.NewDir("root")
  53. if err != nil {
  54. t.Fatal(err)
  55. }
  56. // add a sub directory
  57. subPath := filepath.Join(root.Path(), "sub")
  58. err = os.MkdirAll(subPath, os.ModePerm)
  59. if err != nil {
  60. t.Fatal(err)
  61. }
  62. // watch parent
  63. err = f.notify.Add(root.Path())
  64. if err != nil {
  65. t.Fatal(err)
  66. }
  67. f.fsync()
  68. f.events = nil
  69. // change sub directory
  70. changeFilePath := filepath.Join(subPath, "change")
  71. _, err = os.OpenFile(changeFilePath, os.O_RDONLY|os.O_CREATE, 0666)
  72. if err != nil {
  73. t.Fatal(err)
  74. }
  75. f.assertEvents(changeFilePath)
  76. }
  77. func TestNewDirectoriesAreRecursivelyWatched(t *testing.T) {
  78. f := newNotifyFixture(t)
  79. defer f.tearDown()
  80. root, err := f.root.NewDir("root")
  81. if err != nil {
  82. t.Fatal(err)
  83. }
  84. // watch parent
  85. err = f.notify.Add(root.Path())
  86. if err != nil {
  87. t.Fatal(err)
  88. }
  89. f.fsync()
  90. f.events = nil
  91. // add a sub directory
  92. subPath := filepath.Join(root.Path(), "sub")
  93. err = os.MkdirAll(subPath, os.ModePerm)
  94. if err != nil {
  95. f.t.Fatal(err)
  96. }
  97. // change something inside sub directory
  98. changeFilePath := filepath.Join(subPath, "change")
  99. _, err = os.OpenFile(changeFilePath, os.O_RDONLY|os.O_CREATE, 0666)
  100. if err != nil {
  101. t.Fatal(err)
  102. }
  103. f.assertEvents(subPath, changeFilePath)
  104. }
  105. func TestWatchNonExistentPath(t *testing.T) {
  106. f := newNotifyFixture(t)
  107. defer f.tearDown()
  108. root, err := f.root.NewDir("root")
  109. if err != nil {
  110. t.Fatal(err)
  111. }
  112. path := filepath.Join(root.Path(), "change")
  113. err = f.notify.Add(path)
  114. if err != nil {
  115. t.Fatal(err)
  116. }
  117. d1 := []byte("hello\ngo\n")
  118. err = ioutil.WriteFile(path, d1, 0644)
  119. if err != nil {
  120. t.Fatal(err)
  121. }
  122. f.assertEvents(path)
  123. }
  124. func TestRemove(t *testing.T) {
  125. f := newNotifyFixture(t)
  126. defer f.tearDown()
  127. root, err := f.root.NewDir("root")
  128. if err != nil {
  129. t.Fatal(err)
  130. }
  131. path := filepath.Join(root.Path(), "change")
  132. if err != nil {
  133. t.Fatal(err)
  134. }
  135. d1 := []byte("hello\ngo\n")
  136. err = ioutil.WriteFile(path, d1, 0644)
  137. if err != nil {
  138. t.Fatal(err)
  139. }
  140. err = f.notify.Add(path)
  141. if err != nil {
  142. t.Fatal(err)
  143. }
  144. f.fsync()
  145. f.events = nil
  146. err = os.Remove(path)
  147. if err != nil {
  148. t.Fatal(err)
  149. }
  150. f.assertEvents(path)
  151. }
  152. func TestRemoveAndAddBack(t *testing.T) {
  153. t.Skip("Skipping broken test for now")
  154. f := newNotifyFixture(t)
  155. defer f.tearDown()
  156. root, err := f.root.NewDir("root")
  157. if err != nil {
  158. t.Fatal(err)
  159. }
  160. path := filepath.Join(root.Path(), "change")
  161. if err != nil {
  162. t.Fatal(err)
  163. }
  164. d1 := []byte("hello\ngo\n")
  165. err = ioutil.WriteFile(path, d1, 0644)
  166. if err != nil {
  167. t.Fatal(err)
  168. }
  169. err = f.notify.Add(path)
  170. if err != nil {
  171. t.Fatal(err)
  172. }
  173. err = os.Remove(path)
  174. if err != nil {
  175. t.Fatal(err)
  176. }
  177. f.assertEvents(path)
  178. f.events = nil
  179. err = ioutil.WriteFile(path, d1, 0644)
  180. if err != nil {
  181. t.Fatal(err)
  182. }
  183. f.assertEvents(path)
  184. }
  185. func TestSingleFile(t *testing.T) {
  186. if runtime.GOOS != "darwin" {
  187. t.Skip("Broken on Linux")
  188. }
  189. f := newNotifyFixture(t)
  190. defer f.tearDown()
  191. root, err := f.root.NewDir("root")
  192. if err != nil {
  193. t.Fatal(err)
  194. }
  195. path := filepath.Join(root.Path(), "change")
  196. if err != nil {
  197. t.Fatal(err)
  198. }
  199. d1 := []byte("hello\ngo\n")
  200. err = ioutil.WriteFile(path, d1, 0644)
  201. if err != nil {
  202. t.Fatal(err)
  203. }
  204. err = f.notify.Add(path)
  205. if err != nil {
  206. t.Fatal(err)
  207. }
  208. d2 := []byte("hello\nworld\n")
  209. err = ioutil.WriteFile(path, d2, 0644)
  210. if err != nil {
  211. t.Fatal(err)
  212. }
  213. f.assertEvents(path)
  214. }
  215. type notifyFixture struct {
  216. t *testing.T
  217. root *TempDir
  218. watched *TempDir
  219. notify Notify
  220. events []FileEvent
  221. }
  222. func newNotifyFixture(t *testing.T) *notifyFixture {
  223. SetLimitChecksEnabled(false)
  224. notify, err := NewWatcher()
  225. if err != nil {
  226. t.Fatal(err)
  227. }
  228. root, err := NewDir(t.Name())
  229. if err != nil {
  230. t.Fatal(err)
  231. }
  232. watched, err := root.NewDir("watched")
  233. if err != nil {
  234. t.Fatal(err)
  235. }
  236. err = notify.Add(watched.Path())
  237. if err != nil {
  238. t.Fatal(err)
  239. }
  240. return &notifyFixture{
  241. t: t,
  242. root: root,
  243. watched: watched,
  244. notify: notify,
  245. }
  246. }
  247. func (f *notifyFixture) assertEvents(expected ...string) {
  248. f.fsync()
  249. if len(f.events) != len(expected) {
  250. f.t.Fatalf("Got %d events (expected %d): %v %v", len(f.events), len(expected), f.events, expected)
  251. }
  252. for i, actual := range f.events {
  253. e := FileEvent{expected[i]}
  254. if actual != e {
  255. f.t.Fatalf("Got event %v (expected %v)", actual, e)
  256. }
  257. }
  258. }
  259. func (f *notifyFixture) fsync() {
  260. syncPathBase := fmt.Sprintf("sync-%d.txt", time.Now().UnixNano())
  261. syncPath := filepath.Join(f.watched.Path(), syncPathBase)
  262. anySyncPath := filepath.Join(f.watched.Path(), "sync-")
  263. timeout := time.After(time.Second)
  264. err := ioutil.WriteFile(syncPath, []byte(fmt.Sprintf("%s", time.Now())), os.FileMode(0777))
  265. if err != nil {
  266. f.t.Fatal(err)
  267. }
  268. F:
  269. for {
  270. select {
  271. case err := <-f.notify.Errors():
  272. f.t.Fatal(err)
  273. case event := <-f.notify.Events():
  274. if strings.Contains(event.Path, syncPath) {
  275. break F
  276. }
  277. if strings.Contains(event.Path, anySyncPath) {
  278. continue
  279. }
  280. // Don't bother tracking duplicate changes to the same path
  281. // for testing.
  282. if len(f.events) > 0 && f.events[len(f.events)-1].Path == event.Path {
  283. continue
  284. }
  285. f.events = append(f.events, event)
  286. case <-timeout:
  287. f.t.Fatalf("fsync: timeout")
  288. }
  289. }
  290. if err != nil {
  291. f.t.Fatal(err)
  292. }
  293. }
  294. func (f *notifyFixture) tearDown() {
  295. SetLimitChecksEnabled(true)
  296. err := f.root.TearDown()
  297. if err != nil {
  298. f.t.Fatal(err)
  299. }
  300. err = f.notify.Close()
  301. if err != nil {
  302. f.t.Fatal(err)
  303. }
  304. }