| /* | |
| * Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| /* @test | |
| * @bug 4313887 6838333 7017446 8011537 8042470 | |
| * @summary Unit test for java.nio.file.WatchService | |
| * @library .. | |
| * @run main Basic | |
| */ | |
| import java.nio.file.*; | |
| import static java.nio.file.StandardWatchEventKinds.*; | |
| import java.nio.file.attribute.*; | |
| import java.io.*; | |
| import java.util.*; | |
| import java.util.concurrent.TimeUnit; | |
| /** | |
| * Unit test for WatchService that exercises all methods in various scenarios. | |
| */ | |
| public class Basic { | |
| static void checkKey(WatchKey key, Path dir) { | |
| if (!key.isValid()) | |
| throw new RuntimeException("Key is not valid"); | |
| if (key.watchable() != dir) | |
| throw new RuntimeException("Unexpected watchable"); | |
| } | |
| static void takeExpectedKey(WatchService watcher, WatchKey expected) { | |
| System.out.println("take events..."); | |
| WatchKey key; | |
| try { | |
| key = watcher.take(); | |
| } catch (InterruptedException x) { | |
| // not expected | |
| throw new RuntimeException(x); | |
| } | |
| if (key != expected) | |
| throw new RuntimeException("removed unexpected key"); | |
| } | |
| static void checkExpectedEvent(Iterable<WatchEvent<?>> events, | |
| WatchEvent.Kind<?> expectedKind, | |
| Object expectedContext) | |
| { | |
| WatchEvent<?> event = events.iterator().next(); | |
| System.out.format("got event: type=%s, count=%d, context=%s\n", | |
| event.kind(), event.count(), event.context()); | |
| if (event.kind() != expectedKind) | |
| throw new RuntimeException("unexpected event"); | |
| if (!expectedContext.equals(event.context())) | |
| throw new RuntimeException("unexpected context"); | |
| } | |
| /** | |
| * Simple test of each of the standard events | |
| */ | |
| static void testEvents(Path dir) throws IOException { | |
| System.out.println("-- Standard Events --"); | |
| FileSystem fs = FileSystems.getDefault(); | |
| Path name = fs.getPath("foo"); | |
| try (WatchService watcher = fs.newWatchService()) { | |
| // --- ENTRY_CREATE --- | |
| // register for event | |
| System.out.format("register %s for ENTRY_CREATE\n", dir); | |
| WatchKey myKey = dir.register(watcher, | |
| new WatchEvent.Kind<?>[]{ ENTRY_CREATE }); | |
| checkKey(myKey, dir); | |
| // create file | |
| Path file = dir.resolve("foo"); | |
| System.out.format("create %s\n", file); | |
| Files.createFile(file); | |
| // remove key and check that we got the ENTRY_CREATE event | |
| takeExpectedKey(watcher, myKey); | |
| checkExpectedEvent(myKey.pollEvents(), | |
| StandardWatchEventKinds.ENTRY_CREATE, name); | |
| System.out.println("reset key"); | |
| if (!myKey.reset()) | |
| throw new RuntimeException("key has been cancalled"); | |
| System.out.println("OKAY"); | |
| // --- ENTRY_DELETE --- | |
| System.out.format("register %s for ENTRY_DELETE\n", dir); | |
| WatchKey deleteKey = dir.register(watcher, | |
| new WatchEvent.Kind<?>[]{ ENTRY_DELETE }); | |
| if (deleteKey != myKey) | |
| throw new RuntimeException("register did not return existing key"); | |
| checkKey(deleteKey, dir); | |
| System.out.format("delete %s\n", file); | |
| Files.delete(file); | |
| takeExpectedKey(watcher, myKey); | |
| checkExpectedEvent(myKey.pollEvents(), | |
| StandardWatchEventKinds.ENTRY_DELETE, name); | |
| System.out.println("reset key"); | |
| if (!myKey.reset()) | |
| throw new RuntimeException("key has been cancalled"); | |
| System.out.println("OKAY"); | |
| // create the file for the next test | |
| Files.createFile(file); | |
| // --- ENTRY_MODIFY --- | |
| System.out.format("register %s for ENTRY_MODIFY\n", dir); | |
| WatchKey newKey = dir.register(watcher, | |
| new WatchEvent.Kind<?>[]{ ENTRY_MODIFY }); | |
| if (newKey != myKey) | |
| throw new RuntimeException("register did not return existing key"); | |
| checkKey(newKey, dir); | |
| System.out.format("update: %s\n", file); | |
| try (OutputStream out = Files.newOutputStream(file, StandardOpenOption.APPEND)) { | |
| out.write("I am a small file".getBytes("UTF-8")); | |
| } | |
| // remove key and check that we got the ENTRY_MODIFY event | |
| takeExpectedKey(watcher, myKey); | |
| checkExpectedEvent(myKey.pollEvents(), | |
| StandardWatchEventKinds.ENTRY_MODIFY, name); | |
| System.out.println("OKAY"); | |
| // done | |
| Files.delete(file); | |
| } | |
| } | |
| /** | |
| * Check that a cancelled key will never be queued | |
| */ | |
| static void testCancel(Path dir) throws IOException { | |
| System.out.println("-- Cancel --"); | |
| try (WatchService watcher = FileSystems.getDefault().newWatchService()) { | |
| System.out.format("register %s for events\n", dir); | |
| WatchKey myKey = dir.register(watcher, | |
| new WatchEvent.Kind<?>[]{ ENTRY_CREATE }); | |
| checkKey(myKey, dir); | |
| System.out.println("cancel key"); | |
| myKey.cancel(); | |
| // create a file in the directory | |
| Path file = dir.resolve("mars"); | |
| System.out.format("create: %s\n", file); | |
| Files.createFile(file); | |
| // poll for keys - there will be none | |
| System.out.println("poll..."); | |
| try { | |
| WatchKey key = watcher.poll(3000, TimeUnit.MILLISECONDS); | |
| if (key != null) | |
| throw new RuntimeException("key should not be queued"); | |
| } catch (InterruptedException x) { | |
| throw new RuntimeException(x); | |
| } | |
| // done | |
| Files.delete(file); | |
| System.out.println("OKAY"); | |
| } | |
| } | |
| /** | |
| * Check that deleting a registered directory causes the key to be | |
| * cancelled and queued. | |
| */ | |
| static void testAutomaticCancel(Path dir) throws IOException { | |
| System.out.println("-- Automatic Cancel --"); | |
| Path subdir = Files.createDirectory(dir.resolve("bar")); | |
| try (WatchService watcher = FileSystems.getDefault().newWatchService()) { | |
| System.out.format("register %s for events\n", subdir); | |
| WatchKey myKey = subdir.register(watcher, | |
| new WatchEvent.Kind<?>[]{ ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY }); | |
| System.out.format("delete: %s\n", subdir); | |
| Files.delete(subdir); | |
| takeExpectedKey(watcher, myKey); | |
| System.out.println("reset key"); | |
| if (myKey.reset()) | |
| throw new RuntimeException("Key was not cancelled"); | |
| if (myKey.isValid()) | |
| throw new RuntimeException("Key is still valid"); | |
| System.out.println("OKAY"); | |
| } | |
| } | |
| /** | |
| * Asynchronous close of watcher causes blocked threads to wakeup | |
| */ | |
| static void testWakeup(Path dir) throws IOException { | |
| System.out.println("-- Wakeup Tests --"); | |
| final WatchService watcher = FileSystems.getDefault().newWatchService(); | |
| Runnable r = new Runnable() { | |
| public void run() { | |
| try { | |
| Thread.sleep(5000); | |
| System.out.println("close WatchService..."); | |
| watcher.close(); | |
| } catch (InterruptedException x) { | |
| x.printStackTrace(); | |
| } catch (IOException x) { | |
| x.printStackTrace(); | |
| } | |
| } | |
| }; | |
| // start thread to close watch service after delay | |
| new Thread(r).start(); | |
| try { | |
| System.out.println("take..."); | |
| watcher.take(); | |
| throw new RuntimeException("ClosedWatchServiceException not thrown"); | |
| } catch (InterruptedException x) { | |
| throw new RuntimeException(x); | |
| } catch (ClosedWatchServiceException x) { | |
| System.out.println("ClosedWatchServiceException thrown"); | |
| } | |
| System.out.println("OKAY"); | |
| } | |
| /** | |
| * Simple test to check exceptions and other cases | |
| */ | |
| @SuppressWarnings("unchecked") | |
| static void testExceptions(Path dir) throws IOException { | |
| System.out.println("-- Exceptions and other simple tests --"); | |
| WatchService watcher = FileSystems.getDefault().newWatchService(); | |
| try { | |
| // Poll tests | |
| WatchKey key; | |
| System.out.println("poll..."); | |
| key = watcher.poll(); | |
| if (key != null) | |
| throw new RuntimeException("no keys registered"); | |
| System.out.println("poll with timeout..."); | |
| try { | |
| long start = System.nanoTime(); | |
| key = watcher.poll(3000, TimeUnit.MILLISECONDS); | |
| if (key != null) | |
| throw new RuntimeException("no keys registered"); | |
| long waited = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); | |
| if (waited < 2900) | |
| throw new RuntimeException("poll was too short"); | |
| } catch (InterruptedException x) { | |
| throw new RuntimeException(x); | |
| } | |
| // IllegalArgumentException | |
| System.out.println("IllegalArgumentException tests..."); | |
| try { | |
| dir.register(watcher /*empty event list*/); | |
| throw new RuntimeException("IllegalArgumentException not thrown"); | |
| } catch (IllegalArgumentException x) { | |
| } | |
| try { | |
| // OVERFLOW is ignored so this is equivalent to the empty set | |
| dir.register(watcher, OVERFLOW); | |
| throw new RuntimeException("IllegalArgumentException not thrown"); | |
| } catch (IllegalArgumentException x) { | |
| } | |
| try { | |
| // OVERFLOW is ignored even if specified multiple times | |
| dir.register(watcher, OVERFLOW, OVERFLOW); | |
| throw new RuntimeException("IllegalArgumentException not thrown"); | |
| } catch (IllegalArgumentException x) { | |
| } | |
| // UnsupportedOperationException | |
| try { | |
| dir.register(watcher, | |
| new WatchEvent.Kind<Object>() { | |
| @Override public String name() { return "custom"; } | |
| @Override public Class<Object> type() { return Object.class; } | |
| }); | |
| throw new RuntimeException("UnsupportedOperationException not thrown"); | |
| } catch (UnsupportedOperationException x) { | |
| } | |
| try { | |
| dir.register(watcher, | |
| new WatchEvent.Kind<?>[]{ ENTRY_CREATE }, | |
| new WatchEvent.Modifier() { | |
| @Override public String name() { return "custom"; } | |
| }); | |
| throw new RuntimeException("UnsupportedOperationException not thrown"); | |
| } catch (UnsupportedOperationException x) { | |
| } | |
| // NullPointerException | |
| System.out.println("NullPointerException tests..."); | |
| try { | |
| dir.register(null, ENTRY_CREATE); | |
| throw new RuntimeException("NullPointerException not thrown"); | |
| } catch (NullPointerException x) { | |
| } | |
| try { | |
| dir.register(watcher, new WatchEvent.Kind<?>[]{ null }); | |
| throw new RuntimeException("NullPointerException not thrown"); | |
| } catch (NullPointerException x) { | |
| } | |
| try { | |
| dir.register(watcher, new WatchEvent.Kind<?>[]{ ENTRY_CREATE }, | |
| (WatchEvent.Modifier)null); | |
| throw new RuntimeException("NullPointerException not thrown"); | |
| } catch (NullPointerException x) { | |
| } | |
| } finally { | |
| watcher.close(); | |
| } | |
| // -- ClosedWatchServiceException -- | |
| System.out.println("ClosedWatchServiceException tests..."); | |
| try { | |
| watcher.poll(); | |
| throw new RuntimeException("ClosedWatchServiceException not thrown"); | |
| } catch (ClosedWatchServiceException x) { | |
| } | |
| // assume that poll throws exception immediately | |
| long start = System.nanoTime(); | |
| try { | |
| watcher.poll(10000, TimeUnit.MILLISECONDS); | |
| throw new RuntimeException("ClosedWatchServiceException not thrown"); | |
| } catch (InterruptedException x) { | |
| throw new RuntimeException(x); | |
| } catch (ClosedWatchServiceException x) { | |
| long waited = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); | |
| if (waited > 5000) | |
| throw new RuntimeException("poll was too long"); | |
| } | |
| try { | |
| watcher.take(); | |
| throw new RuntimeException("ClosedWatchServiceException not thrown"); | |
| } catch (InterruptedException x) { | |
| throw new RuntimeException(x); | |
| } catch (ClosedWatchServiceException x) { | |
| } | |
| try { | |
| dir.register(watcher, new WatchEvent.Kind<?>[]{ ENTRY_CREATE }); | |
| throw new RuntimeException("ClosedWatchServiceException not thrown"); | |
| } catch (ClosedWatchServiceException x) { | |
| } | |
| System.out.println("OKAY"); | |
| } | |
| /** | |
| * Test that directory can be registered with more than one watch service | |
| * and that events don't interfere with each other | |
| */ | |
| static void testTwoWatchers(Path dir) throws IOException { | |
| System.out.println("-- Two watchers test --"); | |
| FileSystem fs = FileSystems.getDefault(); | |
| WatchService watcher1 = fs.newWatchService(); | |
| WatchService watcher2 = fs.newWatchService(); | |
| try { | |
| Path name1 = fs.getPath("gus1"); | |
| Path name2 = fs.getPath("gus2"); | |
| // create gus1 | |
| Path file1 = dir.resolve(name1); | |
| System.out.format("create %s\n", file1); | |
| Files.createFile(file1); | |
| // register with both watch services (different events) | |
| System.out.println("register for different events"); | |
| WatchKey key1 = dir.register(watcher1, | |
| new WatchEvent.Kind<?>[]{ ENTRY_CREATE }); | |
| WatchKey key2 = dir.register(watcher2, | |
| new WatchEvent.Kind<?>[]{ ENTRY_DELETE }); | |
| if (key1 == key2) | |
| throw new RuntimeException("keys should be different"); | |
| // create gus2 | |
| Path file2 = dir.resolve(name2); | |
| System.out.format("create %s\n", file2); | |
| Files.createFile(file2); | |
| // check that key1 got ENTRY_CREATE | |
| takeExpectedKey(watcher1, key1); | |
| checkExpectedEvent(key1.pollEvents(), | |
| StandardWatchEventKinds.ENTRY_CREATE, name2); | |
| // check that key2 got zero events | |
| WatchKey key = watcher2.poll(); | |
| if (key != null) | |
| throw new RuntimeException("key not expected"); | |
| // delete gus1 | |
| Files.delete(file1); | |
| // check that key2 got ENTRY_DELETE | |
| takeExpectedKey(watcher2, key2); | |
| checkExpectedEvent(key2.pollEvents(), | |
| StandardWatchEventKinds.ENTRY_DELETE, name1); | |
| // check that key1 got zero events | |
| key = watcher1.poll(); | |
| if (key != null) | |
| throw new RuntimeException("key not expected"); | |
| // reset for next test | |
| key1.reset(); | |
| key2.reset(); | |
| // change registration with watcher2 so that they are both | |
| // registered for the same event | |
| System.out.println("register for same event"); | |
| key2 = dir.register(watcher2, new WatchEvent.Kind<?>[]{ ENTRY_CREATE }); | |
| // create file and key2 should be queued | |
| System.out.format("create %s\n", file1); | |
| Files.createFile(file1); | |
| takeExpectedKey(watcher2, key2); | |
| checkExpectedEvent(key2.pollEvents(), | |
| StandardWatchEventKinds.ENTRY_CREATE, name1); | |
| System.out.println("OKAY"); | |
| } finally { | |
| watcher2.close(); | |
| watcher1.close(); | |
| } | |
| } | |
| /** | |
| * Test that thread interruped status is preserved upon a call | |
| * to register() | |
| */ | |
| static void testThreadInterrupt(Path dir) throws IOException { | |
| System.out.println("-- Thread interrupted status test --"); | |
| FileSystem fs = FileSystems.getDefault(); | |
| Thread curr = Thread.currentThread(); | |
| try (WatchService watcher = fs.newWatchService()) { | |
| System.out.println("interrupting current thread"); | |
| curr.interrupt(); | |
| dir.register(watcher, ENTRY_CREATE); | |
| if (!curr.isInterrupted()) | |
| throw new RuntimeException("thread should remain interrupted"); | |
| System.out.println("current thread is still interrupted"); | |
| System.out.println("OKAY"); | |
| } finally { | |
| curr.interrupted(); | |
| } | |
| } | |
| public static void main(String[] args) throws IOException { | |
| Path dir = TestUtil.createTemporaryDirectory(); | |
| try { | |
| testEvents(dir); | |
| testCancel(dir); | |
| testAutomaticCancel(dir); | |
| testWakeup(dir); | |
| testExceptions(dir); | |
| testTwoWatchers(dir); | |
| testThreadInterrupt(dir); | |
| } finally { | |
| TestUtil.removeAll(dir); | |
| } | |
| } | |
| } |