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.assertThrows;
22  import static org.junit.jupiter.api.Assertions.assertTrue;
23  import static org.junit.jupiter.api.Assertions.fail;
24  
25  import java.io.File;
26  import java.time.Duration;
27  import java.util.ArrayList;
28  import java.util.Arrays;
29  import java.util.Collection;
30  import java.util.Iterator;
31  import java.util.concurrent.Executors;
32  import java.util.concurrent.ThreadFactory;
33  
34  import org.apache.commons.io.ThreadUtils;
35  import org.apache.commons.io.test.TestUtils;
36  import org.junit.jupiter.api.Test;
37  
38  /**
39   * {@link FileAlterationMonitor} Test Case.
40   */
41  public class FileAlterationMonitorTest extends AbstractMonitorTest {
42  
43      /**
44       * Constructs a new test case.
45       */
46      public FileAlterationMonitorTest() {
47          listener = new CollectionFileListener(false);
48      }
49  
50      /**
51       * Check all the File Collections have the expected sizes.
52       */
53      private void checkFile(final String label, final File file, final Collection<File> files) {
54          for (int i = 0; i < 20; i++) {
55              if (files.contains(file)) {
56                  return; // found, test passes
57              }
58              TestUtils.sleepQuietly(pauseTime);
59          }
60          fail(label + " " + file + " not found");
61      }
62  
63      /**
64       * Test add/remove observers.
65       */
66      @Test
67      public void testAddRemoveObservers() {
68          FileAlterationObserver[] observers = null;
69  
70          // Null Observers
71          FileAlterationMonitor monitor = new FileAlterationMonitor(123, observers);
72          assertEquals(123, monitor.getInterval(), "Interval");
73          assertFalse(monitor.getObservers().iterator().hasNext(), "Observers[1]");
74  
75          // Null Observer
76          observers = new FileAlterationObserver[1]; // observer is null
77          monitor = new FileAlterationMonitor(456, observers);
78          assertFalse(monitor.getObservers().iterator().hasNext(), "Observers[2]");
79  
80          // Null Observer
81          monitor.addObserver(null);
82          assertFalse(monitor.getObservers().iterator().hasNext(), "Observers[3]");
83          monitor.removeObserver(null);
84  
85          // Add Observer
86          final FileAlterationObserver observer = new FileAlterationObserver("foo");
87          monitor.addObserver(observer);
88          final Iterator<FileAlterationObserver> it = monitor.getObservers().iterator();
89          assertTrue(it.hasNext(), "Observers[4]");
90          assertEquals(observer, it.next(), "Added");
91          assertFalse(it.hasNext(), "Observers[5]");
92  
93          // Remove Observer
94          monitor.removeObserver(observer);
95          assertFalse(monitor.getObservers().iterator().hasNext(), "Observers[6]");
96      }
97  
98      @Test
99      public void testCollectionConstructor() {
100         observer = new FileAlterationObserver("foo");
101         final Collection<FileAlterationObserver> observers = Arrays.asList(observer);
102         final FileAlterationMonitor monitor = new FileAlterationMonitor(0, observers);
103         final Iterator<FileAlterationObserver> iterator = monitor.getObservers().iterator();
104         assertEquals(observer, iterator.next());
105     }
106 
107     @Test
108     public void testCollectionConstructorShouldDoNothingWithNullCollection() {
109         final Collection<FileAlterationObserver> observers = null;
110         final FileAlterationMonitor monitor = new FileAlterationMonitor(0, observers);
111         assertFalse(monitor.getObservers().iterator().hasNext());
112     }
113 
114     @Test
115     public void testCollectionConstructorShouldDoNothingWithNullObservers() {
116         final Collection<FileAlterationObserver> observers = new ArrayList<>(5);
117         final FileAlterationMonitor monitor = new FileAlterationMonitor(0, observers);
118         assertFalse(monitor.getObservers().iterator().hasNext());
119     }
120 
121     /**
122      * Test default constructor.
123      */
124     @Test
125     public void testDefaultConstructor() {
126         final FileAlterationMonitor monitor = new FileAlterationMonitor();
127         assertEquals(10000, monitor.getInterval(), "Interval");
128     }
129 
130     /**
131      * Test checkAndNotify() method
132      * @throws Exception
133      */
134     @Test
135     public void testMonitor() throws Exception {
136         final long interval = 100;
137         listener.clear();
138         final FileAlterationMonitor monitor = new FileAlterationMonitor(interval, observer);
139         assertEquals(interval, monitor.getInterval(), "Interval");
140         monitor.start();
141 
142         // try and start again
143         assertThrows(IllegalStateException.class, () -> monitor.start());
144 
145         // Create a File
146         checkCollectionsEmpty("A");
147         File file1 = touch(new File(testDir, "file1.java"));
148         checkFile("Create", file1, listener.getCreatedFiles());
149         listener.clear();
150 
151         // Update a file
152         checkCollectionsEmpty("B");
153         file1 = touch(file1);
154         checkFile("Update", file1, listener.getChangedFiles());
155         listener.clear();
156 
157         // Delete a file
158         checkCollectionsEmpty("C");
159         file1.delete();
160         checkFile("Delete", file1, listener.getDeletedFiles());
161         listener.clear();
162 
163         // Stop monitoring
164         monitor.stop();
165 
166         // try and stop again
167         assertThrows(IllegalStateException.class, () -> monitor.stop());
168     }
169 
170     /**
171      * Test case for IO-535
172      *
173      * Verify that {@link FileAlterationMonitor#stop()} stops the created thread
174      */
175     @Test
176     public void testStopWhileWaitingForNextInterval() throws Exception {
177         final Collection<Thread> createdThreads = new ArrayList<>(1);
178         final ThreadFactory threadFactory = new ThreadFactory() {
179             private final ThreadFactory delegate = Executors.defaultThreadFactory();
180 
181             @Override
182             public Thread newThread(final Runnable r) {
183                 final Thread thread = delegate.newThread(r);
184                 thread.setDaemon(true); //do not leak threads if the test fails
185                 createdThreads.add(thread);
186                 return thread;
187             }
188         };
189 
190         final FileAlterationMonitor monitor = new FileAlterationMonitor(1_000);
191         monitor.setThreadFactory(threadFactory);
192 
193         monitor.start();
194         assertFalse(createdThreads.isEmpty());
195 
196         ThreadUtils.sleep(Duration.ofMillis(10)); // wait until the watcher thread enters Thread.sleep()
197         monitor.stop(100);
198 
199         createdThreads.forEach(thread -> assertFalse(thread.isAlive(), "The FileAlterationMonitor did not stop the threads it created."));
200     }
201 
202     /**
203      * Test using a thread factory.
204      * @throws Exception
205      */
206     @Test
207     public void testThreadFactory() throws Exception {
208         final long interval = 100;
209         listener.clear();
210         final FileAlterationMonitor monitor = new FileAlterationMonitor(interval, observer);
211         monitor.setThreadFactory(Executors.defaultThreadFactory());
212         assertEquals(interval, monitor.getInterval(), "Interval");
213         monitor.start();
214 
215         // Create a File
216         checkCollectionsEmpty("A");
217         final File file2 = touch(new File(testDir, "file2.java"));
218         checkFile("Create", file2, listener.getCreatedFiles());
219         listener.clear();
220 
221         // Delete a file
222         checkCollectionsEmpty("B");
223         file2.delete();
224         checkFile("Delete", file2, listener.getDeletedFiles());
225         listener.clear();
226 
227         // Stop monitoring
228         monitor.stop();
229     }
230 }