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