Linux file watcher: poll for missing roots
[idea/community.git] / platform / platform-tests / testSrc / com / intellij / openapi / vfs / local / FileWatcherTest.java
1 /*
2  * Copyright 2000-2013 JetBrains s.r.o.
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 com.intellij.openapi.vfs.local;
17
18 import com.intellij.openapi.application.AccessToken;
19 import com.intellij.openapi.application.ApplicationManager;
20 import com.intellij.openapi.diagnostic.Logger;
21 import com.intellij.openapi.util.Ref;
22 import com.intellij.openapi.util.SystemInfo;
23 import com.intellij.openapi.util.io.FileUtil;
24 import com.intellij.openapi.util.io.IoTestUtil;
25 import com.intellij.openapi.vfs.*;
26 import com.intellij.openapi.vfs.impl.local.FileWatcher;
27 import com.intellij.openapi.vfs.impl.local.LocalFileSystemImpl;
28 import com.intellij.openapi.vfs.newvfs.BulkFileListener;
29 import com.intellij.openapi.vfs.newvfs.NewVirtualFile;
30 import com.intellij.openapi.vfs.newvfs.events.VFileContentChangeEvent;
31 import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent;
32 import com.intellij.openapi.vfs.newvfs.events.VFileDeleteEvent;
33 import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
34 import com.intellij.openapi.vfs.newvfs.impl.VirtualDirectoryImpl;
35 import com.intellij.testFramework.PlatformLangTestCase;
36 import com.intellij.util.Alarm;
37 import com.intellij.util.Function;
38 import com.intellij.util.TimeoutUtil;
39 import com.intellij.util.containers.ContainerUtil;
40 import com.intellij.util.messages.MessageBusConnection;
41 import org.jetbrains.annotations.NotNull;
42 import org.jetbrains.annotations.Nullable;
43
44 import java.io.File;
45 import java.io.IOException;
46 import java.util.*;
47
48 import static com.intellij.openapi.util.io.IoTestUtil.createSubst;
49 import static com.intellij.openapi.util.io.IoTestUtil.createTestDir;
50 import static com.intellij.openapi.util.io.IoTestUtil.createTestFile;
51
52 public class FileWatcherTest extends PlatformLangTestCase {
53   private static final int INTER_RESPONSE_DELAY = 500;  // time to wait for a next event in a sequence
54   private static final int NATIVE_PROCESS_DELAY = 60000;  // time to wait for a native watcher response
55
56   private static Logger LOG = Logger.getInstance("#com.intellij.openapi.vfs.impl.local.FileWatcher");
57
58   private FileWatcher myWatcher;
59   private LocalFileSystem myFileSystem;
60   private MessageBusConnection myConnection;
61   private volatile boolean myAccept = false;
62   private Alarm myAlarm;
63   private final Runnable myNotifier = new Runnable() {
64     @Override
65     public void run() {
66       LOG.debug("-- (event, expected=" + myAccept + ")");
67       if (!myAccept) return;
68       myAlarm.cancelAllRequests();
69       myAlarm.addRequest(new Runnable() {
70         @Override
71         public void run() {
72           myAccept = false;
73           LOG.debug("** waiting finished");
74           synchronized (myWaiter) {
75             myWaiter.notifyAll();
76           }
77         }
78       }, INTER_RESPONSE_DELAY);
79     }
80   };
81   private final Object myWaiter = new Object();
82   private int myTimeout = NATIVE_PROCESS_DELAY;
83   private final List<VFileEvent> myEvents = new ArrayList<VFileEvent>();
84
85   @Override
86   protected void setUp() throws Exception {
87     LOG.debug("================== setting up " + getName() + " ==================");
88
89     super.setUp();
90
91     myFileSystem = LocalFileSystem.getInstance();
92     assertNotNull(myFileSystem);
93
94     myWatcher = ((LocalFileSystemImpl)myFileSystem).getFileWatcher();
95     assertNotNull(myWatcher);
96     assertFalse(myWatcher.isOperational());
97     myWatcher.startup(myNotifier);
98     assertTrue(myWatcher.isOperational());
99
100     myAlarm = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, getProject());
101     myTimeout = NATIVE_PROCESS_DELAY;
102
103     myConnection = ApplicationManager.getApplication().getMessageBus().connect();
104     myConnection.subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener.Adapter() {
105       @Override
106       public void after(@NotNull List<? extends VFileEvent> events) {
107         synchronized (myEvents) {
108           myEvents.addAll(events);
109         }
110       }
111     });
112
113     LOG = FileWatcher.getLog();
114     LOG.debug("================== setting up " + getName() + " ==================");
115   }
116
117   @Override
118   protected void tearDown() throws Exception {
119     LOG.debug("================== tearing down " + getName() + " ==================");
120
121     try {
122       myConnection.disconnect();
123       myWatcher.shutdown();
124       assertFalse(myWatcher.isOperational());
125     }
126     finally {
127       myFileSystem = null;
128       myWatcher = null;
129       super.tearDown();
130     }
131
132     LOG.debug("================== tearing down " + getName() + " ==================");
133   }
134
135
136   public void testFileRoot() throws Exception {
137     File file = createTestFile("test.txt");
138     refresh(file);
139
140     LocalFileSystem.WatchRequest request = watch(file);
141     try {
142       myAccept = true;
143       FileUtil.writeToFile(file, "new content");
144       assertEvent(VFileContentChangeEvent.class, file.getAbsolutePath());
145
146       myAccept = true;
147       FileUtil.delete(file);
148       assertEvent(VFileDeleteEvent.class, file.getAbsolutePath());
149
150       myAccept = true;
151       FileUtil.writeToFile(file, "re-creation");
152       assertEvent(VFileCreateEvent.class, file.getAbsolutePath());
153     }
154     finally {
155       unwatch(request);
156       delete(file);
157     }
158   }
159
160   public void testNonCanonicallyNamedFileRoot() throws Exception {
161     if (SystemInfo.isFileSystemCaseSensitive) {
162       System.err.println("Ignored: case-insensitive FS required");
163       return;
164     }
165
166     File file = createTestFile("test.txt");
167     refresh(file);
168
169     String watchRoot = file.getAbsolutePath().toUpperCase(Locale.US);
170     LocalFileSystem.WatchRequest request = watch(new File(watchRoot));
171     try {
172       myAccept = true;
173       FileUtil.writeToFile(file, "new content");
174       assertEvent(VFileContentChangeEvent.class, file.getAbsolutePath());
175
176       myAccept = true;
177       FileUtil.delete(file);
178       assertEvent(VFileDeleteEvent.class, file.getAbsolutePath());
179
180       myAccept = true;
181       FileUtil.writeToFile(file, "re-creation");
182       assertEvent(VFileCreateEvent.class, file.getAbsolutePath());
183     }
184     finally {
185       unwatch(request);
186       delete(file);
187     }
188   }
189
190   public void testDirectoryRecursive() throws Exception {
191     File topDir = createTestDir("top");
192     refresh(topDir);
193
194     LocalFileSystem.WatchRequest request = watch(topDir);
195     try {
196       myAccept = true;
197       File subDir = createTestDir(topDir, "sub");
198       assertEvent(VFileCreateEvent.class, subDir.getAbsolutePath());
199       refresh(subDir);
200
201       myAccept = true;
202       File file = createTestFile(subDir, "test.txt");
203       assertEvent(VFileCreateEvent.class, file.getAbsolutePath());
204
205       myAccept = true;
206       FileUtil.writeToFile(file, "new content");
207       assertEvent(VFileContentChangeEvent.class, file.getAbsolutePath());
208
209       myAccept = true;
210       FileUtil.delete(file);
211       assertEvent(VFileDeleteEvent.class, file.getAbsolutePath());
212
213       myAccept = true;
214       FileUtil.writeToFile(file, "re-creation");
215       assertEvent(VFileCreateEvent.class, file.getAbsolutePath());
216     }
217     finally {
218       unwatch(request);
219       delete(topDir);
220     }
221   }
222
223   public void testDirectoryFlat() throws Exception {
224     File topDir = createTestDir("top");
225     File watchedFile = createTestFile(topDir, "test.txt");
226     File subDir = createTestDir(topDir, "sub");
227     File unwatchedFile = createTestFile(subDir, "test.txt");
228     refresh(topDir);
229
230     LocalFileSystem.WatchRequest request = watch(topDir, false);
231     try {
232       myAccept = true;
233       FileUtil.writeToFile(watchedFile, "new content");
234       assertEvent(VFileContentChangeEvent.class, watchedFile.getAbsolutePath());
235
236       myTimeout = 10 * INTER_RESPONSE_DELAY;
237       myAccept = true;
238       FileUtil.writeToFile(unwatchedFile, "new content");
239       assertEvent(VFileEvent.class);
240       myTimeout = NATIVE_PROCESS_DELAY;
241     }
242     finally {
243       unwatch(request);
244       delete(topDir);
245     }
246   }
247
248   public void testDirectoryMixed() throws Exception {
249     File topDir = createTestDir("top");
250     File watchedFile1 = createTestFile(topDir, "test.txt");
251     File sub1Dir = createTestDir(topDir, "sub1");
252     File unwatchedFile = createTestFile(sub1Dir, "test.txt");
253     File sub2Dir = createTestDir(topDir, "sub2");
254     File sub2subDir = createTestDir(sub2Dir, "sub");
255     File watchedFile2 = createTestFile(sub2subDir, "test.txt");
256     refresh(topDir);
257
258     LocalFileSystem.WatchRequest topRequest = watch(topDir, false);
259     LocalFileSystem.WatchRequest subRequest = watch(sub2Dir);
260     try {
261       myAccept = true;
262       FileUtil.writeToFile(watchedFile1, "new content");
263       FileUtil.writeToFile(watchedFile2, "new content");
264       FileUtil.writeToFile(unwatchedFile, "new content");
265       assertEvent(VFileContentChangeEvent.class, watchedFile1.getAbsolutePath(), watchedFile2.getAbsolutePath());
266     }
267     finally {
268       unwatch(subRequest, topRequest);
269       delete(topDir);
270     }
271   }
272
273   public void testDirectoryNonExisting() throws Exception {
274     File topDir = createTestDir("top");
275     File subDir = new File(topDir, "subDir");
276     File file = new File(subDir, "file.txt");
277     refresh(topDir);
278
279     LocalFileSystem.WatchRequest request = watch(subDir);
280     try {
281       myAccept = true;
282       assertTrue(subDir.toString(), subDir.mkdir());
283       assertEvent(VFileCreateEvent.class, subDir.getAbsolutePath());
284       refresh(subDir);
285
286       myAccept = true;
287       FileUtil.writeToFile(file, "new content");
288       assertEvent(VFileCreateEvent.class, file.getAbsolutePath());
289     }
290     finally {
291       unwatch(request);
292       delete(topDir);
293     }
294   }
295
296   public void testDirectoryOverlapping() throws Exception {
297     File topDir = createTestDir("top");
298     File fileInTopDir = createTestFile(topDir, "file1.txt");
299     File subDir = createTestDir(topDir, "sub");
300     File fileInSubDir = createTestFile(subDir, "file2.txt");
301     File sideDir = createTestDir("side");
302     File fileInSideDir = createTestFile(sideDir, "file3.txt");
303     refresh(topDir);
304     refresh(sideDir);
305
306     LocalFileSystem.WatchRequest requestForSubDir = watch(subDir);
307     LocalFileSystem.WatchRequest requestForSideDir = watch(sideDir);
308     try {
309       myAccept = true;
310       FileUtil.writeToFile(fileInTopDir, "new content");
311       FileUtil.writeToFile(fileInSubDir, "new content");
312       FileUtil.writeToFile(fileInSideDir, "new content");
313       assertEvent(VFileContentChangeEvent.class, fileInSubDir.getAbsolutePath(), fileInSideDir.getAbsolutePath());
314
315       LocalFileSystem.WatchRequest requestForTopDir = watch(topDir);
316       try {
317         myAccept = true;
318         FileUtil.writeToFile(fileInTopDir, "newer content");
319         FileUtil.writeToFile(fileInSubDir, "newer content");
320         FileUtil.writeToFile(fileInSideDir, "newer content");
321         assertEvent(VFileContentChangeEvent.class, fileInTopDir.getAbsolutePath(), fileInSubDir.getAbsolutePath(), fileInSideDir.getAbsolutePath());
322       }
323       finally {
324         unwatch(requestForTopDir);
325       }
326
327       myAccept = true;
328       FileUtil.writeToFile(fileInTopDir, "newest content");
329       FileUtil.writeToFile(fileInSubDir, "newest content");
330       FileUtil.writeToFile(fileInSideDir, "newest content");
331       assertEvent(VFileContentChangeEvent.class, fileInSubDir.getAbsolutePath(), fileInSideDir.getAbsolutePath());
332
333       myAccept = true;
334       FileUtil.delete(fileInTopDir);
335       FileUtil.delete(fileInSubDir);
336       FileUtil.delete(fileInSideDir);
337       assertEvent(VFileDeleteEvent.class, fileInTopDir.getAbsolutePath(), fileInSubDir.getAbsolutePath(), fileInSideDir.getAbsolutePath());
338     }
339     finally {
340       unwatch(requestForSubDir, requestForSideDir);
341       delete(topDir);
342     }
343   }
344
345 /*
346   public void testSymlinkAboveWatchRoot() throws Exception {
347     final File topDir = FileUtil.createTempDirectory("top.", null);
348     final File topLink = IoTestUtil.createTempLink(topDir.getAbsolutePath(), "link");
349     final File subDir = FileUtil.createTempDirectory(topDir, "sub.", null);
350     final File file = FileUtil.createTempFile(subDir, "test.", ".txt");
351     final File fileLink = new File(new File(topLink, subDir.getName()), file.getName());
352     refresh(topDir);
353     refresh(topLink);
354
355     final LocalFileSystem.WatchRequest request = watch(topLink);
356     try {
357       myAccept = true;
358       FileUtil.writeToFile(file, "new content");
359       assertEvent(VFileContentChangeEvent.class, fileLink.getAbsolutePath());
360
361       myAccept = true;
362       FileUtil.delete(file);
363       assertEvent(VFileDeleteEvent.class, fileLink.getAbsolutePath());
364
365       myAccept = true;
366       FileUtil.writeToFile(file, "re-creation");
367       assertEvent(VFileCreateEvent.class, fileLink.getAbsolutePath());
368     }
369     finally {
370       myFileSystem.removeWatchedRoot(request);
371       delete(topLink);
372       delete(topDir);
373     }
374   }
375
376   public void testSymlinkBelowWatchRoot() throws Exception {
377     final File targetDir = FileUtil.createTempDirectory("top.", null);
378     final File file = FileUtil.createTempFile(targetDir, "test.", ".txt");
379     final File linkDir = FileUtil.createTempDirectory("link.", null);
380     final File link = new File(linkDir, "link");
381     IoTestUtil.createTempLink(targetDir.getAbsolutePath(), link.getAbsolutePath());
382     final File fileLink = new File(link, file.getName());
383     refresh(targetDir);
384     refresh(linkDir);
385
386     final LocalFileSystem.WatchRequest request = watch(linkDir);
387     try {
388       myAccept = true;
389       FileUtil.writeToFile(file, "new content");
390       assertEvent(VFileContentChangeEvent.class, fileLink.getAbsolutePath());
391
392       myAccept = true;
393       FileUtil.delete(file);
394       assertEvent(VFileDeleteEvent.class, fileLink.getAbsolutePath());
395
396       myAccept = true;
397       FileUtil.writeToFile(file, "re-creation");
398       assertEvent(VFileCreateEvent.class, fileLink.getAbsolutePath());
399     }
400     finally {
401       myFileSystem.removeWatchedRoot(request);
402       delete(linkDir);
403       delete(targetDir);
404     }
405   }
406 */
407
408   public void testSubst() throws Exception {
409     if (!SystemInfo.isWindows) {
410       System.err.println("Ignored: Windows required");
411       return;
412     }
413
414     File targetDir = createTestDir("top");
415     File subDir = createTestDir(targetDir, "sub");
416     File file = createTestFile(subDir, "test.txt");
417     File rootFile = createSubst(targetDir.getAbsolutePath());
418     VirtualDirectoryImpl.allowRootAccess(rootFile.getPath());
419     VirtualFile vfsRoot = myFileSystem.findFileByIoFile(rootFile);
420
421     try {
422       assertNotNull(rootFile.getPath(), vfsRoot);
423       File substDir = new File(rootFile, subDir.getName());
424       File substFile = new File(substDir, file.getName());
425       refresh(targetDir);
426       refresh(substDir);
427
428       LocalFileSystem.WatchRequest request = watch(substDir);
429       try {
430         myAccept = true;
431         FileUtil.writeToFile(file, "new content");
432         assertEvent(VFileContentChangeEvent.class, substFile.getAbsolutePath());
433
434         LocalFileSystem.WatchRequest request2 = watch(targetDir);
435         try {
436           myAccept = true;
437           FileUtil.delete(file);
438           assertEvent(VFileDeleteEvent.class, file.getAbsolutePath(), substFile.getAbsolutePath());
439         }
440         finally {
441           unwatch(request2);
442         }
443
444         myAccept = true;
445         FileUtil.writeToFile(file, "re-creation");
446         assertEvent(VFileCreateEvent.class, substFile.getAbsolutePath());
447       }
448       finally {
449         unwatch(request);
450       }
451     }
452     finally {
453       delete(targetDir);
454       IoTestUtil.deleteSubst(rootFile.getPath());
455       if (vfsRoot != null) {
456         ((NewVirtualFile)vfsRoot).markDirty();
457         myFileSystem.refresh(false);
458       }
459       VirtualDirectoryImpl.disallowRootAccess(rootFile.getPath());
460     }
461   }
462
463   public void testDirectoryRecreation() throws Exception {
464     File rootDir = createTestDir("root");
465     File topDir = createTestDir(rootDir, "top");
466     File file1 = createTestFile(topDir, "file1.txt", "abc");
467     File file2 = createTestFile(topDir, "file2.txt", "123");
468     refresh(topDir);
469
470     LocalFileSystem.WatchRequest request = watch(rootDir);
471     try {
472       myAccept = true;
473       assertTrue(FileUtil.delete(topDir));
474       assertTrue(topDir.mkdir());
475       TimeoutUtil.sleep(100);
476       assertTrue(file1.createNewFile());
477       assertTrue(file2.createNewFile());
478       assertEvent(VFileContentChangeEvent.class, file1.getPath(), file2.getPath());
479     }
480     finally {
481       unwatch(request);
482       delete(topDir);
483     }
484   }
485
486   public void testWatchRootRecreation() throws Exception {
487     File rootDir = createTestDir("root");
488     File file1 = createTestFile(rootDir, "file1.txt", "abc");
489     File file2 = createTestFile(rootDir, "file2.txt", "123");
490     refresh(rootDir);
491
492     LocalFileSystem.WatchRequest request = watch(rootDir);
493     try {
494       myAccept = true;
495       assertTrue(FileUtil.delete(rootDir));
496       assertTrue(rootDir.mkdir());
497       if (SystemInfo.isLinux) TimeoutUtil.sleep(1100);  // implementation specific
498       assertTrue(file1.createNewFile());
499       assertTrue(file2.createNewFile());
500       assertEvent(VFileContentChangeEvent.class, file1.getPath(), file2.getPath());
501     }
502     finally {
503       unwatch(request);
504       delete(rootDir);
505     }
506   }
507
508   public void testWatchRootRenameRemove() throws Exception {
509     File topDir = createTestDir("top");
510     File rootDir = createTestDir(topDir, "root");
511     File rootDir2 = new File(topDir, "_" + rootDir.getName());
512     refresh(topDir);
513
514     LocalFileSystem.WatchRequest request = watch(rootDir);
515     try {
516       myAccept = true;
517       assertTrue(rootDir.renameTo(rootDir2));
518       assertEvent(VFileEvent.class, rootDir.getPath(), rootDir2.getPath());
519
520       myAccept = true;
521       assertTrue(rootDir2.renameTo(rootDir));
522       assertEvent(VFileEvent.class, rootDir.getPath(), rootDir2.getPath());
523
524       myAccept = true;
525       assertTrue(FileUtil.delete(topDir));
526       assertEvent(VFileDeleteEvent.class, topDir.getPath());
527
528       // todo[r.sh] current VFS implementation loses watch root once it's removed; this probably should be fixed
529       myAccept = true;
530       assertTrue(rootDir.mkdirs());
531       assertEvent(VFileCreateEvent.class);
532     }
533     finally {
534       unwatch(request);
535       delete(topDir);
536     }
537   }
538
539   public void testSwitchingToFsRoot() throws Exception {
540     File topDir = createTestDir("top");
541     File rootDir = createTestDir(topDir, "root");
542     File file1 = createTestFile(topDir, "1.txt");
543     File file2 = createTestFile(rootDir, "2.txt");
544     refresh(topDir);
545
546     File fsRoot = new File(SystemInfo.isUnix ? "/" : topDir.getPath().substring(0, topDir.getPath().indexOf(File.separatorChar)) + "\\");
547     assertTrue("can't guess root of " + topDir, fsRoot.exists());
548
549     LocalFileSystem.WatchRequest request = watch(rootDir);
550     try {
551       myAccept = true;
552       FileUtil.writeToFile(file1, "abc");
553       FileUtil.writeToFile(file2, "abc");
554       assertEvent(VFileContentChangeEvent.class, file2.getPath());
555
556       LocalFileSystem.WatchRequest rootRequest = watch(fsRoot);
557       try {
558         myTimeout = 10 * INTER_RESPONSE_DELAY;
559         myAccept = true;
560         FileUtil.writeToFile(file1, "12345");
561         FileUtil.writeToFile(file2, "12345");
562         assertEvent(VFileContentChangeEvent.class, file1.getPath(), file2.getPath());
563         myTimeout = NATIVE_PROCESS_DELAY;
564       }
565       finally {
566         unwatch(rootRequest);
567       }
568
569       myAccept = true;
570       FileUtil.writeToFile(file1, "");
571       FileUtil.writeToFile(file2, "");
572       assertEvent(VFileContentChangeEvent.class, file2.getPath());
573     }
574     finally {
575       unwatch(request);
576     }
577
578     myTimeout = 10 * INTER_RESPONSE_DELAY;
579     myAccept = true;
580     FileUtil.writeToFile(file1, "xyz");
581     FileUtil.writeToFile(file2, "xyz");
582     assertEvent(VFileEvent.class);
583     myTimeout = NATIVE_PROCESS_DELAY;
584   }
585
586   public void testLineBreaksInName() throws Exception {
587     if (!SystemInfo.isUnix) {
588       System.err.println("Ignored: Unix required");
589       return;
590     }
591
592     File topDir = createTestDir("topDir");
593     File testDir = createTestDir(topDir, "weird\ndir\nname");
594     File testFile = createTestFile(testDir, "weird\nfile\nname");
595     refresh(topDir);
596
597     LocalFileSystem.WatchRequest request = watch(topDir);
598     try {
599       myAccept = true;
600       FileUtil.writeToFile(testFile, "abc");
601       assertEvent(VFileContentChangeEvent.class, testFile.getPath());
602     }
603     finally {
604       unwatch(request);
605     }
606   }
607
608
609   @NotNull
610   private LocalFileSystem.WatchRequest watch(File watchFile) {
611     return watch(watchFile, true);
612   }
613
614   @NotNull
615   private LocalFileSystem.WatchRequest watch(final File watchFile, final boolean recursive) {
616     final Ref<LocalFileSystem.WatchRequest> request = Ref.create();
617     getEvents("events to add watch " + watchFile, new Runnable() {
618       @Override
619       public void run() {
620         request.set(myFileSystem.addRootToWatch(watchFile.getAbsolutePath(), recursive));
621       }
622     });
623     assertFalse(request.isNull());
624     assertFalse(myWatcher.isSettingRoots());
625     return request.get();
626   }
627
628   private void unwatch(final LocalFileSystem.WatchRequest... requests) {
629     getEvents("events to stop watching", new Runnable() {
630       @Override
631       public void run() {
632         myFileSystem.removeWatchedRoots(Arrays.asList(requests));
633       }
634     });
635   }
636
637   private VirtualFile refresh(File file) {
638     VirtualFile vFile = myFileSystem.refreshAndFindFileByIoFile(file);
639     assertNotNull(file.toString(), vFile);
640     VfsUtilCore.visitChildrenRecursively(vFile, new VirtualFileVisitor() {
641       @Override
642       public boolean visitFile(@NotNull VirtualFile file) {
643         file.getChildren();
644         return true;
645       }
646     });
647     return vFile;
648   }
649
650   private void delete(File file) throws IOException {
651     VirtualFile vFile = myFileSystem.findFileByIoFile(file);
652     if (vFile != null) {
653       AccessToken token = ApplicationManager.getApplication().acquireWriteActionLock(getClass());
654       try {
655         vFile.delete(this);
656       }
657       finally {
658         token.finish();
659       }
660     }
661     if (file.exists()) {
662       FileUtil.delete(file);
663     }
664   }
665
666   private List<VFileEvent> getEvents(String msg, @Nullable Runnable action) {
667     LOG.debug("** waiting for " + msg);
668     myAccept = true;
669
670     if (action != null) {
671       action.run();
672     }
673
674     int timeout = myTimeout;
675     try {
676       synchronized (myWaiter) {
677         //noinspection WaitNotInLoop
678         myWaiter.wait(timeout);
679       }
680     }
681     catch (InterruptedException e) {
682       LOG.warn(e);
683     }
684
685     LOG.debug("** waited for " + timeout);
686     myFileSystem.refresh(false);
687
688     ArrayList<VFileEvent> result;
689     synchronized (myEvents) {
690       result = new ArrayList<VFileEvent>(myEvents);
691       myEvents.clear();
692     }
693     LOG.debug("** events: " + result.size());
694     return result;
695   }
696
697   private void assertEvent(Class<? extends VFileEvent> type, String... paths) {
698     List<VFileEvent> events = getEvents(type.getSimpleName(), null);
699     assertEquals(events.toString(), paths.length, events.size());
700
701     Set<String> pathSet = ContainerUtil.map2Set(paths, new Function<String, String>() {
702       @Override
703       public String fun(final String path) {
704         return FileUtil.toSystemIndependentName(path);
705       }
706     });
707
708     for (VFileEvent event : events) {
709       assertTrue(event.toString(), type.isInstance(event));
710       VirtualFile eventFile = event.getFile();
711       assertNotNull(event.toString(), eventFile);
712       assertTrue(eventFile + " not in " + Arrays.toString(paths), pathSet.remove(eventFile.getPath()));
713     }
714   }
715 }