View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.io.monitor;
18  
19  import static org.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.assertFalse;
21  import static org.junit.jupiter.api.Assertions.assertTrue;
22  
23  import java.io.File;
24  import java.io.FileFilter;
25  import java.io.IOException;
26  import java.util.Iterator;
27  
28  import org.apache.commons.io.FileUtils;
29  import org.apache.commons.io.filefilter.CanReadFileFilter;
30  import org.apache.commons.io.filefilter.FileFilterUtils;
31  import org.junit.jupiter.api.Test;
32  
33  /**
34   * {@link FileAlterationObserver} Test Case.
35   */
36  public class FileAlterationObserverTest extends AbstractMonitorTest {
37  
38      /**
39       * Constructs a new instance.
40       */
41      public FileAlterationObserverTest() {
42          listener = new CollectionFileListener(true);
43      }
44  
45      /**
46       * Call {@link FileAlterationObserver#checkAndNotify()}.
47       */
48      protected void checkAndNotify() {
49          observer.checkAndNotify();
50      }
51  
52      /**
53       * Test add/remove listeners.
54       */
55      @Test
56      public void testAddRemoveListeners() {
57          final FileAlterationObserver observer = new FileAlterationObserver("/foo");
58          // Null Listener
59          observer.addListener(null);
60          assertFalse(observer.getListeners().iterator().hasNext(), "Listeners[1]");
61          observer.removeListener(null);
62          assertFalse(observer.getListeners().iterator().hasNext(), "Listeners[2]");
63  
64          // Add Listener
65          final FileAlterationListenerAdaptor listener = new FileAlterationListenerAdaptor();
66          observer.addListener(listener);
67          final Iterator<FileAlterationListener> it = observer.getListeners().iterator();
68          assertTrue(it.hasNext(), "Listeners[3]");
69          assertEquals(listener, it.next(), "Added");
70          assertFalse(it.hasNext(), "Listeners[4]");
71  
72          // Remove Listener
73          observer.removeListener(listener);
74          assertFalse(observer.getListeners().iterator().hasNext(), "Listeners[5]");
75      }
76  
77      /**
78       * Test checkAndNotify() method
79       *
80       * @throws Exception
81       */
82      @Test
83      public void testDirectory() throws Exception {
84          checkAndNotify();
85          checkCollectionsEmpty("A");
86          final File testDirA = new File(testDir, "test-dir-A");
87          final File testDirB = new File(testDir, "test-dir-B");
88          final File testDirC = new File(testDir, "test-dir-C");
89          testDirA.mkdir();
90          testDirB.mkdir();
91          testDirC.mkdir();
92          final File testDirAFile1 = touch(new File(testDirA, "A-file1.java"));
93          final File testDirAFile2 = touch(new File(testDirA, "A-file2.txt")); // filter should ignore this
94          final File testDirAFile3 = touch(new File(testDirA, "A-file3.java"));
95          File testDirAFile4 = touch(new File(testDirA, "A-file4.java"));
96          final File testDirBFile1 = touch(new File(testDirB, "B-file1.java"));
97  
98          checkAndNotify();
99          checkCollectionSizes("B", 3, 0, 0, 4, 0, 0);
100         assertTrue(listener.getCreatedDirectories().contains(testDirA), "B testDirA");
101         assertTrue(listener.getCreatedDirectories().contains(testDirB), "B testDirB");
102         assertTrue(listener.getCreatedDirectories().contains(testDirC), "B testDirC");
103         assertTrue(listener.getCreatedFiles().contains(testDirAFile1), "B testDirAFile1");
104         assertFalse(listener.getCreatedFiles().contains(testDirAFile2), "B testDirAFile2");
105         assertTrue(listener.getCreatedFiles().contains(testDirAFile3), "B testDirAFile3");
106         assertTrue(listener.getCreatedFiles().contains(testDirAFile4), "B testDirAFile4");
107         assertTrue(listener.getCreatedFiles().contains(testDirBFile1), "B testDirBFile1");
108 
109         checkAndNotify();
110         checkCollectionsEmpty("C");
111 
112         testDirAFile4 = touch(testDirAFile4);
113         FileUtils.deleteDirectory(testDirB);
114         checkAndNotify();
115         checkCollectionSizes("D", 0, 0, 1, 0, 1, 1);
116         assertTrue(listener.getDeletedDirectories().contains(testDirB), "D testDirB");
117         assertTrue(listener.getChangedFiles().contains(testDirAFile4), "D testDirAFile4");
118         assertTrue(listener.getDeletedFiles().contains(testDirBFile1), "D testDirBFile1");
119 
120         FileUtils.deleteDirectory(testDir);
121         checkAndNotify();
122         checkCollectionSizes("E", 0, 0, 2, 0, 0, 3);
123         assertTrue(listener.getDeletedDirectories().contains(testDirA), "E testDirA");
124         assertTrue(listener.getDeletedFiles().contains(testDirAFile1), "E testDirAFile1");
125         assertFalse(listener.getDeletedFiles().contains(testDirAFile2), "E testDirAFile2");
126         assertTrue(listener.getDeletedFiles().contains(testDirAFile3), "E testDirAFile3");
127         assertTrue(listener.getDeletedFiles().contains(testDirAFile4), "E testDirAFile4");
128 
129         testDir.mkdir();
130         checkAndNotify();
131         checkCollectionsEmpty("F");
132 
133         checkAndNotify();
134         checkCollectionsEmpty("G");
135     }
136 
137     /**
138      * Test checkAndNotify() creating
139      *
140      * @throws IOException if an I/O error occurs.
141      */
142     @Test
143     public void testFileCreate() throws IOException {
144         checkAndNotify();
145         checkCollectionsEmpty("A");
146         File testDirA = new File(testDir, "test-dir-A");
147         testDirA.mkdir();
148         testDir = touch(testDir);
149         testDirA = touch(testDirA);
150         File testDirAFile1 = new File(testDirA, "A-file1.java");
151         final File testDirAFile2 = touch(new File(testDirA, "A-file2.java"));
152         File testDirAFile3 = new File(testDirA, "A-file3.java");
153         final File testDirAFile4 = touch(new File(testDirA, "A-file4.java"));
154         File testDirAFile5 = new File(testDirA, "A-file5.java");
155 
156         checkAndNotify();
157         checkCollectionSizes("B", 1, 0, 0, 2, 0, 0);
158         assertFalse(listener.getCreatedFiles().contains(testDirAFile1), "B testDirAFile1");
159         assertTrue(listener.getCreatedFiles().contains(testDirAFile2), "B testDirAFile2");
160         assertFalse(listener.getCreatedFiles().contains(testDirAFile3), "B testDirAFile3");
161         assertTrue(listener.getCreatedFiles().contains(testDirAFile4), "B testDirAFile4");
162         assertFalse(listener.getCreatedFiles().contains(testDirAFile5), "B testDirAFile5");
163 
164         assertFalse(testDirAFile1.exists(), "B testDirAFile1 exists");
165         assertTrue(testDirAFile2.exists(), "B testDirAFile2 exists");
166         assertFalse(testDirAFile3.exists(), "B testDirAFile3 exists");
167         assertTrue(testDirAFile4.exists(), "B testDirAFile4 exists");
168         assertFalse(testDirAFile5.exists(), "B testDirAFile5 exists");
169 
170         checkAndNotify();
171         checkCollectionsEmpty("C");
172 
173         // Create file with name < first entry
174         testDirAFile1 = touch(testDirAFile1);
175         testDirA = touch(testDirA);
176         checkAndNotify();
177         checkCollectionSizes("D", 0, 1, 0, 1, 0, 0);
178         assertTrue(testDirAFile1.exists(), "D testDirAFile1 exists");
179         assertTrue(listener.getCreatedFiles().contains(testDirAFile1), "D testDirAFile1");
180 
181         // Create file with name between 2 entries
182         testDirAFile3 = touch(testDirAFile3);
183         testDirA = touch(testDirA);
184         checkAndNotify();
185         checkCollectionSizes("E", 0, 1, 0, 1, 0, 0);
186         assertTrue(testDirAFile3.exists(), "E testDirAFile3 exists");
187         assertTrue(listener.getCreatedFiles().contains(testDirAFile3), "E testDirAFile3");
188 
189         // Create file with name > last entry
190         testDirAFile5 = touch(testDirAFile5);
191         testDirA = touch(testDirA);
192         checkAndNotify();
193         checkCollectionSizes("F", 0, 1, 0, 1, 0, 0);
194         assertTrue(testDirAFile5.exists(), "F testDirAFile5 exists");
195         assertTrue(listener.getCreatedFiles().contains(testDirAFile5), "F testDirAFile5");
196     }
197 
198     /**
199      * Test checkAndNotify() deleting
200      *
201      * @throws IOException if an I/O error occurs.
202      */
203     @Test
204     public void testFileDelete() throws IOException {
205         checkAndNotify();
206         checkCollectionsEmpty("A");
207         File testDirA = new File(testDir, "test-dir-A");
208         testDirA.mkdir();
209         testDir = touch(testDir);
210         testDirA = touch(testDirA);
211         final File testDirAFile1 = touch(new File(testDirA, "A-file1.java"));
212         final File testDirAFile2 = touch(new File(testDirA, "A-file2.java"));
213         final File testDirAFile3 = touch(new File(testDirA, "A-file3.java"));
214         final File testDirAFile4 = touch(new File(testDirA, "A-file4.java"));
215         final File testDirAFile5 = touch(new File(testDirA, "A-file5.java"));
216 
217         assertTrue(testDirAFile1.exists(), "B testDirAFile1 exists");
218         assertTrue(testDirAFile2.exists(), "B testDirAFile2 exists");
219         assertTrue(testDirAFile3.exists(), "B testDirAFile3 exists");
220         assertTrue(testDirAFile4.exists(), "B testDirAFile4 exists");
221         assertTrue(testDirAFile5.exists(), "B testDirAFile5 exists");
222 
223         checkAndNotify();
224         checkCollectionSizes("B", 1, 0, 0, 5, 0, 0);
225         assertTrue(listener.getCreatedFiles().contains(testDirAFile1), "B testDirAFile1");
226         assertTrue(listener.getCreatedFiles().contains(testDirAFile2), "B testDirAFile2");
227         assertTrue(listener.getCreatedFiles().contains(testDirAFile3), "B testDirAFile3");
228         assertTrue(listener.getCreatedFiles().contains(testDirAFile4), "B testDirAFile4");
229         assertTrue(listener.getCreatedFiles().contains(testDirAFile5), "B testDirAFile5");
230 
231         checkAndNotify();
232         checkCollectionsEmpty("C");
233 
234         // Delete first entry
235         FileUtils.deleteQuietly(testDirAFile1);
236         testDirA = touch(testDirA);
237         checkAndNotify();
238         checkCollectionSizes("D", 0, 1, 0, 0, 0, 1);
239         assertFalse(testDirAFile1.exists(), "D testDirAFile1 exists");
240         assertTrue(listener.getDeletedFiles().contains(testDirAFile1), "D testDirAFile1");
241 
242         // Delete file with name between 2 entries
243         FileUtils.deleteQuietly(testDirAFile3);
244         testDirA = touch(testDirA);
245         checkAndNotify();
246         checkCollectionSizes("E", 0, 1, 0, 0, 0, 1);
247         assertFalse(testDirAFile3.exists(), "E testDirAFile3 exists");
248         assertTrue(listener.getDeletedFiles().contains(testDirAFile3), "E testDirAFile3");
249 
250         // Delete last entry
251         FileUtils.deleteQuietly(testDirAFile5);
252         testDirA = touch(testDirA);
253         checkAndNotify();
254         checkCollectionSizes("F", 0, 1, 0, 0, 0, 1);
255         assertFalse(testDirAFile5.exists(), "F testDirAFile5 exists");
256         assertTrue(listener.getDeletedFiles().contains(testDirAFile5), "F testDirAFile5");
257     }
258 
259     /**
260      * Test checkAndNotify() creating
261      *
262      * @throws IOException if an I/O error occurs.
263      */
264     @Test
265     public void testFileUpdate() throws IOException {
266         checkAndNotify();
267         checkCollectionsEmpty("A");
268         File testDirA = new File(testDir, "test-dir-A");
269         testDirA.mkdir();
270         testDir = touch(testDir);
271         testDirA = touch(testDirA);
272         File testDirAFile1 = touch(new File(testDirA, "A-file1.java"));
273         final File testDirAFile2 = touch(new File(testDirA, "A-file2.java"));
274         File testDirAFile3 = touch(new File(testDirA, "A-file3.java"));
275         final File testDirAFile4 = touch(new File(testDirA, "A-file4.java"));
276         File testDirAFile5 = touch(new File(testDirA, "A-file5.java"));
277 
278         checkAndNotify();
279         checkCollectionSizes("B", 1, 0, 0, 5, 0, 0);
280         assertTrue(listener.getCreatedFiles().contains(testDirAFile1), "B testDirAFile1");
281         assertTrue(listener.getCreatedFiles().contains(testDirAFile2), "B testDirAFile2");
282         assertTrue(listener.getCreatedFiles().contains(testDirAFile3), "B testDirAFile3");
283         assertTrue(listener.getCreatedFiles().contains(testDirAFile4), "B testDirAFile4");
284         assertTrue(listener.getCreatedFiles().contains(testDirAFile5), "B testDirAFile5");
285 
286         assertTrue(testDirAFile1.exists(), "B testDirAFile1 exists");
287         assertTrue(testDirAFile2.exists(), "B testDirAFile2 exists");
288         assertTrue(testDirAFile3.exists(), "B testDirAFile3 exists");
289         assertTrue(testDirAFile4.exists(), "B testDirAFile4 exists");
290         assertTrue(testDirAFile5.exists(), "B testDirAFile5 exists");
291 
292         checkAndNotify();
293         checkCollectionsEmpty("C");
294 
295         // Update first entry
296         testDirAFile1 = touch(testDirAFile1);
297         testDirA = touch(testDirA);
298         checkAndNotify();
299         checkCollectionSizes("D", 0, 1, 0, 0, 1, 0);
300         assertTrue(listener.getChangedFiles().contains(testDirAFile1), "D testDirAFile1");
301 
302         // Update file with name between 2 entries
303         testDirAFile3 = touch(testDirAFile3);
304         testDirA = touch(testDirA);
305         checkAndNotify();
306         checkCollectionSizes("E", 0, 1, 0, 0, 1, 0);
307         assertTrue(listener.getChangedFiles().contains(testDirAFile3), "E testDirAFile3");
308 
309         // Update last entry
310         testDirAFile5 = touch(testDirAFile5);
311         testDirA = touch(testDirA);
312         checkAndNotify();
313         checkCollectionSizes("F", 0, 1, 0, 0, 1, 0);
314         assertTrue(listener.getChangedFiles().contains(testDirAFile5), "F testDirAFile5");
315     }
316 
317     /**
318      * Test checkAndNotify() method
319      *
320      * @throws IOException if an I/O error occurs.
321      */
322     @Test
323     public void testObserveSingleFile() throws IOException {
324         final File testDirA = new File(testDir, "test-dir-A");
325         File testDirAFile1 = new File(testDirA, "A-file1.java");
326         testDirA.mkdir();
327 
328         final FileFilter nameFilter = FileFilterUtils.nameFileFilter(testDirAFile1.getName());
329         createObserver(testDirA, nameFilter);
330         checkAndNotify();
331         checkCollectionsEmpty("A");
332         assertFalse(testDirAFile1.exists(), "A testDirAFile1 exists");
333 
334         // Create
335         testDirAFile1 = touch(testDirAFile1);
336         File testDirAFile2 = touch(new File(testDirA, "A-file2.txt")); /* filter should ignore */
337         File testDirAFile3 = touch(new File(testDirA, "A-file3.java")); /* filter should ignore */
338         assertTrue(testDirAFile1.exists(), "B testDirAFile1 exists");
339         assertTrue(testDirAFile2.exists(), "B testDirAFile2 exists");
340         assertTrue(testDirAFile3.exists(), "B testDirAFile3 exists");
341         checkAndNotify();
342         checkCollectionSizes("C", 0, 0, 0, 1, 0, 0);
343         assertTrue(listener.getCreatedFiles().contains(testDirAFile1), "C created");
344         assertFalse(listener.getCreatedFiles().contains(testDirAFile2), "C created");
345         assertFalse(listener.getCreatedFiles().contains(testDirAFile3), "C created");
346 
347         // Modify
348         testDirAFile1 = touch(testDirAFile1);
349         testDirAFile2 = touch(testDirAFile2);
350         testDirAFile3 = touch(testDirAFile3);
351         checkAndNotify();
352         checkCollectionSizes("D", 0, 0, 0, 0, 1, 0);
353         assertTrue(listener.getChangedFiles().contains(testDirAFile1), "D changed");
354         assertFalse(listener.getChangedFiles().contains(testDirAFile2), "D changed");
355         assertFalse(listener.getChangedFiles().contains(testDirAFile3), "D changed");
356 
357         // Delete
358         FileUtils.deleteQuietly(testDirAFile1);
359         FileUtils.deleteQuietly(testDirAFile2);
360         FileUtils.deleteQuietly(testDirAFile3);
361         assertFalse(testDirAFile1.exists(), "E testDirAFile1 exists");
362         assertFalse(testDirAFile2.exists(), "E testDirAFile2 exists");
363         assertFalse(testDirAFile3.exists(), "E testDirAFile3 exists");
364         checkAndNotify();
365         checkCollectionSizes("E", 0, 0, 0, 0, 0, 1);
366         assertTrue(listener.getDeletedFiles().contains(testDirAFile1), "E deleted");
367         assertFalse(listener.getDeletedFiles().contains(testDirAFile2), "E deleted");
368         assertFalse(listener.getDeletedFiles().contains(testDirAFile3), "E deleted");
369     }
370 
371     /**
372      * Test toString().
373      */
374     @Test
375     public void testToString() {
376         final File file = new File("/foo");
377 
378         FileAlterationObserver observer = new FileAlterationObserver(file);
379         assertEquals("FileAlterationObserver[file='" + file.getPath() + "', true, listeners=0]", observer.toString());
380 
381         observer = new FileAlterationObserver(file, CanReadFileFilter.CAN_READ);
382         assertEquals("FileAlterationObserver[file='" + file.getPath() + "', CanReadFileFilter, listeners=0]", observer.toString());
383 
384         assertEquals(file, observer.getDirectory());
385     }
386 }