1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 package libcore.java.nio.file; 17 18 import org.junit.Test; 19 import org.junit.runner.RunWith; 20 import org.junit.runners.JUnit4; 21 import org.junit.Rule; 22 23 import java.nio.file.FileSystems; 24 import java.nio.file.Files; 25 import java.nio.file.Path; 26 import java.nio.file.Paths; 27 import java.nio.file.WatchEvent; 28 import java.nio.file.WatchKey; 29 import java.nio.file.WatchService; 30 import java.util.ArrayList; 31 import java.util.Arrays; 32 import java.util.HashMap; 33 import java.util.Iterator; 34 import java.util.List; 35 import java.util.Map; 36 import java.util.concurrent.TimeUnit; 37 38 import static junit.framework.TestCase.assertEquals; 39 import static junit.framework.TestCase.assertFalse; 40 import static junit.framework.TestCase.assertNotNull; 41 import static junit.framework.TestCase.assertNull; 42 import static junit.framework.TestCase.assertTrue; 43 import static junit.framework.TestCase.fail; 44 45 import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE; 46 import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE; 47 import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; 48 49 @RunWith(JUnit4.class) 50 public class WatchServiceTest { 51 private static final WatchEvent.Kind<?>[] ALL_EVENTS_KINDS = 52 {ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY}; 53 54 @Rule 55 public final FilesSetup filesSetup = new FilesSetup(); 56 57 static class WatchEventResult { 58 final WatchEvent.Kind<Path> expectedKind; 59 final int expectedCount; 60 final boolean testCount; 61 WatchEventResult(WatchEvent.Kind<Path> expectedKind)62 public WatchEventResult(WatchEvent.Kind<Path> expectedKind) { 63 this.expectedKind = expectedKind; 64 this.expectedCount = 0; 65 this.testCount = false; 66 } 67 WatchEventResult(WatchEvent.Kind<Path> expectedKind, int expectedCount)68 public WatchEventResult(WatchEvent.Kind<Path> expectedKind, 69 int expectedCount) { 70 this.expectedKind = expectedKind; 71 this.expectedCount = expectedCount; 72 this.testCount = true; 73 } 74 } 75 checkWatchServiceEventMultipleKeys(WatchService watchService, Map<WatchKey, List<WatchEventResult>> expectedResults, boolean expectedResetResult)76 private static void checkWatchServiceEventMultipleKeys(WatchService watchService, 77 Map<WatchKey, List<WatchEventResult>> expectedResults, 78 boolean expectedResetResult) throws InterruptedException { 79 80 // Make a deep copy 81 HashMap<WatchKey, ArrayList<WatchEventResult>> expectedResultsCopy 82 = new HashMap<>(); 83 for (Map.Entry<WatchKey, List<WatchEventResult>> entry : expectedResults.entrySet()) { 84 expectedResultsCopy.put(entry.getKey(), new ArrayList<>(entry.getValue())); 85 } 86 87 while (!expectedResultsCopy.isEmpty()) { 88 WatchKey watchKey = watchService.poll(2, TimeUnit.SECONDS); 89 assertNotNull(watchKey); 90 91 List<WatchEventResult> expectedEvents = expectedResultsCopy.get(watchKey); 92 assertNotNull(expectedEvents); 93 94 Iterator<WatchEventResult> expectedEventsIterator = expectedEvents.iterator(); 95 for (WatchEvent<?> event : watchKey.pollEvents()) { 96 WatchEventResult expectedEventResult = expectedEventsIterator.next(); 97 assertNotNull(expectedEventResult); 98 99 assertEquals(expectedEventResult.expectedKind, event.kind()); 100 if (expectedEventResult.testCount) { 101 assertEquals(expectedEventResult.expectedCount, event.count()); 102 } 103 expectedEventsIterator.remove(); 104 } 105 assertEquals(expectedResetResult, watchKey.reset()); 106 if (!expectedEventsIterator.hasNext()) { 107 expectedResultsCopy.remove(watchKey); 108 } 109 } 110 } 111 checkWatchServiceEvent(WatchService watchService, WatchKey expectedWatchKey, List<WatchEventResult> expectedEvents, boolean expectedResetResult)112 private static void checkWatchServiceEvent(WatchService watchService, 113 WatchKey expectedWatchKey, 114 List<WatchEventResult> expectedEvents, 115 boolean expectedResetResult) throws InterruptedException { 116 Map<WatchKey, List<WatchEventResult>> expected = new HashMap<>(); 117 expected.put(expectedWatchKey, expectedEvents); 118 checkWatchServiceEventMultipleKeys(watchService, expected, expectedResetResult); 119 } 120 121 @Test test_singleFile()122 public void test_singleFile() throws Exception { 123 WatchService watchService = FileSystems.getDefault().newWatchService(); 124 Path file = Paths.get(filesSetup.getTestDir(), "directory/file"); 125 Path directory = Paths.get(filesSetup.getTestDir(), "directory"); 126 assertFalse(Files.exists(file)); 127 Files.createDirectories(directory); 128 WatchKey directoryKey1 = directory.register(watchService, ALL_EVENTS_KINDS); 129 130 // emit EVENT_CREATE 131 Files.createFile(file); 132 checkWatchServiceEvent(watchService, directoryKey1, 133 Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1)), true); 134 assertNull(watchService.poll()); 135 136 // emit EVENT_MODIFY 137 Files.write(file, "hello1".getBytes()); 138 checkWatchServiceEvent(watchService, directoryKey1, 139 Arrays.asList(new WatchEventResult(ENTRY_MODIFY)), true); 140 141 // http:///b/35346596 142 // Sometimes we receive a second, latent EVENT_MODIFY that happens shortly 143 // after the first one. This will intercept it and make sure it won't 144 // mess with ENTRY_DELETE later. 145 Thread.sleep(500); 146 WatchKey doubleModifyKey = watchService.poll(); 147 if (doubleModifyKey != null) { 148 List<WatchEvent<?>> event = doubleModifyKey.pollEvents(); 149 assertEquals(ENTRY_MODIFY, event.get(0).kind()); 150 doubleModifyKey.reset(); 151 } 152 assertNull(watchService.poll()); 153 154 // emit EVENT_DELETE 155 Files.delete(file); 156 checkWatchServiceEvent(watchService, directoryKey1, 157 Arrays.asList(new WatchEventResult(ENTRY_DELETE, 1)), true); 158 159 // Assert no more events 160 assertNull(watchService.poll()); 161 watchService.close(); 162 } 163 164 @Test test_EventMask()165 public void test_EventMask() throws Exception { 166 WatchService watchService = FileSystems.getDefault().newWatchService(); 167 WatchEvent.Kind<?>[] events = {ENTRY_DELETE}; 168 Path file = Paths.get(filesSetup.getTestDir(), "directory/file"); 169 Path directory = Paths.get(filesSetup.getTestDir(), "directory"); 170 assertFalse(Files.exists(file)); 171 Files.createDirectories(directory); 172 WatchKey directoryKey1 = directory.register(watchService, events); 173 174 // emit EVENT_CREATE 175 Files.createFile(file); 176 // emit EVENT_MODIFY (masked) 177 Files.write(file, "hello1".getBytes()); 178 // emit EVENT_DELETE (masked) 179 Files.delete(file); 180 181 checkWatchServiceEvent(watchService, directoryKey1, 182 Arrays.asList(new WatchEventResult(ENTRY_DELETE, 1)), true); 183 assertNull(watchService.poll()); 184 watchService.close(); 185 } 186 187 @Test test_singleDirectory()188 public void test_singleDirectory() throws Exception { 189 WatchService watchService = FileSystems.getDefault().newWatchService(); 190 Path dirInDir = Paths.get(filesSetup.getTestDir(), "directory/dir"); 191 Path directory = Paths.get(filesSetup.getTestDir(), "directory"); 192 assertFalse(Files.exists(dirInDir)); 193 Files.createDirectories(directory); 194 WatchKey directoryKey1 = directory.register(watchService, ALL_EVENTS_KINDS); 195 196 // emit EVENT_CREATE 197 Files.createDirectories(dirInDir); 198 199 // Shouldn't emit EVENT_MODIFY 200 Path dirInDirInDir = Paths.get(filesSetup.getTestDir(), "directory/dir/dir"); 201 Files.createDirectories(dirInDirInDir); 202 Files.delete(dirInDirInDir); 203 204 // emit EVENT_DELETE 205 Files.delete(dirInDir); 206 207 checkWatchServiceEvent(watchService, directoryKey1, 208 Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1), 209 new WatchEventResult(ENTRY_DELETE, 1)), true); 210 assertNull(watchService.poll()); 211 watchService.close(); 212 213 watchService.close(); 214 } 215 216 @Test test_cancel()217 public void test_cancel() throws Exception { 218 WatchService watchService = FileSystems.getDefault().newWatchService(); 219 Path file = Paths.get(filesSetup.getTestDir(), "directory/file"); 220 Path directory = Paths.get(filesSetup.getTestDir(), "directory"); 221 assertFalse(Files.exists(file)); 222 Files.createDirectories(directory); 223 WatchKey directoryKey1 = directory.register(watchService, ALL_EVENTS_KINDS); 224 225 // emit EVENT_CREATE 226 Files.createFile(file); 227 228 // Canceling the key may prevent the EVENT_CREATE from being picked-up... 229 // TODO: Fix this (b/35190858). 230 Thread.sleep(500); 231 232 // Cancel the key 233 directoryKey1.cancel(); 234 assertFalse(directoryKey1.isValid()); 235 236 // Shouldn't emit EVENT_MODIFY and EVENT_DELETE 237 Files.write(file, "hello1".getBytes()); 238 Files.delete(file); 239 240 checkWatchServiceEvent(watchService, directoryKey1, 241 Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1)), false); 242 assertNull(watchService.poll()); 243 watchService.close(); 244 } 245 246 @Test test_removeTarget()247 public void test_removeTarget() throws Exception { 248 WatchService watchService = FileSystems.getDefault().newWatchService(); 249 Path file = Paths.get(filesSetup.getTestDir(), "directory/file"); 250 Path directory = Paths.get(filesSetup.getTestDir(), "directory"); 251 assertFalse(Files.exists(file)); 252 Files.createDirectories(directory); 253 WatchKey directoryKey1 = directory.register(watchService, ALL_EVENTS_KINDS); 254 255 // emit EVENT_CREATE x1 256 Files.createFile(file); 257 Files.delete(file); 258 259 // Delete underlying target. 260 assertTrue(directoryKey1.isValid()); 261 Files.delete(directory); 262 263 // We need to give some time to watch service thread to catch up with the 264 // deletion 265 while (directoryKey1.isValid()) { 266 Thread.sleep(500); 267 } 268 269 checkWatchServiceEvent(watchService, directoryKey1, 270 Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1), 271 new WatchEventResult(ENTRY_DELETE, 1)), false); 272 assertNull(watchService.poll()); 273 274 watchService.close(); 275 } 276 277 @Test test_multipleKeys()278 public void test_multipleKeys() throws Exception { 279 WatchService watchService1 = FileSystems.getDefault().newWatchService(); 280 281 Path directory1 = Paths.get(filesSetup.getTestDir(), "directory1"); 282 Path directory2 = Paths.get(filesSetup.getTestDir(), "directory2"); 283 284 Path dir1file1 = Paths.get(filesSetup.getTestDir(), "directory1/file1"); 285 assertFalse(Files.exists(dir1file1)); 286 Path dir2file1 = Paths.get(filesSetup.getTestDir(), "directory2/file1"); 287 assertFalse(Files.exists(dir2file1)); 288 289 Files.createDirectories(directory1); 290 Files.createDirectories(directory2); 291 WatchKey directoryKey1 = directory1.register(watchService1, ALL_EVENTS_KINDS); 292 WatchKey directoryKey2 = directory2.register(watchService1, ALL_EVENTS_KINDS); 293 294 // emit EVENT_CREATE/DELETE for all 295 Path[] allFiles = new Path[]{dir1file1, dir2file1}; 296 for (Path path : allFiles) { 297 Files.createFile(path); 298 Files.delete(path); 299 } 300 301 Map<WatchKey, List<WatchEventResult>> expected = new HashMap<>(); 302 expected.put(directoryKey1, 303 Arrays.asList( 304 new WatchEventResult(ENTRY_CREATE, 1), 305 new WatchEventResult(ENTRY_DELETE, 1))); 306 expected.put(directoryKey2, 307 Arrays.asList( 308 new WatchEventResult(ENTRY_CREATE, 1), 309 new WatchEventResult(ENTRY_DELETE, 1))); 310 311 checkWatchServiceEventMultipleKeys(watchService1, expected, true); 312 assertNull(watchService1.poll()); 313 watchService1.close(); 314 } 315 316 @Test test_multipleServices()317 public void test_multipleServices() throws Exception { 318 WatchService watchService1 = FileSystems.getDefault().newWatchService(); 319 WatchService watchService2 = FileSystems.getDefault().newWatchService(); 320 321 Path directory1 = Paths.get(filesSetup.getTestDir(), "directory1"); 322 Path directory2 = Paths.get(filesSetup.getTestDir(), "directory2"); 323 324 Path dir1file1 = Paths.get(filesSetup.getTestDir(), "directory1/file1"); 325 assertFalse(Files.exists(dir1file1)); 326 Path dir2file1 = Paths.get(filesSetup.getTestDir(), "directory2/file1"); 327 assertFalse(Files.exists(dir2file1)); 328 329 Files.createDirectories(directory1); 330 Files.createDirectories(directory2); 331 332 // 2 services listening for distinct directories 333 WatchKey directoryKey1 = directory1.register(watchService1, ALL_EVENTS_KINDS); 334 WatchKey directoryKey2 = directory2.register(watchService2, ALL_EVENTS_KINDS); 335 // emit EVENT_CREATE/DELETE for all 336 Path[] allFiles = new Path[]{dir1file1, dir2file1}; 337 for (Path path : allFiles) { 338 Files.createFile(path); 339 Files.delete(path); 340 } 341 342 checkWatchServiceEvent(watchService1, directoryKey1, 343 Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1), 344 new WatchEventResult(ENTRY_DELETE, 1)), true); 345 checkWatchServiceEvent(watchService2, directoryKey2, 346 Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1), 347 new WatchEventResult(ENTRY_DELETE, 1)), true); 348 349 // 2 services listening for a same directory 350 WatchKey directoryKey3 = directory1.register(watchService2, ALL_EVENTS_KINDS); 351 { 352 Files.createFile(dir1file1); 353 Files.delete(dir1file1); 354 } 355 checkWatchServiceEvent(watchService1, directoryKey1, 356 Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1), 357 new WatchEventResult(ENTRY_DELETE, 1)), true); 358 checkWatchServiceEvent(watchService2, directoryKey3, 359 Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1), 360 new WatchEventResult(ENTRY_DELETE, 1)), true); 361 362 363 364 assertNull(watchService1.poll()); 365 watchService1.close(); 366 assertNull(watchService2.poll()); 367 watchService2.close(); 368 } 369 } 370