| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * 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.intellij.openapi.vfs.local; |
| |
| import com.intellij.openapi.application.AccessToken; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.util.Ref; |
| import com.intellij.openapi.util.SystemInfo; |
| import com.intellij.openapi.util.io.FileUtil; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.openapi.vfs.*; |
| import com.intellij.openapi.vfs.impl.local.FileWatcher; |
| import com.intellij.openapi.vfs.impl.local.LocalFileSystemImpl; |
| import com.intellij.openapi.vfs.newvfs.BulkFileListener; |
| import com.intellij.openapi.vfs.newvfs.NewVirtualFile; |
| import com.intellij.openapi.vfs.newvfs.events.*; |
| import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess; |
| import com.intellij.testFramework.PlatformLangTestCase; |
| import com.intellij.util.Alarm; |
| import com.intellij.util.Function; |
| import com.intellij.util.TimeoutUtil; |
| import com.intellij.util.containers.ContainerUtil; |
| import com.intellij.util.messages.MessageBusConnection; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.*; |
| |
| import static com.intellij.openapi.util.io.IoTestUtil.*; |
| |
| public class FileWatcherTest extends PlatformLangTestCase { |
| private static final int INTER_RESPONSE_DELAY = 500; // time to wait for a next event in a sequence |
| private static final int NATIVE_PROCESS_DELAY = 60000; // time to wait for a native watcher response |
| |
| private static Logger LOG = Logger.getInstance("#com.intellij.openapi.vfs.impl.local.FileWatcher"); |
| |
| private FileWatcher myWatcher; |
| private LocalFileSystem myFileSystem; |
| private MessageBusConnection myConnection; |
| private volatile boolean myAccept = false; |
| private Alarm myAlarm; |
| private final Runnable myNotifier = new Runnable() { |
| @Override |
| public void run() { |
| LOG.debug("-- (event, expected=" + myAccept + ")"); |
| if (!myAccept) return; |
| myAlarm.cancelAllRequests(); |
| myAlarm.addRequest(new Runnable() { |
| @Override |
| public void run() { |
| myAccept = false; |
| LOG.debug("** waiting finished"); |
| synchronized (myWaiter) { |
| myWaiter.notifyAll(); |
| } |
| } |
| }, INTER_RESPONSE_DELAY); |
| } |
| }; |
| private final Object myWaiter = new Object(); |
| private int myTimeout = NATIVE_PROCESS_DELAY; |
| private final List<VFileEvent> myEvents = ContainerUtil.newArrayList(); |
| private final List<String> myAcceptedDirectories = ContainerUtil.newArrayList(); |
| |
| @Override |
| protected void setUp() throws Exception { |
| LOG.debug("================== setting up " + getName() + " =================="); |
| |
| super.setUp(); |
| |
| myFileSystem = LocalFileSystem.getInstance(); |
| assertNotNull(myFileSystem); |
| |
| myWatcher = ((LocalFileSystemImpl)myFileSystem).getFileWatcher(); |
| assertNotNull(myWatcher); |
| assertFalse(myWatcher.isOperational()); |
| myWatcher.startup(myNotifier); |
| assertTrue(myWatcher.isOperational()); |
| |
| myAlarm = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, getProject()); |
| myTimeout = NATIVE_PROCESS_DELAY; |
| |
| myConnection = ApplicationManager.getApplication().getMessageBus().connect(); |
| myConnection.subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener.Adapter() { |
| @Override |
| public void after(@NotNull List<? extends VFileEvent> events) { |
| synchronized (myEvents) { |
| myEvents.addAll(events); |
| } |
| } |
| }); |
| |
| ((LocalFileSystemImpl)myFileSystem).cleanupForNextTest(); |
| |
| myAcceptedDirectories.clear(); |
| myAcceptedDirectories.add(FileUtil.getTempDirectory()); |
| |
| LOG = FileWatcher.getLog(); |
| LOG.debug("================== setting up " + getName() + " =================="); |
| } |
| |
| @Override |
| protected void tearDown() throws Exception { |
| LOG.debug("================== tearing down " + getName() + " =================="); |
| |
| try { |
| myConnection.disconnect(); |
| myWatcher.shutdown(); |
| assertFalse(myWatcher.isOperational()); |
| } |
| finally { |
| myFileSystem = null; |
| myWatcher = null; |
| super.tearDown(); |
| } |
| |
| LOG.debug("================== tearing down " + getName() + " =================="); |
| } |
| |
| |
| public void testFileRoot() throws Exception { |
| File file = createTestFile("test.txt"); |
| refresh(file); |
| |
| LocalFileSystem.WatchRequest request = watch(file); |
| try { |
| myAccept = true; |
| FileUtil.writeToFile(file, "new content"); |
| assertEvent(VFileContentChangeEvent.class, file.getPath()); |
| |
| myAccept = true; |
| FileUtil.delete(file); |
| assertEvent(VFileDeleteEvent.class, file.getPath()); |
| |
| myAccept = true; |
| FileUtil.writeToFile(file, "re-creation"); |
| assertEvent(VFileCreateEvent.class, file.getPath()); |
| } |
| finally { |
| unwatch(request); |
| delete(file); |
| } |
| } |
| |
| public void testNonCanonicallyNamedFileRoot() throws Exception { |
| if (SystemInfo.isFileSystemCaseSensitive) { |
| System.err.println("Ignored: case-insensitive FS required"); |
| return; |
| } |
| |
| File file = createTestFile("test.txt"); |
| refresh(file); |
| |
| String watchRoot = file.getPath().toUpperCase(Locale.US); |
| LocalFileSystem.WatchRequest request = watch(new File(watchRoot)); |
| try { |
| myAccept = true; |
| FileUtil.writeToFile(file, "new content"); |
| assertEvent(VFileContentChangeEvent.class, file.getPath()); |
| |
| myAccept = true; |
| FileUtil.delete(file); |
| assertEvent(VFileDeleteEvent.class, file.getPath()); |
| |
| myAccept = true; |
| FileUtil.writeToFile(file, "re-creation"); |
| assertEvent(VFileCreateEvent.class, file.getPath()); |
| } |
| finally { |
| unwatch(request); |
| delete(file); |
| } |
| } |
| |
| public void testDirectoryRecursive() throws Exception { |
| File topDir = createTestDir("top"); |
| refresh(topDir); |
| |
| LocalFileSystem.WatchRequest request = watch(topDir); |
| try { |
| myAccept = true; |
| File subDir = createTestDir(topDir, "sub"); |
| assertEvent(VFileCreateEvent.class, subDir.getPath()); |
| refresh(subDir); |
| |
| myAccept = true; |
| File file = createTestFile(subDir, "test.txt"); |
| assertEvent(VFileCreateEvent.class, file.getPath()); |
| |
| myAccept = true; |
| FileUtil.writeToFile(file, "new content"); |
| assertEvent(VFileContentChangeEvent.class, file.getPath()); |
| |
| myAccept = true; |
| FileUtil.delete(file); |
| assertEvent(VFileDeleteEvent.class, file.getPath()); |
| |
| myAccept = true; |
| FileUtil.writeToFile(file, "re-creation"); |
| assertEvent(VFileCreateEvent.class, file.getPath()); |
| } |
| finally { |
| unwatch(request); |
| delete(topDir); |
| } |
| } |
| |
| public void testDirectoryFlat() throws Exception { |
| File topDir = createTestDir("top"); |
| File watchedFile = createTestFile(topDir, "test.txt"); |
| File subDir = createTestDir(topDir, "sub"); |
| File unwatchedFile = createTestFile(subDir, "test.txt"); |
| refresh(topDir); |
| |
| LocalFileSystem.WatchRequest request = watch(topDir, false); |
| try { |
| myAccept = true; |
| FileUtil.writeToFile(watchedFile, "new content"); |
| assertEvent(VFileContentChangeEvent.class, watchedFile.getPath()); |
| |
| myTimeout = 10 * INTER_RESPONSE_DELAY; |
| myAccept = true; |
| FileUtil.writeToFile(unwatchedFile, "new content"); |
| assertEvent(VFileEvent.class); |
| myTimeout = NATIVE_PROCESS_DELAY; |
| } |
| finally { |
| unwatch(request); |
| delete(topDir); |
| } |
| } |
| |
| public void testDirectoryMixed() throws Exception { |
| File topDir = createTestDir("top"); |
| File watchedFile1 = createTestFile(topDir, "test.txt"); |
| File sub1Dir = createTestDir(topDir, "sub1"); |
| File unwatchedFile = createTestFile(sub1Dir, "test.txt"); |
| File sub2Dir = createTestDir(topDir, "sub2"); |
| File sub2subDir = createTestDir(sub2Dir, "sub"); |
| File watchedFile2 = createTestFile(sub2subDir, "test.txt"); |
| refresh(topDir); |
| |
| LocalFileSystem.WatchRequest topRequest = watch(topDir, false); |
| LocalFileSystem.WatchRequest subRequest = watch(sub2Dir); |
| try { |
| myAccept = true; |
| FileUtil.writeToFile(watchedFile1, "new content"); |
| FileUtil.writeToFile(watchedFile2, "new content"); |
| FileUtil.writeToFile(unwatchedFile, "new content"); |
| assertEvent(VFileContentChangeEvent.class, watchedFile1.getPath(), watchedFile2.getPath()); |
| } |
| finally { |
| unwatch(subRequest, topRequest); |
| delete(topDir); |
| } |
| } |
| |
| public void testDirectoryNonExisting() throws Exception { |
| File topDir = createTestDir("top"); |
| File subDir = new File(topDir, "subDir"); |
| File file = new File(subDir, "file.txt"); |
| refresh(topDir); |
| |
| LocalFileSystem.WatchRequest request = watch(subDir); |
| try { |
| myAccept = true; |
| assertTrue(subDir.toString(), subDir.mkdir()); |
| assertEvent(VFileCreateEvent.class, subDir.getPath()); |
| refresh(subDir); |
| |
| myAccept = true; |
| FileUtil.writeToFile(file, "new content"); |
| assertEvent(VFileCreateEvent.class, file.getPath()); |
| } |
| finally { |
| unwatch(request); |
| delete(topDir); |
| } |
| } |
| |
| public void testIncorrectPath() throws Exception { |
| File topDir = createTestDir("top"); |
| File file = createTestFile(topDir, "file.zip"); |
| File subDir = new File(file, "sub/zip"); |
| refresh(topDir); |
| |
| LocalFileSystem.WatchRequest request = watch(subDir, false); |
| try { |
| myTimeout = 10 * INTER_RESPONSE_DELAY; |
| myAccept = true; |
| FileUtil.writeToFile(file, "new content"); |
| assertEvent(VFileEvent.class); |
| myTimeout = NATIVE_PROCESS_DELAY; |
| } |
| finally { |
| unwatch(request); |
| delete(topDir); |
| } |
| } |
| |
| public void testDirectoryOverlapping() throws Exception { |
| File topDir = createTestDir("top"); |
| File fileInTopDir = createTestFile(topDir, "file1.txt"); |
| File subDir = createTestDir(topDir, "sub"); |
| File fileInSubDir = createTestFile(subDir, "file2.txt"); |
| File sideDir = createTestDir("side"); |
| File fileInSideDir = createTestFile(sideDir, "file3.txt"); |
| refresh(topDir); |
| refresh(sideDir); |
| |
| LocalFileSystem.WatchRequest requestForSubDir = watch(subDir); |
| LocalFileSystem.WatchRequest requestForSideDir = watch(sideDir); |
| try { |
| myAccept = true; |
| FileUtil.writeToFile(fileInTopDir, "new content"); |
| FileUtil.writeToFile(fileInSubDir, "new content"); |
| FileUtil.writeToFile(fileInSideDir, "new content"); |
| assertEvent(VFileContentChangeEvent.class, fileInSubDir.getPath(), fileInSideDir.getPath()); |
| |
| LocalFileSystem.WatchRequest requestForTopDir = watch(topDir); |
| try { |
| myAccept = true; |
| FileUtil.writeToFile(fileInTopDir, "newer content"); |
| FileUtil.writeToFile(fileInSubDir, "newer content"); |
| FileUtil.writeToFile(fileInSideDir, "newer content"); |
| assertEvent(VFileContentChangeEvent.class, fileInTopDir.getPath(), fileInSubDir.getPath(), fileInSideDir.getPath()); |
| } |
| finally { |
| unwatch(requestForTopDir); |
| } |
| |
| myAccept = true; |
| FileUtil.writeToFile(fileInTopDir, "newest content"); |
| FileUtil.writeToFile(fileInSubDir, "newest content"); |
| FileUtil.writeToFile(fileInSideDir, "newest content"); |
| assertEvent(VFileContentChangeEvent.class, fileInSubDir.getPath(), fileInSideDir.getPath()); |
| |
| myAccept = true; |
| FileUtil.delete(fileInTopDir); |
| FileUtil.delete(fileInSubDir); |
| FileUtil.delete(fileInSideDir); |
| assertEvent(VFileDeleteEvent.class, fileInTopDir.getPath(), fileInSubDir.getPath(), fileInSideDir.getPath()); |
| } |
| finally { |
| unwatch(requestForSubDir, requestForSideDir); |
| delete(topDir); |
| } |
| } |
| |
| /* |
| public void testSymlinkAboveWatchRoot() throws Exception { |
| final File topDir = FileUtil.createTempDirectory("top.", null); |
| final File topLink = IoTestUtil.createTempLink(topDir.getPath(), "link"); |
| final File subDir = FileUtil.createTempDirectory(topDir, "sub.", null); |
| final File file = FileUtil.createTempFile(subDir, "test.", ".txt"); |
| final File fileLink = new File(new File(topLink, subDir.getName()), file.getName()); |
| refresh(topDir); |
| refresh(topLink); |
| |
| final LocalFileSystem.WatchRequest request = watch(topLink); |
| try { |
| myAccept = true; |
| FileUtil.writeToFile(file, "new content"); |
| assertEvent(VFileContentChangeEvent.class, fileLink.getPath()); |
| |
| myAccept = true; |
| FileUtil.delete(file); |
| assertEvent(VFileDeleteEvent.class, fileLink.getPath()); |
| |
| myAccept = true; |
| FileUtil.writeToFile(file, "re-creation"); |
| assertEvent(VFileCreateEvent.class, fileLink.getPath()); |
| } |
| finally { |
| myFileSystem.removeWatchedRoot(request); |
| delete(topLink); |
| delete(topDir); |
| } |
| } |
| |
| public void testSymlinkBelowWatchRoot() throws Exception { |
| final File targetDir = FileUtil.createTempDirectory("top.", null); |
| final File file = FileUtil.createTempFile(targetDir, "test.", ".txt"); |
| final File linkDir = FileUtil.createTempDirectory("link.", null); |
| final File link = new File(linkDir, "link"); |
| IoTestUtil.createTempLink(targetDir.getPath(), link.getPath()); |
| final File fileLink = new File(link, file.getName()); |
| refresh(targetDir); |
| refresh(linkDir); |
| |
| final LocalFileSystem.WatchRequest request = watch(linkDir); |
| try { |
| myAccept = true; |
| FileUtil.writeToFile(file, "new content"); |
| assertEvent(VFileContentChangeEvent.class, fileLink.getPath()); |
| |
| myAccept = true; |
| FileUtil.delete(file); |
| assertEvent(VFileDeleteEvent.class, fileLink.getPath()); |
| |
| myAccept = true; |
| FileUtil.writeToFile(file, "re-creation"); |
| assertEvent(VFileCreateEvent.class, fileLink.getPath()); |
| } |
| finally { |
| myFileSystem.removeWatchedRoot(request); |
| delete(linkDir); |
| delete(targetDir); |
| } |
| } |
| */ |
| |
| public void testSubst() throws Exception { |
| if (!SystemInfo.isWindows) { |
| System.err.println("Ignored: Windows required"); |
| return; |
| } |
| |
| File targetDir = createTestDir("top"); |
| File subDir = createTestDir(targetDir, "sub"); |
| File file = createTestFile(subDir, "test.txt"); |
| File rootFile = createSubst(targetDir.getPath()); |
| VfsRootAccess.allowRootAccess(rootFile.getPath()); |
| VirtualFile vfsRoot = myFileSystem.findFileByIoFile(rootFile); |
| |
| try { |
| assertNotNull(rootFile.getPath(), vfsRoot); |
| File substDir = new File(rootFile, subDir.getName()); |
| File substFile = new File(substDir, file.getName()); |
| refresh(targetDir); |
| refresh(substDir); |
| myAcceptedDirectories.add(substDir.getPath()); |
| |
| LocalFileSystem.WatchRequest request = watch(substDir); |
| try { |
| myAccept = true; |
| FileUtil.writeToFile(file, "new content"); |
| assertEvent(VFileContentChangeEvent.class, substFile.getPath()); |
| |
| LocalFileSystem.WatchRequest request2 = watch(targetDir); |
| try { |
| myAccept = true; |
| FileUtil.delete(file); |
| assertEvent(VFileDeleteEvent.class, file.getPath(), substFile.getPath()); |
| } |
| finally { |
| unwatch(request2); |
| } |
| |
| myAccept = true; |
| FileUtil.writeToFile(file, "re-creation"); |
| assertEvent(VFileCreateEvent.class, substFile.getPath()); |
| } |
| finally { |
| unwatch(request); |
| } |
| } |
| finally { |
| delete(targetDir); |
| deleteSubst(rootFile.getPath()); |
| if (vfsRoot != null) { |
| ((NewVirtualFile)vfsRoot).markDirty(); |
| myFileSystem.refresh(false); |
| } |
| VfsRootAccess.disallowRootAccess(rootFile.getPath()); |
| } |
| } |
| |
| public void testDirectoryRecreation() throws Exception { |
| File rootDir = createTestDir("root"); |
| File topDir = createTestDir(rootDir, "top"); |
| File file1 = createTestFile(topDir, "file1.txt", "abc"); |
| File file2 = createTestFile(topDir, "file2.txt", "123"); |
| refresh(topDir); |
| |
| LocalFileSystem.WatchRequest request = watch(rootDir); |
| try { |
| myAccept = true; |
| assertTrue(FileUtil.delete(topDir)); |
| assertTrue(topDir.mkdir()); |
| TimeoutUtil.sleep(100); |
| assertTrue(file1.createNewFile()); |
| assertTrue(file2.createNewFile()); |
| assertEvent(VFileContentChangeEvent.class, file1.getPath(), file2.getPath()); |
| } |
| finally { |
| unwatch(request); |
| delete(topDir); |
| } |
| } |
| |
| public void testWatchRootRecreation() throws Exception { |
| File rootDir = createTestDir("root"); |
| File file1 = createTestFile(rootDir, "file1.txt", "abc"); |
| File file2 = createTestFile(rootDir, "file2.txt", "123"); |
| refresh(rootDir); |
| |
| LocalFileSystem.WatchRequest request = watch(rootDir); |
| try { |
| myAccept = true; |
| assertTrue(FileUtil.delete(rootDir)); |
| assertTrue(rootDir.mkdir()); |
| if (SystemInfo.isLinux) TimeoutUtil.sleep(1500); // implementation specific |
| assertTrue(file1.createNewFile()); |
| assertTrue(file2.createNewFile()); |
| assertEvent(VFileContentChangeEvent.class, file1.getPath(), file2.getPath()); |
| } |
| finally { |
| unwatch(request); |
| delete(rootDir); |
| } |
| } |
| |
| public void testWatchRootRenameRemove() throws Exception { |
| File topDir = createTestDir("top"); |
| File rootDir = createTestDir(topDir, "root"); |
| File rootDir2 = new File(topDir, "_" + rootDir.getName()); |
| refresh(topDir); |
| |
| LocalFileSystem.WatchRequest request = watch(rootDir); |
| try { |
| myAccept = true; |
| assertTrue(rootDir.renameTo(rootDir2)); |
| assertEvent(VFileEvent.class, rootDir.getPath(), rootDir2.getPath()); |
| |
| myAccept = true; |
| assertTrue(rootDir2.renameTo(rootDir)); |
| assertEvent(VFileEvent.class, rootDir.getPath(), rootDir2.getPath()); |
| |
| myAccept = true; |
| assertTrue(FileUtil.delete(rootDir)); |
| assertEvent(VFileDeleteEvent.class, rootDir.getPath()); |
| |
| myAccept = true; |
| assertTrue(rootDir.mkdirs()); |
| assertEvent(VFileCreateEvent.class, rootDir.getPath()); |
| |
| myAccept = true; |
| assertTrue(FileUtil.delete(topDir)); |
| assertEvent(VFileDeleteEvent.class, topDir.getPath()); |
| |
| // todo[r.sh] current VFS implementation loses watch root once it's removed; this probably should be fixed |
| myAccept = true; |
| assertTrue(rootDir.mkdirs()); |
| assertEvent(VFileCreateEvent.class); |
| } |
| finally { |
| unwatch(request); |
| delete(topDir); |
| } |
| } |
| |
| public void testSwitchingToFsRoot() throws Exception { |
| File topDir = createTestDir("top"); |
| File rootDir = createTestDir(topDir, "root"); |
| File file1 = createTestFile(topDir, "1.txt"); |
| File file2 = createTestFile(rootDir, "2.txt"); |
| refresh(topDir); |
| |
| File fsRoot = new File(SystemInfo.isUnix ? "/" : topDir.getPath().substring(0, topDir.getPath().indexOf(File.separatorChar)) + "\\"); |
| assertTrue("can't guess root of " + topDir, fsRoot.exists()); |
| |
| LocalFileSystem.WatchRequest request = watch(rootDir); |
| try { |
| myAccept = true; |
| FileUtil.writeToFile(file1, "abc"); |
| FileUtil.writeToFile(file2, "abc"); |
| assertEvent(VFileContentChangeEvent.class, file2.getPath()); |
| |
| LocalFileSystem.WatchRequest rootRequest = watch(fsRoot); |
| try { |
| myTimeout = 10 * INTER_RESPONSE_DELAY; |
| myAccept = true; |
| FileUtil.writeToFile(file1, "12345"); |
| FileUtil.writeToFile(file2, "12345"); |
| assertEvent(VFileContentChangeEvent.class, file1.getPath(), file2.getPath()); |
| myTimeout = NATIVE_PROCESS_DELAY; |
| } |
| finally { |
| unwatch(rootRequest); |
| } |
| |
| myAccept = true; |
| FileUtil.writeToFile(file1, ""); |
| FileUtil.writeToFile(file2, ""); |
| assertEvent(VFileContentChangeEvent.class, file2.getPath()); |
| } |
| finally { |
| unwatch(request); |
| } |
| |
| myTimeout = 10 * INTER_RESPONSE_DELAY; |
| myAccept = true; |
| FileUtil.writeToFile(file1, "xyz"); |
| FileUtil.writeToFile(file2, "xyz"); |
| assertEvent(VFileEvent.class); |
| myTimeout = NATIVE_PROCESS_DELAY; |
| } |
| |
| public void testLineBreaksInName() throws Exception { |
| if (!SystemInfo.isUnix) { |
| System.err.println("Ignored: Unix required"); |
| return; |
| } |
| |
| File topDir = createTestDir("topDir"); |
| File testDir = createTestDir(topDir, "weird\ndir\nname"); |
| File testFile = createTestFile(testDir, "weird\nfile\nname"); |
| refresh(topDir); |
| |
| LocalFileSystem.WatchRequest request = watch(topDir); |
| try { |
| myAccept = true; |
| FileUtil.writeToFile(testFile, "abc"); |
| assertEvent(VFileContentChangeEvent.class, testFile.getPath()); |
| } |
| finally { |
| unwatch(request); |
| } |
| } |
| |
| public void testHiddenFiles() throws Exception { |
| if (!SystemInfo.isWindows) { |
| System.err.println("Ignored: Windows required"); |
| return; |
| } |
| |
| File topDir = createTestDir("topDir"); |
| File testDir = createTestDir(topDir, "dir"); |
| File testFile = createTestFile(testDir, "file", "123"); |
| refresh(topDir); |
| |
| LocalFileSystem.WatchRequest request = watch(topDir); |
| try { |
| myAccept = true; |
| setHidden(testFile.getPath(), true); |
| assertEvent(VFilePropertyChangeEvent.class, testFile.getPath()); |
| } |
| finally { |
| unwatch(request); |
| } |
| } |
| |
| public void testFileCaseChange() throws Exception { |
| if (SystemInfo.isFileSystemCaseSensitive) { |
| System.err.println("Ignored: case-insensitive FS required"); |
| return; |
| } |
| |
| File topDir = createTestDir("topDir"); |
| File testFile = createTestFile(topDir, "file.txt", "123"); |
| refresh(topDir); |
| |
| LocalFileSystem.WatchRequest request = watch(topDir); |
| try { |
| myAccept = true; |
| File newFile = new File(testFile.getParent(), StringUtil.capitalize(testFile.getName())); |
| FileUtil.rename(testFile, newFile); |
| assertEvent(VFilePropertyChangeEvent.class, newFile.getPath()); |
| } |
| finally { |
| unwatch(request); |
| } |
| } |
| |
| public void testPartialRefresh() throws Exception { |
| // tests the same scenario with an active file watcher (prevents explicit marking of refreshed paths) |
| File top = createTestDir("top"); |
| LocalFileSystemTest.doTestPartialRefresh(top); |
| } |
| |
| public void testInterruptedRefresh() throws Exception { |
| // tests the same scenario with an active file watcher (prevents explicit marking of refreshed paths) |
| File top = createTestDir("top"); |
| LocalFileSystemTest.doTestInterruptedRefresh(top); |
| } |
| |
| public void testUnicodePaths() throws Exception { |
| if (!SystemInfo.isUnix || SystemInfo.isMac) { |
| System.err.println("Ignored: well-defined FS required"); |
| return; |
| } |
| |
| File topDir = createTestDir("top"); |
| File testDir = createTestDir(topDir, "тест"); |
| File testFile = createTestFile(testDir, "файл.txt"); |
| refresh(topDir); |
| |
| LocalFileSystem.WatchRequest request = watch(topDir); |
| try { |
| myAccept = true; |
| FileUtil.writeToFile(testFile, "abc"); |
| assertEvent(VFileContentChangeEvent.class, testFile.getPath()); |
| } |
| finally { |
| unwatch(request); |
| } |
| } |
| |
| |
| @NotNull |
| private LocalFileSystem.WatchRequest watch(File watchFile) { |
| return watch(watchFile, true); |
| } |
| |
| @NotNull |
| private LocalFileSystem.WatchRequest watch(final File watchFile, final boolean recursive) { |
| final Ref<LocalFileSystem.WatchRequest> request = Ref.create(); |
| getEvents("events to add watch " + watchFile, new Runnable() { |
| @Override |
| public void run() { |
| request.set(myFileSystem.addRootToWatch(watchFile.getPath(), recursive)); |
| } |
| }); |
| assertFalse(request.isNull()); |
| assertFalse(myWatcher.isSettingRoots()); |
| return request.get(); |
| } |
| |
| private void unwatch(final LocalFileSystem.WatchRequest... requests) { |
| getEvents("events to stop watching", new Runnable() { |
| @Override |
| public void run() { |
| myFileSystem.removeWatchedRoots(Arrays.asList(requests)); |
| } |
| }); |
| } |
| |
| private VirtualFile refresh(File file) { |
| VirtualFile vFile = myFileSystem.refreshAndFindFileByIoFile(file); |
| assertNotNull(file.toString(), vFile); |
| VfsUtilCore.visitChildrenRecursively(vFile, new VirtualFileVisitor() { |
| @Override |
| public boolean visitFile(@NotNull VirtualFile file) { |
| file.getChildren(); |
| return true; |
| } |
| }); |
| return vFile; |
| } |
| |
| private void delete(File file) throws IOException { |
| VirtualFile vFile = myFileSystem.findFileByIoFile(file); |
| if (vFile != null) { |
| AccessToken token = ApplicationManager.getApplication().acquireWriteActionLock(getClass()); |
| try { |
| vFile.delete(this); |
| } |
| finally { |
| token.finish(); |
| } |
| } |
| if (file.exists()) { |
| FileUtil.delete(file); |
| } |
| } |
| |
| private List<VFileEvent> getEvents(String msg, @Nullable Runnable action) { |
| LOG.debug("** waiting for " + msg); |
| myAccept = true; |
| |
| if (action != null) { |
| action.run(); |
| } |
| |
| long timeout = myTimeout, start = System.currentTimeMillis(); |
| try { |
| synchronized (myWaiter) { |
| //noinspection WaitNotInLoop |
| myWaiter.wait(timeout); |
| } |
| } |
| catch (InterruptedException e) { |
| LOG.warn(e); |
| } |
| |
| LOG.debug("** waited for " + (System.currentTimeMillis() - start) + " of " + timeout); |
| myFileSystem.refresh(false); |
| |
| List<VFileEvent> result; |
| synchronized (myEvents) { |
| result = ContainerUtil.newArrayList(myEvents); |
| myEvents.clear(); |
| } |
| |
| if (!result.isEmpty()) { |
| nextEvent: |
| for (Iterator<VFileEvent> iterator = result.iterator(); iterator.hasNext(); ) { |
| VFileEvent event = iterator.next(); |
| VirtualFile file = event.getFile(); |
| if (file != null) { |
| for (String acceptedDirectory : myAcceptedDirectories) { |
| if (FileUtil.isAncestor(acceptedDirectory, file.getPath(), false)) { |
| continue nextEvent; |
| } |
| } |
| LOG.debug("~~ not accepted: " + event); |
| iterator.remove(); |
| } |
| } |
| } |
| |
| LOG.debug("** events: " + result.size()); |
| return result; |
| } |
| |
| private void assertEvent(Class<? extends VFileEvent> type, String... paths) { |
| List<VFileEvent> events = getEvents(type.getSimpleName(), null); |
| assertEquals(events.toString(), paths.length, events.size()); |
| |
| Set<String> pathSet = ContainerUtil.map2Set(paths, new Function<String, String>() { |
| @Override |
| public String fun(final String path) { |
| return FileUtil.toSystemIndependentName(path); |
| } |
| }); |
| |
| for (VFileEvent event : events) { |
| assertTrue(event.toString(), type.isInstance(event)); |
| VirtualFile eventFile = event.getFile(); |
| assertNotNull(event.toString(), eventFile); |
| assertTrue(eventFile + " not in " + Arrays.toString(paths), pathSet.remove(eventFile.getPath())); |
| } |
| } |
| } |