| /* |
| * Copyright 2013 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.google.common.jimfs; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE; |
| import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE; |
| import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; |
| import static java.util.concurrent.TimeUnit.MILLISECONDS; |
| import static org.junit.Assert.fail; |
| |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.jimfs.AbstractWatchService.Event; |
| import com.google.common.jimfs.AbstractWatchService.Key; |
| import com.google.common.util.concurrent.Runnables; |
| import com.google.common.util.concurrent.Uninterruptibles; |
| import java.io.IOException; |
| import java.nio.file.Files; |
| import java.nio.file.NoSuchFileException; |
| import java.nio.file.NotDirectoryException; |
| import java.nio.file.Path; |
| import java.nio.file.WatchEvent; |
| import java.nio.file.WatchKey; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.UUID; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| /** |
| * Tests for {@link PollingWatchService}. |
| * |
| * @author Colin Decker |
| */ |
| @RunWith(JUnit4.class) |
| public class PollingWatchServiceTest { |
| |
| private JimfsFileSystem fs; |
| private PollingWatchService watcher; |
| |
| @Before |
| public void setUp() { |
| fs = (JimfsFileSystem) Jimfs.newFileSystem(Configuration.unix()); |
| watcher = |
| new PollingWatchService( |
| fs.getDefaultView(), |
| fs.getPathService(), |
| new FileSystemState(Runnables.doNothing()), |
| 4, |
| MILLISECONDS); |
| } |
| |
| @After |
| public void tearDown() throws IOException { |
| watcher.close(); |
| fs.close(); |
| watcher = null; |
| fs = null; |
| } |
| |
| @Test |
| public void testNewWatcher() { |
| assertThat(watcher.isOpen()).isTrue(); |
| assertThat(watcher.isPolling()).isFalse(); |
| } |
| |
| @Test |
| public void testRegister() throws IOException { |
| Key key = watcher.register(createDirectory(), ImmutableList.of(ENTRY_CREATE)); |
| assertThat(key.isValid()).isTrue(); |
| |
| assertThat(watcher.isPolling()).isTrue(); |
| } |
| |
| @Test |
| public void testRegister_fileDoesNotExist() throws IOException { |
| try { |
| watcher.register(fs.getPath("/a/b/c"), ImmutableList.of(ENTRY_CREATE)); |
| fail(); |
| } catch (NoSuchFileException expected) { |
| } |
| } |
| |
| @Test |
| public void testRegister_fileIsNotDirectory() throws IOException { |
| Path path = fs.getPath("/a.txt"); |
| Files.createFile(path); |
| try { |
| watcher.register(path, ImmutableList.of(ENTRY_CREATE)); |
| fail(); |
| } catch (NotDirectoryException expected) { |
| } |
| } |
| |
| @Test |
| public void testCancellingLastKeyStopsPolling() throws IOException { |
| Key key = watcher.register(createDirectory(), ImmutableList.of(ENTRY_CREATE)); |
| key.cancel(); |
| assertThat(key.isValid()).isFalse(); |
| |
| assertThat(watcher.isPolling()).isFalse(); |
| |
| Key key2 = watcher.register(createDirectory(), ImmutableList.of(ENTRY_CREATE)); |
| Key key3 = watcher.register(createDirectory(), ImmutableList.of(ENTRY_DELETE)); |
| |
| assertThat(watcher.isPolling()).isTrue(); |
| |
| key2.cancel(); |
| |
| assertThat(watcher.isPolling()).isTrue(); |
| |
| key3.cancel(); |
| |
| assertThat(watcher.isPolling()).isFalse(); |
| } |
| |
| @Test |
| public void testCloseCancelsAllKeysAndStopsPolling() throws IOException { |
| Key key1 = watcher.register(createDirectory(), ImmutableList.of(ENTRY_CREATE)); |
| Key key2 = watcher.register(createDirectory(), ImmutableList.of(ENTRY_DELETE)); |
| |
| assertThat(key1.isValid()).isTrue(); |
| assertThat(key2.isValid()).isTrue(); |
| assertThat(watcher.isPolling()).isTrue(); |
| |
| watcher.close(); |
| |
| assertThat(key1.isValid()).isFalse(); |
| assertThat(key2.isValid()).isFalse(); |
| assertThat(watcher.isPolling()).isFalse(); |
| } |
| |
| @Test(timeout = 2000) |
| public void testWatchForOneEventType() throws IOException, InterruptedException { |
| JimfsPath path = createDirectory(); |
| watcher.register(path, ImmutableList.of(ENTRY_CREATE)); |
| |
| Files.createFile(path.resolve("foo")); |
| |
| assertWatcherHasEvents(new Event<>(ENTRY_CREATE, 1, fs.getPath("foo"))); |
| |
| Files.createFile(path.resolve("bar")); |
| Files.createFile(path.resolve("baz")); |
| |
| assertWatcherHasEvents( |
| new Event<>(ENTRY_CREATE, 1, fs.getPath("bar")), |
| new Event<>(ENTRY_CREATE, 1, fs.getPath("baz"))); |
| } |
| |
| @Test(timeout = 2000) |
| public void testWatchForMultipleEventTypes() throws IOException, InterruptedException { |
| JimfsPath path = createDirectory(); |
| watcher.register(path, ImmutableList.of(ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY)); |
| |
| Files.createDirectory(path.resolve("foo")); |
| Files.createFile(path.resolve("bar")); |
| |
| assertWatcherHasEvents( |
| new Event<>(ENTRY_CREATE, 1, fs.getPath("bar")), |
| new Event<>(ENTRY_CREATE, 1, fs.getPath("foo"))); |
| |
| Files.createFile(path.resolve("baz")); |
| Files.delete(path.resolve("bar")); |
| Files.createFile(path.resolve("foo/bar")); |
| |
| assertWatcherHasEvents( |
| new Event<>(ENTRY_CREATE, 1, fs.getPath("baz")), |
| new Event<>(ENTRY_DELETE, 1, fs.getPath("bar")), |
| new Event<>(ENTRY_MODIFY, 1, fs.getPath("foo"))); |
| |
| Files.delete(path.resolve("foo/bar")); |
| ensureTimeToPoll(); // watcher polls, seeing modification, then polls again, seeing delete |
| Files.delete(path.resolve("foo")); |
| |
| assertWatcherHasEvents( |
| new Event<>(ENTRY_MODIFY, 1, fs.getPath("foo")), |
| new Event<>(ENTRY_DELETE, 1, fs.getPath("foo"))); |
| |
| Files.createDirectories(path.resolve("foo/bar")); |
| |
| // polling here may either see just the creation of foo, or may first see the creation of foo |
| // and then the creation of foo/bar (modification of foo) since those don't happen atomically |
| assertWatcherHasEvents( |
| ImmutableList.<WatchEvent<?>>of(new Event<>(ENTRY_CREATE, 1, fs.getPath("foo"))), |
| // or |
| ImmutableList.<WatchEvent<?>>of( |
| new Event<>(ENTRY_CREATE, 1, fs.getPath("foo")), |
| new Event<>(ENTRY_MODIFY, 1, fs.getPath("foo")))); |
| |
| Files.delete(path.resolve("foo/bar")); |
| Files.delete(path.resolve("foo")); |
| |
| // polling here may either just see the deletion of foo, or may first see the deletion of bar |
| // (modification of foo) and then the deletion of foo |
| assertWatcherHasEvents( |
| ImmutableList.<WatchEvent<?>>of(new Event<>(ENTRY_DELETE, 1, fs.getPath("foo"))), |
| // or |
| ImmutableList.<WatchEvent<?>>of( |
| new Event<>(ENTRY_MODIFY, 1, fs.getPath("foo")), |
| new Event<>(ENTRY_DELETE, 1, fs.getPath("foo")))); |
| } |
| |
| private void assertWatcherHasEvents(WatchEvent<?>... events) throws InterruptedException { |
| assertWatcherHasEvents(Arrays.asList(events), ImmutableList.<WatchEvent<?>>of()); |
| } |
| |
| private void assertWatcherHasEvents(List<WatchEvent<?>> expected, List<WatchEvent<?>> alternate) |
| throws InterruptedException { |
| ensureTimeToPoll(); // otherwise we could read 1 event but not all the events we're expecting |
| WatchKey key = watcher.take(); |
| List<WatchEvent<?>> keyEvents = key.pollEvents(); |
| |
| if (keyEvents.size() == expected.size() || alternate.isEmpty()) { |
| assertThat(keyEvents).containsExactlyElementsIn(expected); |
| } else { |
| assertThat(keyEvents).containsExactlyElementsIn(alternate); |
| } |
| key.reset(); |
| } |
| |
| private static void ensureTimeToPoll() { |
| Uninterruptibles.sleepUninterruptibly(40, MILLISECONDS); |
| } |
| |
| private JimfsPath createDirectory() throws IOException { |
| JimfsPath path = fs.getPath("/" + UUID.randomUUID().toString()); |
| Files.createDirectory(path); |
| return path; |
| } |
| } |