OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 import 'package:scheduled_test/scheduled_test.dart'; | 5 import 'package:scheduled_test/scheduled_test.dart'; |
| 6 import 'package:watcher/src/utils.dart'; |
6 | 7 |
7 import '../utils.dart'; | 8 import '../utils.dart'; |
8 | 9 |
9 sharedTests() { | 10 sharedTests() { |
10 test('does not notify for files that already exist when started', () { | 11 test('does not notify for files that already exist when started', () { |
11 // Make some pre-existing files. | 12 // Make some pre-existing files. |
12 writeFile("a.txt"); | 13 writeFile("a.txt"); |
13 writeFile("b.txt"); | 14 writeFile("b.txt"); |
14 | 15 |
15 startWatcher(); | 16 startWatcher(); |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
51 expectModifyEvent("file.txt"); | 52 expectModifyEvent("file.txt"); |
52 }); | 53 }); |
53 | 54 |
54 test('notifies even if the file contents are unchanged', () { | 55 test('notifies even if the file contents are unchanged', () { |
55 writeFile("a.txt", contents: "same"); | 56 writeFile("a.txt", contents: "same"); |
56 writeFile("b.txt", contents: "before"); | 57 writeFile("b.txt", contents: "before"); |
57 startWatcher(); | 58 startWatcher(); |
58 | 59 |
59 writeFile("a.txt", contents: "same"); | 60 writeFile("a.txt", contents: "same"); |
60 writeFile("b.txt", contents: "after"); | 61 writeFile("b.txt", contents: "after"); |
61 inAnyOrder(() { | 62 inAnyOrder([ |
62 expectModifyEvent("a.txt"); | 63 isModifyEvent("a.txt"), |
63 expectModifyEvent("b.txt"); | 64 isModifyEvent("b.txt") |
64 }); | 65 ]); |
65 }); | 66 }); |
66 | 67 |
67 test('when the watched directory is deleted, removes all files', () { | 68 test('when the watched directory is deleted, removes all files', () { |
68 writeFile("dir/a.txt"); | 69 writeFile("dir/a.txt"); |
69 writeFile("dir/b.txt"); | 70 writeFile("dir/b.txt"); |
70 | 71 |
71 startWatcher(dir: "dir"); | 72 startWatcher(dir: "dir"); |
72 | 73 |
73 deleteDir("dir"); | 74 deleteDir("dir"); |
74 inAnyOrder(() { | 75 inAnyOrder([ |
75 expectRemoveEvent("dir/a.txt"); | 76 isRemoveEvent("dir/a.txt"), |
76 expectRemoveEvent("dir/b.txt"); | 77 isRemoveEvent("dir/b.txt") |
77 }); | 78 ]); |
78 }); | 79 }); |
79 | 80 |
80 group("moves", () { | 81 group("moves", () { |
81 test('notifies when a file is moved within the watched directory', () { | 82 test('notifies when a file is moved within the watched directory', () { |
82 writeFile("old.txt"); | 83 writeFile("old.txt"); |
83 startWatcher(); | 84 startWatcher(); |
84 renameFile("old.txt", "new.txt"); | 85 renameFile("old.txt", "new.txt"); |
85 | 86 |
86 inAnyOrder(() { | 87 inAnyOrder([ |
87 expectAddEvent("new.txt"); | 88 isAddEvent("new.txt"), |
88 expectRemoveEvent("old.txt"); | 89 isRemoveEvent("old.txt") |
89 }); | 90 ]); |
90 }); | 91 }); |
91 | 92 |
92 test('notifies when a file is moved from outside the watched directory', | 93 test('notifies when a file is moved from outside the watched directory', |
93 () { | 94 () { |
94 writeFile("old.txt"); | 95 writeFile("old.txt"); |
95 createDir("dir"); | 96 createDir("dir"); |
96 startWatcher(dir: "dir"); | 97 startWatcher(dir: "dir"); |
97 | 98 |
98 renameFile("old.txt", "dir/new.txt"); | 99 renameFile("old.txt", "dir/new.txt"); |
99 expectAddEvent("dir/new.txt"); | 100 expectAddEvent("dir/new.txt"); |
100 }); | 101 }); |
101 | 102 |
102 test('notifies when a file is moved outside the watched directory', () { | 103 test('notifies when a file is moved outside the watched directory', () { |
103 writeFile("dir/old.txt"); | 104 writeFile("dir/old.txt"); |
104 startWatcher(dir: "dir"); | 105 startWatcher(dir: "dir"); |
105 | 106 |
106 renameFile("dir/old.txt", "new.txt"); | 107 renameFile("dir/old.txt", "new.txt"); |
107 expectRemoveEvent("dir/old.txt"); | 108 expectRemoveEvent("dir/old.txt"); |
108 }); | 109 }); |
109 }); | 110 }); |
110 | 111 |
| 112 // Most of the time, when multiple filesystem actions happen in sequence, |
| 113 // they'll be batched together and the watcher will see them all at once. |
| 114 // These tests verify that the watcher normalizes and combine these events |
| 115 // properly. However, very occasionally the events will be reported in |
| 116 // separate batches, and the watcher will report them as though they occurred |
| 117 // far apart in time, so each of these tests has a "backup case" to allow for |
| 118 // that as well. |
111 group("clustered changes", () { | 119 group("clustered changes", () { |
112 test("doesn't notify when a file is created and then immediately removed", | 120 test("doesn't notify when a file is created and then immediately removed", |
113 () { | 121 () { |
114 startWatcher(); | 122 startWatcher(); |
115 writeFile("file.txt"); | 123 writeFile("file.txt"); |
116 deleteFile("file.txt"); | 124 deleteFile("file.txt"); |
117 | 125 |
118 // [startWatcher] will assert that no events were fired. | 126 // Backup case. |
| 127 startClosingEventStream(); |
| 128 allowEvents(() { |
| 129 expectAddEvent("file.txt"); |
| 130 expectRemoveEvent("file.txt"); |
| 131 }); |
119 }); | 132 }); |
120 | 133 |
121 test("reports a modification when a file is deleted and then immediately " | 134 test("reports a modification when a file is deleted and then immediately " |
122 "recreated", () { | 135 "recreated", () { |
123 writeFile("file.txt"); | 136 writeFile("file.txt"); |
124 startWatcher(); | 137 startWatcher(); |
125 | 138 |
126 deleteFile("file.txt"); | 139 deleteFile("file.txt"); |
127 writeFile("file.txt", contents: "re-created"); | 140 writeFile("file.txt", contents: "re-created"); |
128 expectModifyEvent("file.txt"); | 141 |
| 142 allowEither(() { |
| 143 expectModifyEvent("file.txt"); |
| 144 }, () { |
| 145 // Backup case. |
| 146 expectRemoveEvent("file.txt"); |
| 147 expectAddEvent("file.txt"); |
| 148 }); |
129 }); | 149 }); |
130 | 150 |
131 test("reports a modification when a file is moved and then immediately " | 151 test("reports a modification when a file is moved and then immediately " |
132 "recreated", () { | 152 "recreated", () { |
133 writeFile("old.txt"); | 153 writeFile("old.txt"); |
134 startWatcher(); | 154 startWatcher(); |
135 | 155 |
136 renameFile("old.txt", "new.txt"); | 156 renameFile("old.txt", "new.txt"); |
137 writeFile("old.txt", contents: "re-created"); | 157 writeFile("old.txt", contents: "re-created"); |
138 inAnyOrder(() { | 158 |
139 expectModifyEvent("old.txt"); | 159 allowEither(() { |
| 160 inAnyOrder([ |
| 161 isModifyEvent("old.txt"), |
| 162 isAddEvent("new.txt") |
| 163 ]); |
| 164 }, () { |
| 165 // Backup case. |
| 166 expectRemoveEvent("old.txt"); |
140 expectAddEvent("new.txt"); | 167 expectAddEvent("new.txt"); |
| 168 expectAddEvent("old.txt"); |
141 }); | 169 }); |
142 }); | 170 }); |
143 | 171 |
144 test("reports a removal when a file is modified and then immediately " | 172 test("reports a removal when a file is modified and then immediately " |
145 "removed", () { | 173 "removed", () { |
146 writeFile("file.txt"); | 174 writeFile("file.txt"); |
147 startWatcher(); | 175 startWatcher(); |
148 | 176 |
149 writeFile("file.txt", contents: "modified"); | 177 writeFile("file.txt", contents: "modified"); |
150 deleteFile("file.txt"); | 178 deleteFile("file.txt"); |
| 179 |
| 180 // Backup case. |
| 181 allowModifyEvent("file.txt"); |
| 182 |
151 expectRemoveEvent("file.txt"); | 183 expectRemoveEvent("file.txt"); |
152 }); | 184 }); |
153 | 185 |
154 test("reports an add when a file is added and then immediately modified", | 186 test("reports an add when a file is added and then immediately modified", |
155 () { | 187 () { |
156 startWatcher(); | 188 startWatcher(); |
157 | 189 |
158 writeFile("file.txt"); | 190 writeFile("file.txt"); |
159 writeFile("file.txt", contents: "modified"); | 191 writeFile("file.txt", contents: "modified"); |
| 192 |
160 expectAddEvent("file.txt"); | 193 expectAddEvent("file.txt"); |
| 194 |
| 195 // Backup case. |
| 196 startClosingEventStream(); |
| 197 allowModifyEvent("file.txt"); |
161 }); | 198 }); |
162 }); | 199 }); |
163 | 200 |
164 group("subdirectories", () { | 201 group("subdirectories", () { |
165 test('watches files in subdirectories', () { | 202 test('watches files in subdirectories', () { |
166 startWatcher(); | 203 startWatcher(); |
167 writeFile("a/b/c/d/file.txt"); | 204 writeFile("a/b/c/d/file.txt"); |
168 expectAddEvent("a/b/c/d/file.txt"); | 205 expectAddEvent("a/b/c/d/file.txt"); |
169 }); | 206 }); |
170 | 207 |
171 test('notifies when a subdirectory is moved within the watched directory ' | 208 test('notifies when a subdirectory is moved within the watched directory ' |
172 'and then its contents are modified', () { | 209 'and then its contents are modified', () { |
173 writeFile("old/file.txt"); | 210 writeFile("old/file.txt"); |
174 startWatcher(); | 211 startWatcher(); |
175 | 212 |
176 renameDir("old", "new"); | 213 renameDir("old", "new"); |
177 inAnyOrder(() { | 214 inAnyOrder([ |
178 expectRemoveEvent("old/file.txt"); | 215 isRemoveEvent("old/file.txt"), |
179 expectAddEvent("new/file.txt"); | 216 isAddEvent("new/file.txt") |
180 }); | 217 ]); |
181 | 218 |
182 writeFile("new/file.txt", contents: "modified"); | 219 writeFile("new/file.txt", contents: "modified"); |
183 expectModifyEvent("new/file.txt"); | 220 expectModifyEvent("new/file.txt"); |
184 }); | 221 }); |
185 | 222 |
186 test('emits events for many nested files added at once', () { | 223 test('emits events for many nested files added at once', () { |
187 withPermutations((i, j, k) => | 224 withPermutations((i, j, k) => |
188 writeFile("sub/sub-$i/sub-$j/file-$k.txt")); | 225 writeFile("sub/sub-$i/sub-$j/file-$k.txt")); |
189 | 226 |
190 createDir("dir"); | 227 createDir("dir"); |
191 startWatcher(dir: "dir"); | 228 startWatcher(dir: "dir"); |
192 renameDir("sub", "dir/sub"); | 229 renameDir("sub", "dir/sub"); |
193 | 230 |
194 inAnyOrder(() { | 231 inAnyOrder(withPermutations((i, j, k) => |
195 withPermutations((i, j, k) => | 232 isAddEvent("dir/sub/sub-$i/sub-$j/file-$k.txt"))); |
196 expectAddEvent("dir/sub/sub-$i/sub-$j/file-$k.txt")); | |
197 }); | |
198 }); | 233 }); |
199 | 234 |
200 test('emits events for many nested files removed at once', () { | 235 test('emits events for many nested files removed at once', () { |
201 withPermutations((i, j, k) => | 236 withPermutations((i, j, k) => |
202 writeFile("dir/sub/sub-$i/sub-$j/file-$k.txt")); | 237 writeFile("dir/sub/sub-$i/sub-$j/file-$k.txt")); |
203 | 238 |
204 createDir("dir"); | 239 createDir("dir"); |
205 startWatcher(dir: "dir"); | 240 startWatcher(dir: "dir"); |
206 | 241 |
207 // Rename the directory rather than deleting it because native watchers | 242 // Rename the directory rather than deleting it because native watchers |
208 // report a rename as a single DELETE event for the directory, whereas | 243 // report a rename as a single DELETE event for the directory, whereas |
209 // they report recursive deletion with DELETE events for every file in the | 244 // they report recursive deletion with DELETE events for every file in the |
210 // directory. | 245 // directory. |
211 renameDir("dir/sub", "sub"); | 246 renameDir("dir/sub", "sub"); |
212 | 247 |
213 inAnyOrder(() { | 248 inAnyOrder(withPermutations((i, j, k) => |
214 withPermutations((i, j, k) => | 249 isRemoveEvent("dir/sub/sub-$i/sub-$j/file-$k.txt"))); |
215 expectRemoveEvent("dir/sub/sub-$i/sub-$j/file-$k.txt")); | |
216 }); | |
217 }); | 250 }); |
218 | 251 |
219 test('emits events for many nested files moved at once', () { | 252 test('emits events for many nested files moved at once', () { |
220 withPermutations((i, j, k) => | 253 withPermutations((i, j, k) => |
221 writeFile("dir/old/sub-$i/sub-$j/file-$k.txt")); | 254 writeFile("dir/old/sub-$i/sub-$j/file-$k.txt")); |
222 | 255 |
223 createDir("dir"); | 256 createDir("dir"); |
224 startWatcher(dir: "dir"); | 257 startWatcher(dir: "dir"); |
225 renameDir("dir/old", "dir/new"); | 258 renameDir("dir/old", "dir/new"); |
226 | 259 |
227 inAnyOrder(() { | 260 inAnyOrder(unionAll(withPermutations((i, j, k) { |
228 withPermutations((i, j, k) { | 261 return new Set.from([ |
229 expectRemoveEvent("dir/old/sub-$i/sub-$j/file-$k.txt"); | 262 isRemoveEvent("dir/old/sub-$i/sub-$j/file-$k.txt"), |
230 expectAddEvent("dir/new/sub-$i/sub-$j/file-$k.txt"); | 263 isAddEvent("dir/new/sub-$i/sub-$j/file-$k.txt") |
231 }); | 264 ]); |
232 }); | 265 }))); |
233 }); | 266 }); |
234 | 267 |
235 test("emits events for many files added at once in a subdirectory with the " | 268 test("emits events for many files added at once in a subdirectory with the " |
236 "same name as a removed file", () { | 269 "same name as a removed file", () { |
237 writeFile("dir/sub"); | 270 writeFile("dir/sub"); |
238 withPermutations((i, j, k) => | 271 withPermutations((i, j, k) => |
239 writeFile("old/sub-$i/sub-$j/file-$k.txt")); | 272 writeFile("old/sub-$i/sub-$j/file-$k.txt")); |
240 startWatcher(dir: "dir"); | 273 startWatcher(dir: "dir"); |
241 | 274 |
242 deleteFile("dir/sub"); | 275 deleteFile("dir/sub"); |
243 renameDir("old", "dir/sub"); | 276 renameDir("old", "dir/sub"); |
244 inAnyOrder(() { | 277 |
245 expectRemoveEvent("dir/sub"); | 278 var events = withPermutations((i, j, k) => |
246 withPermutations((i, j, k) => | 279 isAddEvent("dir/sub/sub-$i/sub-$j/file-$k.txt")); |
247 expectAddEvent("dir/sub/sub-$i/sub-$j/file-$k.txt")); | 280 events.add(isRemoveEvent("dir/sub")); |
248 }); | 281 inAnyOrder(events); |
249 }); | 282 }); |
250 }); | 283 }); |
251 } | 284 } |
OLD | NEW |