Home | History | Annotate | Download | only in file
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      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 libcore.java.nio.file;
     17 
     18 import org.junit.Test;
     19 import org.junit.runner.RunWith;
     20 import org.junit.runners.JUnit4;
     21 import org.junit.Rule;
     22 
     23 import java.nio.file.FileSystems;
     24 import java.nio.file.Files;
     25 import java.nio.file.Path;
     26 import java.nio.file.Paths;
     27 import java.nio.file.WatchEvent;
     28 import java.nio.file.WatchKey;
     29 import java.nio.file.WatchService;
     30 import java.util.ArrayList;
     31 import java.util.Arrays;
     32 import java.util.HashMap;
     33 import java.util.Iterator;
     34 import java.util.List;
     35 import java.util.Map;
     36 import java.util.concurrent.TimeUnit;
     37 
     38 import static junit.framework.TestCase.assertEquals;
     39 import static junit.framework.TestCase.assertFalse;
     40 import static junit.framework.TestCase.assertNotNull;
     41 import static junit.framework.TestCase.assertNull;
     42 import static junit.framework.TestCase.assertTrue;
     43 import static junit.framework.TestCase.fail;
     44 
     45 import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
     46 import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
     47 import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
     48 
     49 @RunWith(JUnit4.class)
     50 public class WatchServiceTest {
     51     private static final WatchEvent.Kind<?>[] ALL_EVENTS_KINDS =
     52         {ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY};
     53 
     54     @Rule
     55     public final FilesSetup filesSetup = new FilesSetup();
     56 
     57     static class WatchEventResult {
     58         final WatchEvent.Kind<Path> expectedKind;
     59         final int expectedCount;
     60         final boolean testCount;
     61 
     62         public WatchEventResult(WatchEvent.Kind<Path> expectedKind) {
     63             this.expectedKind = expectedKind;
     64             this.expectedCount = 0;
     65             this.testCount = false;
     66         }
     67 
     68         public WatchEventResult(WatchEvent.Kind<Path> expectedKind,
     69                                 int expectedCount) {
     70             this.expectedKind = expectedKind;
     71             this.expectedCount = expectedCount;
     72             this.testCount = true;
     73         }
     74     }
     75 
     76     private static void checkWatchServiceEventMultipleKeys(WatchService watchService,
     77             Map<WatchKey, List<WatchEventResult>> expectedResults,
     78             boolean expectedResetResult) throws InterruptedException {
     79 
     80         // Make a deep copy
     81         HashMap<WatchKey, ArrayList<WatchEventResult>> expectedResultsCopy
     82                 = new HashMap<>();
     83         for (Map.Entry<WatchKey, List<WatchEventResult>> entry : expectedResults.entrySet()) {
     84             expectedResultsCopy.put(entry.getKey(), new ArrayList<>(entry.getValue()));
     85         }
     86 
     87         while (!expectedResultsCopy.isEmpty()) {
     88             WatchKey watchKey = watchService.poll(2, TimeUnit.SECONDS);
     89             assertNotNull(watchKey);
     90 
     91             List<WatchEventResult> expectedEvents = expectedResultsCopy.get(watchKey);
     92             assertNotNull(expectedEvents);
     93 
     94             Iterator<WatchEventResult> expectedEventsIterator = expectedEvents.iterator();
     95             for (WatchEvent<?> event : watchKey.pollEvents()) {
     96                 WatchEventResult expectedEventResult = expectedEventsIterator.next();
     97                 assertNotNull(expectedEventResult);
     98 
     99                 assertEquals(expectedEventResult.expectedKind, event.kind());
    100                 if (expectedEventResult.testCount) {
    101                     assertEquals(expectedEventResult.expectedCount, event.count());
    102                 }
    103                 expectedEventsIterator.remove();
    104             }
    105             assertEquals(expectedResetResult, watchKey.reset());
    106             if (!expectedEventsIterator.hasNext()) {
    107                 expectedResultsCopy.remove(watchKey);
    108             }
    109         }
    110     }
    111 
    112     private static void checkWatchServiceEvent(WatchService watchService,
    113             WatchKey expectedWatchKey,
    114             List<WatchEventResult> expectedEvents,
    115             boolean expectedResetResult) throws InterruptedException {
    116         Map<WatchKey, List<WatchEventResult>> expected = new HashMap<>();
    117         expected.put(expectedWatchKey, expectedEvents);
    118         checkWatchServiceEventMultipleKeys(watchService, expected, expectedResetResult);
    119     }
    120 
    121     @Test
    122     public void test_singleFile() throws Exception {
    123         WatchService watchService = FileSystems.getDefault().newWatchService();
    124         Path file = Paths.get(filesSetup.getTestDir(), "directory/file");
    125         Path directory = Paths.get(filesSetup.getTestDir(), "directory");
    126         assertFalse(Files.exists(file));
    127         Files.createDirectories(directory);
    128         WatchKey directoryKey1 = directory.register(watchService, ALL_EVENTS_KINDS);
    129 
    130         // emit EVENT_CREATE
    131         Files.createFile(file);
    132         checkWatchServiceEvent(watchService, directoryKey1,
    133             Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1)), true);
    134         assertNull(watchService.poll());
    135 
    136         // emit EVENT_MODIFY
    137         Files.write(file, "hello1".getBytes());
    138         checkWatchServiceEvent(watchService, directoryKey1,
    139             Arrays.asList(new WatchEventResult(ENTRY_MODIFY)), true);
    140 
    141         // http:///b/35346596
    142         // Sometimes we receive a second, latent EVENT_MODIFY that happens shortly
    143         // after the first one. This will intercept it and make sure it won't
    144         // mess with ENTRY_DELETE later.
    145         Thread.sleep(500);
    146         WatchKey doubleModifyKey = watchService.poll();
    147         if (doubleModifyKey != null) {
    148             List<WatchEvent<?>> event = doubleModifyKey.pollEvents();
    149             assertEquals(ENTRY_MODIFY, event.get(0).kind());
    150             doubleModifyKey.reset();
    151         }
    152         assertNull(watchService.poll());
    153 
    154         // emit EVENT_DELETE
    155         Files.delete(file);
    156         checkWatchServiceEvent(watchService, directoryKey1,
    157             Arrays.asList(new WatchEventResult(ENTRY_DELETE, 1)), true);
    158 
    159         // Assert no more events
    160         assertNull(watchService.poll());
    161         watchService.close();
    162     }
    163 
    164     @Test
    165     public void test_EventMask() throws Exception {
    166         WatchService watchService = FileSystems.getDefault().newWatchService();
    167         WatchEvent.Kind<?>[] events = {ENTRY_DELETE};
    168         Path file = Paths.get(filesSetup.getTestDir(), "directory/file");
    169         Path directory = Paths.get(filesSetup.getTestDir(), "directory");
    170         assertFalse(Files.exists(file));
    171         Files.createDirectories(directory);
    172         WatchKey directoryKey1 = directory.register(watchService, events);
    173 
    174         // emit EVENT_CREATE
    175         Files.createFile(file);
    176         // emit EVENT_MODIFY (masked)
    177         Files.write(file, "hello1".getBytes());
    178         // emit EVENT_DELETE (masked)
    179         Files.delete(file);
    180 
    181         checkWatchServiceEvent(watchService, directoryKey1,
    182                 Arrays.asList(new WatchEventResult(ENTRY_DELETE, 1)), true);
    183         assertNull(watchService.poll());
    184         watchService.close();
    185     }
    186 
    187     @Test
    188     public void test_singleDirectory() throws Exception {
    189         WatchService watchService = FileSystems.getDefault().newWatchService();
    190         Path dirInDir = Paths.get(filesSetup.getTestDir(), "directory/dir");
    191         Path directory = Paths.get(filesSetup.getTestDir(), "directory");
    192         assertFalse(Files.exists(dirInDir));
    193         Files.createDirectories(directory);
    194         WatchKey directoryKey1 = directory.register(watchService, ALL_EVENTS_KINDS);
    195 
    196         // emit EVENT_CREATE
    197         Files.createDirectories(dirInDir);
    198 
    199         // Shouldn't emit EVENT_MODIFY
    200         Path dirInDirInDir = Paths.get(filesSetup.getTestDir(), "directory/dir/dir");
    201         Files.createDirectories(dirInDirInDir);
    202         Files.delete(dirInDirInDir);
    203 
    204         // emit EVENT_DELETE
    205         Files.delete(dirInDir);
    206 
    207         checkWatchServiceEvent(watchService, directoryKey1,
    208                 Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1),
    209                               new WatchEventResult(ENTRY_DELETE, 1)), true);
    210         assertNull(watchService.poll());
    211         watchService.close();
    212 
    213         watchService.close();
    214     }
    215 
    216     @Test
    217     public void test_cancel() throws Exception {
    218         WatchService watchService = FileSystems.getDefault().newWatchService();
    219         Path file = Paths.get(filesSetup.getTestDir(), "directory/file");
    220         Path directory = Paths.get(filesSetup.getTestDir(), "directory");
    221         assertFalse(Files.exists(file));
    222         Files.createDirectories(directory);
    223         WatchKey directoryKey1 = directory.register(watchService, ALL_EVENTS_KINDS);
    224 
    225         // emit EVENT_CREATE
    226         Files.createFile(file);
    227 
    228         // Canceling the key may prevent the EVENT_CREATE from being picked-up...
    229         // TODO: Fix this (b/35190858).
    230         Thread.sleep(500);
    231 
    232         // Cancel the key
    233         directoryKey1.cancel();
    234         assertFalse(directoryKey1.isValid());
    235 
    236         // Shouldn't emit EVENT_MODIFY and EVENT_DELETE
    237         Files.write(file, "hello1".getBytes());
    238         Files.delete(file);
    239 
    240         checkWatchServiceEvent(watchService, directoryKey1,
    241                 Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1)), false);
    242         assertNull(watchService.poll());
    243         watchService.close();
    244     }
    245 
    246     @Test
    247     public void test_removeTarget() throws Exception {
    248         WatchService watchService = FileSystems.getDefault().newWatchService();
    249         Path file = Paths.get(filesSetup.getTestDir(), "directory/file");
    250         Path directory = Paths.get(filesSetup.getTestDir(), "directory");
    251         assertFalse(Files.exists(file));
    252         Files.createDirectories(directory);
    253         WatchKey directoryKey1 = directory.register(watchService, ALL_EVENTS_KINDS);
    254 
    255         // emit EVENT_CREATE x1
    256         Files.createFile(file);
    257         Files.delete(file);
    258 
    259         // Delete underlying target.
    260         assertTrue(directoryKey1.isValid());
    261         Files.delete(directory);
    262 
    263         // We need to give some time to watch service thread to catch up with the
    264         // deletion
    265         while (directoryKey1.isValid()) {
    266             Thread.sleep(500);
    267         }
    268 
    269         checkWatchServiceEvent(watchService, directoryKey1,
    270                 Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1),
    271                               new WatchEventResult(ENTRY_DELETE, 1)), false);
    272         assertNull(watchService.poll());
    273 
    274         watchService.close();
    275     }
    276 
    277     @Test
    278     public void test_multipleKeys() throws Exception {
    279         WatchService watchService1 = FileSystems.getDefault().newWatchService();
    280 
    281         Path directory1 = Paths.get(filesSetup.getTestDir(), "directory1");
    282         Path directory2 = Paths.get(filesSetup.getTestDir(), "directory2");
    283 
    284         Path dir1file1 = Paths.get(filesSetup.getTestDir(), "directory1/file1");
    285         assertFalse(Files.exists(dir1file1));
    286         Path dir2file1 = Paths.get(filesSetup.getTestDir(), "directory2/file1");
    287         assertFalse(Files.exists(dir2file1));
    288 
    289         Files.createDirectories(directory1);
    290         Files.createDirectories(directory2);
    291         WatchKey directoryKey1 = directory1.register(watchService1, ALL_EVENTS_KINDS);
    292         WatchKey directoryKey2 = directory2.register(watchService1, ALL_EVENTS_KINDS);
    293 
    294         // emit EVENT_CREATE/DELETE for all
    295         Path[] allFiles = new Path[]{dir1file1, dir2file1};
    296         for (Path path : allFiles) {
    297             Files.createFile(path);
    298             Files.delete(path);
    299         }
    300 
    301         Map<WatchKey, List<WatchEventResult>> expected = new HashMap<>();
    302         expected.put(directoryKey1,
    303                 Arrays.asList(
    304                         new WatchEventResult(ENTRY_CREATE, 1),
    305                         new WatchEventResult(ENTRY_DELETE, 1)));
    306         expected.put(directoryKey2,
    307                 Arrays.asList(
    308                         new WatchEventResult(ENTRY_CREATE, 1),
    309                         new WatchEventResult(ENTRY_DELETE, 1)));
    310 
    311         checkWatchServiceEventMultipleKeys(watchService1, expected, true);
    312         assertNull(watchService1.poll());
    313         watchService1.close();
    314     }
    315 
    316     @Test
    317     public void test_multipleServices() throws Exception {
    318         WatchService watchService1 = FileSystems.getDefault().newWatchService();
    319         WatchService watchService2 = FileSystems.getDefault().newWatchService();
    320 
    321         Path directory1 = Paths.get(filesSetup.getTestDir(), "directory1");
    322         Path directory2 = Paths.get(filesSetup.getTestDir(), "directory2");
    323 
    324         Path dir1file1 = Paths.get(filesSetup.getTestDir(), "directory1/file1");
    325         assertFalse(Files.exists(dir1file1));
    326         Path dir2file1 = Paths.get(filesSetup.getTestDir(), "directory2/file1");
    327         assertFalse(Files.exists(dir2file1));
    328 
    329         Files.createDirectories(directory1);
    330         Files.createDirectories(directory2);
    331 
    332         // 2 services listening for distinct directories
    333         WatchKey directoryKey1 = directory1.register(watchService1, ALL_EVENTS_KINDS);
    334         WatchKey directoryKey2 = directory2.register(watchService2, ALL_EVENTS_KINDS);
    335         // emit EVENT_CREATE/DELETE for all
    336         Path[] allFiles = new Path[]{dir1file1, dir2file1};
    337         for (Path path : allFiles) {
    338             Files.createFile(path);
    339             Files.delete(path);
    340         }
    341 
    342         checkWatchServiceEvent(watchService1, directoryKey1,
    343                                 Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1),
    344                                               new WatchEventResult(ENTRY_DELETE, 1)), true);
    345         checkWatchServiceEvent(watchService2, directoryKey2,
    346                                 Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1),
    347                                               new WatchEventResult(ENTRY_DELETE, 1)), true);
    348 
    349         // 2 services listening for a same directory
    350         WatchKey directoryKey3 = directory1.register(watchService2, ALL_EVENTS_KINDS);
    351         {
    352             Files.createFile(dir1file1);
    353             Files.delete(dir1file1);
    354         }
    355         checkWatchServiceEvent(watchService1, directoryKey1,
    356                                 Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1),
    357                                               new WatchEventResult(ENTRY_DELETE, 1)), true);
    358         checkWatchServiceEvent(watchService2, directoryKey3,
    359                                 Arrays.asList(new WatchEventResult(ENTRY_CREATE, 1),
    360                                               new WatchEventResult(ENTRY_DELETE, 1)), true);
    361 
    362 
    363 
    364         assertNull(watchService1.poll());
    365         watchService1.close();
    366         assertNull(watchService2.poll());
    367         watchService2.close();
    368     }
    369 }
    370