/******************************************************************************* * Copyright (c) 2012-2017 Codenvy, S.A. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Codenvy, S.A. - initial API and implementation *******************************************************************************/ package org.eclipse.che.api.vfs.impl.file; import org.eclipse.che.commons.lang.IoUtil; import org.eclipse.che.commons.lang.NameGenerator; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import java.io.File; import java.nio.file.FileSystems; import java.nio.file.PathMatcher; import java.util.List; import java.util.Set; import static com.google.common.collect.Sets.newHashSet; import static org.eclipse.che.api.project.shared.dto.event.FileWatcherEventType.CREATED; import static org.eclipse.che.api.project.shared.dto.event.FileWatcherEventType.DELETED; import static org.eclipse.che.api.project.shared.dto.event.FileWatcherEventType.MODIFIED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; public class FileTreeWatcherTest { private File testDirectory; private FileTreeWatcher fileWatcher; private FileWatcherTestTree fileWatcherTestTree; @Before public void setUp() throws Exception { File targetDir = new File(Thread.currentThread().getContextClassLoader().getResource(".").getPath()).getParentFile(); testDirectory = new File(targetDir, NameGenerator.generate("watcher-", 4)); assertTrue(testDirectory.mkdir()); fileWatcherTestTree = new FileWatcherTestTree(testDirectory); } @After public void tearDown() throws Exception { if (fileWatcher != null) { fileWatcher.shutdown(); } IoUtil.deleteRecursive(testDirectory); } @Test public void watchesCreate() throws Exception { fileWatcherTestTree.createDirectory("", "watched"); FileWatcherNotificationHandler notificationHandler = aNotificationHandler(); fileWatcher = new FileTreeWatcher(testDirectory, newHashSet(), notificationHandler); fileWatcher.startup(); Thread.sleep(500); Set<String> created = newHashSet(fileWatcherTestTree.createDirectory(""), fileWatcherTestTree.createFile(""), fileWatcherTestTree.createDirectory("watched"), fileWatcherTestTree.createFile("watched")); Thread.sleep(5000); verify(notificationHandler, never()).errorOccurred(eq(testDirectory), any(Throwable.class)); verify(notificationHandler, never()).handleFileWatcherEvent(eq(DELETED), eq(testDirectory), anyString(), anyBoolean()); verify(notificationHandler, never()).handleFileWatcherEvent(eq(MODIFIED), eq(testDirectory), anyString(), anyBoolean()); ArgumentCaptor<String> createdEvents = ArgumentCaptor.forClass(String.class); verify(notificationHandler, times(4)).handleFileWatcherEvent(eq(CREATED), eq(testDirectory), createdEvents.capture(), anyBoolean()); assertEquals(newHashSet(created), newHashSet(createdEvents.getAllValues())); } @Test public void watchesCreateDirectoryStructure() throws Exception { FileWatcherNotificationHandler notificationHandler = aNotificationHandler(); fileWatcher = new FileTreeWatcher(testDirectory, newHashSet(), notificationHandler); fileWatcher.startup(); Thread.sleep(500); List<String> created = fileWatcherTestTree.createTree("", 2, 2); Thread.sleep(5000); verify(notificationHandler, never()).errorOccurred(eq(testDirectory), any(Throwable.class)); verify(notificationHandler, never()).handleFileWatcherEvent(eq(DELETED), eq(testDirectory), anyString(), anyBoolean()); verify(notificationHandler, never()).handleFileWatcherEvent(eq(MODIFIED), eq(testDirectory), anyString(), anyBoolean()); ArgumentCaptor<String> createdEvents = ArgumentCaptor.forClass(String.class); verify(notificationHandler, times(4)).handleFileWatcherEvent(eq(CREATED), eq(testDirectory), createdEvents.capture(), anyBoolean()); assertEquals(newHashSet(created), newHashSet(createdEvents.getAllValues())); } @Test public void watchesCreatedSubDirectoriesRecursively() throws Exception { FileWatcherNotificationHandler notificationHandler = aNotificationHandler(); fileWatcher = new FileTreeWatcher(testDirectory, newHashSet(), notificationHandler); fileWatcher.startup(); Thread.sleep(500); final String first = fileWatcherTestTree.createDirectory("", "first"); final String second = fileWatcherTestTree.createDirectory(first, "second"); final String file = fileWatcherTestTree.createFile(second); Thread.sleep(5000); fileWatcherTestTree.updateFile(file); Thread.sleep(5000); verify(notificationHandler, never()).errorOccurred(eq(testDirectory), any(Throwable.class)); verify(notificationHandler, never()).handleFileWatcherEvent(eq(DELETED), eq(testDirectory), anyString(), anyBoolean()); verify(notificationHandler, times(3)).handleFileWatcherEvent(eq(CREATED), eq(testDirectory), anyString(), anyBoolean()); ArgumentCaptor<String> modifiedEvents = ArgumentCaptor.forClass(String.class); verify(notificationHandler).handleFileWatcherEvent(eq(MODIFIED), eq(testDirectory), modifiedEvents.capture(), anyBoolean()); assertEquals(newHashSet(file), newHashSet(modifiedEvents.getAllValues())); } @Test public void watchesCreateDirectoryAndStartsWatchingNewlyCreatedDirectory() throws Exception { FileWatcherNotificationHandler notificationHandler = aNotificationHandler(); fileWatcher = new FileTreeWatcher(testDirectory, newHashSet(), notificationHandler); fileWatcher.startup(); Thread.sleep(500); String directory = fileWatcherTestTree.createDirectory(""); Thread.sleep(5000); String file = fileWatcherTestTree.createFile(directory); Thread.sleep(5000); verify(notificationHandler, never()).errorOccurred(eq(testDirectory), any(Throwable.class)); verify(notificationHandler, never()).handleFileWatcherEvent(eq(DELETED), eq(testDirectory), anyString(), anyBoolean()); verify(notificationHandler, never()).handleFileWatcherEvent(eq(MODIFIED), eq(testDirectory), anyString(), anyBoolean()); ArgumentCaptor<String> createdEvents = ArgumentCaptor.forClass(String.class); verify(notificationHandler, times(2)).handleFileWatcherEvent(eq(CREATED), eq(testDirectory), createdEvents.capture(), anyBoolean()); assertEquals(newHashSet(directory, file), newHashSet(createdEvents.getAllValues())); } @Test public void watchesUpdate() throws Exception { fileWatcherTestTree.createDirectory("", "watched"); String notifiedFile1 = fileWatcherTestTree.createFile(""); String notifiedFile2 = fileWatcherTestTree.createFile("watched"); Set<String> updated = newHashSet(notifiedFile1, notifiedFile2); FileWatcherNotificationHandler notificationHandler = aNotificationHandler(); fileWatcher = new FileTreeWatcher(testDirectory, newHashSet(), notificationHandler); fileWatcher.startup(); Thread.sleep(1000); fileWatcherTestTree.updateFile(notifiedFile1); fileWatcherTestTree.updateFile(notifiedFile2); Thread.sleep(5000); verify(notificationHandler, never()).errorOccurred(eq(testDirectory), any(Throwable.class)); verify(notificationHandler, never()).handleFileWatcherEvent(eq(CREATED), eq(testDirectory), anyString(), anyBoolean()); verify(notificationHandler, never()).handleFileWatcherEvent(eq(DELETED), eq(testDirectory), anyString(), anyBoolean()); ArgumentCaptor<String> updatedEvents = ArgumentCaptor.forClass(String.class); verify(notificationHandler, times(2)).handleFileWatcherEvent(eq(MODIFIED), eq(testDirectory), updatedEvents.capture(), anyBoolean()); assertEquals(updated, newHashSet(updatedEvents.getAllValues())); } @Test public void watchesFolderModifiedOnDelete() throws Exception { final String watchedDir = fileWatcherTestTree.createDirectory("", "watched"); final String notifiedFile = fileWatcherTestTree.createFile("watched"); final Set<String> deleted = newHashSet(notifiedFile); final Set<String> modified = newHashSet(watchedDir); final FileWatcherNotificationHandler notificationHandler = aNotificationHandler(); fileWatcher = new FileTreeWatcher(testDirectory, newHashSet(), notificationHandler); fileWatcher.startup(); Thread.sleep(1000); fileWatcherTestTree.delete(notifiedFile); Thread.sleep(5000); verify(notificationHandler, never()).errorOccurred(eq(testDirectory), any(Throwable.class)); final ArgumentCaptor<String> deletedEvents = ArgumentCaptor.forClass(String.class); verify(notificationHandler, times(1)).handleFileWatcherEvent(eq(DELETED), eq(testDirectory), deletedEvents.capture(), anyBoolean()); assertEquals(deleted, newHashSet(deletedEvents.getAllValues())); final ArgumentCaptor<String> modifiedEvents = ArgumentCaptor.forClass(String.class); verify(notificationHandler, times(1)).handleFileWatcherEvent(eq(MODIFIED), eq(testDirectory), modifiedEvents.capture(), anyBoolean()); assertEquals(modified, newHashSet(modifiedEvents.getAllValues())); } @Test public void watchesFolderModifiedOnCreate() throws Exception { final String watchedDir = fileWatcherTestTree.createDirectory("", "watched"); final Set<String> modified = newHashSet(watchedDir); final FileWatcherNotificationHandler notificationHandler = aNotificationHandler(); fileWatcher = new FileTreeWatcher(testDirectory, newHashSet(), notificationHandler); fileWatcher.startup(); Thread.sleep(1000); final String notifiedFile = fileWatcherTestTree.createFile("watched"); final Set<String> created = newHashSet(notifiedFile); Thread.sleep(5000); verify(notificationHandler, never()).errorOccurred(eq(testDirectory), any(Throwable.class)); final ArgumentCaptor<String> deletedEvents = ArgumentCaptor.forClass(String.class); verify(notificationHandler, times(1)).handleFileWatcherEvent(eq(CREATED), eq(testDirectory), deletedEvents.capture(), anyBoolean()); assertEquals(created, newHashSet(deletedEvents.getAllValues())); final ArgumentCaptor<String> modifiedEvents = ArgumentCaptor.forClass(String.class); verify(notificationHandler, times(1)).handleFileWatcherEvent(eq(MODIFIED), eq(testDirectory), modifiedEvents.capture(), anyBoolean()); assertEquals(modified, newHashSet(modifiedEvents.getAllValues())); } @Test public void watchesDelete() throws Exception { fileWatcherTestTree.createDirectory("", "watched"); String deletedDir1 = fileWatcherTestTree.createDirectory("watched"); String deletedFile1 = fileWatcherTestTree.createFile("watched"); Set<String> deleted = newHashSet("watched", deletedDir1, deletedFile1); FileWatcherNotificationHandler notificationHandler = aNotificationHandler(); fileWatcher = new FileTreeWatcher(testDirectory, newHashSet(), notificationHandler); fileWatcher.startup(); Thread.sleep(500); fileWatcherTestTree.delete("watched"); Thread.sleep(5000); verify(notificationHandler, never()).errorOccurred(eq(testDirectory), any(Throwable.class)); verify(notificationHandler, never()).handleFileWatcherEvent(eq(CREATED), eq(testDirectory), anyString(), anyBoolean()); verify(notificationHandler, never()).handleFileWatcherEvent(eq(MODIFIED), eq(testDirectory), anyString(), anyBoolean()); ArgumentCaptor<String> deletedEvents = ArgumentCaptor.forClass(String.class); verify(notificationHandler, times(3)).handleFileWatcherEvent(eq(DELETED), eq(testDirectory), deletedEvents.capture(), anyBoolean()); assertEquals(deleted, newHashSet(deletedEvents.getAllValues())); } @Test public void doesNotWatchExcludedDirectories() throws Exception { fileWatcherTestTree.createDirectory("", "excluded"); FileWatcherNotificationHandler notificationHandler = aNotificationHandler(); PathMatcher excludeMatcher = FileSystems.getDefault().getPathMatcher("glob:excluded"); fileWatcher = new FileTreeWatcher(testDirectory, newHashSet(excludeMatcher), notificationHandler); fileWatcher.startup(); Thread.sleep(500); String directory = fileWatcherTestTree.createDirectory(""); String file = fileWatcherTestTree.createFile(""); fileWatcherTestTree.createDirectory("excluded"); fileWatcherTestTree.createFile("excluded"); Set<String> created = newHashSet(directory, file); Thread.sleep(5000); verify(notificationHandler, never()).errorOccurred(eq(testDirectory), any(Throwable.class)); verify(notificationHandler, never()).handleFileWatcherEvent(eq(DELETED), eq(testDirectory), anyString(), anyBoolean()); verify(notificationHandler, never()).handleFileWatcherEvent(eq(MODIFIED), eq(testDirectory), anyString(), anyBoolean()); ArgumentCaptor<String> createdEvents = ArgumentCaptor.forClass(String.class); verify(notificationHandler, times(2)).handleFileWatcherEvent(eq(CREATED), eq(testDirectory), createdEvents.capture(), anyBoolean()); assertEquals(newHashSet(created), newHashSet(createdEvents.getAllValues())); } @Test public void doesNotNotifyAboutIgnoredFiles() throws Exception { FileWatcherNotificationHandler notificationHandler = aNotificationHandler(); PathMatcher excludeMatcher = FileSystems.getDefault().getPathMatcher("glob:*.{foo,bar}"); fileWatcher = new FileTreeWatcher(testDirectory, newHashSet(excludeMatcher), notificationHandler); fileWatcher.startup(); Thread.sleep(500); String file = fileWatcherTestTree.createFile(""); fileWatcherTestTree.createFile("", "xxx.bar"); fileWatcherTestTree.createFile("", "xxx.foo"); Set<String> created = newHashSet(file); Thread.sleep(5000); verify(notificationHandler, never()).errorOccurred(eq(testDirectory), any(Throwable.class)); verify(notificationHandler, never()).handleFileWatcherEvent(eq(DELETED), eq(testDirectory), anyString(), anyBoolean()); verify(notificationHandler, never()).handleFileWatcherEvent(eq(MODIFIED), eq(testDirectory), anyString(), anyBoolean()); ArgumentCaptor<String> createdEvents = ArgumentCaptor.forClass(String.class); verify(notificationHandler, times(1)).handleFileWatcherEvent(eq(CREATED), eq(testDirectory), createdEvents.capture(), anyBoolean()); assertEquals(newHashSet(created), newHashSet(createdEvents.getAllValues())); } @Test public void notifiesNotificationListenerWhenStarted() throws Exception { FileWatcherNotificationHandler notificationHandler = aNotificationHandler(); fileWatcher = new FileTreeWatcher(testDirectory, newHashSet(), notificationHandler); fileWatcher.startup(); Thread.sleep(500); verify(notificationHandler, timeout(10000)).started(eq(testDirectory)); } @Test public void notifiesNotificationListenerWhenErrorOccurs() throws Exception { RuntimeException error = new RuntimeException(); FileWatcherNotificationHandler notificationHandler = aNotificationHandler(); doThrow(error).when(notificationHandler).handleFileWatcherEvent(eq(CREATED), eq(testDirectory), anyString(), anyBoolean()); fileWatcher = new FileTreeWatcher(testDirectory, newHashSet(), notificationHandler); fileWatcher.startup(); Thread.sleep(500); fileWatcherTestTree.createFile(""); Thread.sleep(5000); verify(notificationHandler, timeout(10000)).errorOccurred(eq(testDirectory), eq(error)); } private FileWatcherNotificationHandler aNotificationHandler() { return mock(FileWatcherNotificationHandler.class); } }