1/*
2 * Copyright (C) 2016 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
17package libcore.java.nio.file;
18
19import com.sun.nio.file.ExtendedWatchEventModifier;
20
21import org.junit.After;
22import org.junit.Before;
23import org.junit.Rule;
24import org.junit.Test;
25
26import java.io.File;
27import java.io.IOException;
28import java.net.URI;
29import java.net.URISyntaxException;
30import java.nio.file.ClosedWatchServiceException;
31import java.nio.file.FileSystems;
32import java.nio.file.Files;
33import java.nio.file.LinkOption;
34import java.nio.file.NoSuchFileException;
35import java.nio.file.NotDirectoryException;
36import java.nio.file.Path;
37import java.nio.file.Paths;
38import java.nio.file.WatchEvent;
39import java.nio.file.WatchKey;
40import java.nio.file.WatchService;
41import java.util.ArrayList;
42import java.util.Iterator;
43import java.util.List;
44
45import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
46import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
47import static junit.framework.TestCase.assertFalse;
48import static junit.framework.TestCase.assertNull;
49import static junit.framework.TestCase.assertTrue;
50import static org.junit.Assert.assertEquals;
51import static org.junit.Assert.fail;
52
53public class LinuxPathTest {
54
55    @Rule
56    public FilesSetup filesSetup = new FilesSetup();
57
58    /**
59     * CTS doesn't allow creating files in the test directory, however, Vogar allows creation of
60     * new files in the test directory. Therefore, for the tests which don't require write
61     * permission, dummyPath would serve the purpose, however, for the others, {@link
62     * FilesSetup#getTestDirPath()} should be used.
63     */
64    private static final Path dummyPath = Paths.get("dummyPath");
65
66    @Test
67    public void test_getFileSystem() {
68        assertTrue(dummyPath.getFileSystem().provider() instanceof
69                sun.nio.fs.LinuxFileSystemProvider);
70    }
71
72    @Test
73    public void test_isAbsolute() {
74        assertFalse(dummyPath.isAbsolute());
75        Path absolutePath = dummyPath.toAbsolutePath();
76        assertTrue(absolutePath.isAbsolute());
77    }
78
79    @Test
80    public void test_getRoot() {
81        assertEquals(Paths.get("/"), dummyPath.toAbsolutePath().getRoot());
82        assertNull(dummyPath.getRoot());
83    }
84
85    @Test
86    public void test_getFileName() {
87        assertEquals(dummyPath, dummyPath.getFileName());
88        assertEquals(dummyPath, dummyPath.toAbsolutePath().getFileName());
89        assertNull(dummyPath.getRoot());
90        assertEquals(Paths.get("data"), Paths.get("/data").getFileName());
91        assertEquals(Paths.get("data"), Paths.get("/data/").getFileName());
92        assertEquals(Paths.get(".."), Paths.get("/data/dir1/..").getFileName());
93    }
94
95    @Test
96    public void test_getParent() {
97        assertNull(dummyPath.getParent());
98        assertEquals(Paths.get("rootDir"), Paths.get("rootDir/dir").getParent());
99    }
100
101    @Test
102    public void test_getNameCount() {
103        assertEquals(0, Paths.get("/").getNameCount());
104        assertEquals(1, Paths.get("/dir").getNameCount());
105        assertEquals(2, Paths.get("/dir/dir").getNameCount());
106        assertEquals(2, Paths.get("/dir/..").getNameCount());
107    }
108
109    @Test
110    public void test_getName() {
111        assertEquals(Paths.get("t"), Paths.get("/t/t1/t2/t3").getName(0));
112        assertEquals(Paths.get("t2"), Paths.get("/t/t1/t2/t3").getName(2));
113        assertEquals(Paths.get("t3"), Paths.get("/t/t1/t2/t3").getName(3));
114
115        // Without root.
116        assertEquals(Paths.get("t3"), Paths.get("t/t1/t2/t3").getName(3));
117
118        // Invalid index.
119        try {
120            Paths.get("/t/t1/t2/t3").getName(4);
121            fail();
122        } catch (IllegalArgumentException expected) {}
123
124        // Negative index value.
125        try {
126            Paths.get("/t/t1/t2/t3").getName(-1);
127            fail();
128        } catch (IllegalArgumentException expected) {}
129    }
130
131    @Test
132    public void test_subPath() {
133        assertEquals(Paths.get("t1/t2"), Paths.get("t1/t2/t3").subpath(0, 2));
134        assertEquals(Paths.get("t2"), Paths.get("t1/t2/t3").subpath(1, 2));
135
136        try {
137            Paths.get("t1/t2/t3").subpath(1, 1);
138            fail();
139        } catch (IllegalArgumentException expected) {}
140
141        try {
142            assertEquals(Paths.get("t1/t1"), Paths.get("t1/t2/t3").subpath(1, 0));
143            fail();
144        } catch (IllegalArgumentException expected) {}
145
146        try {
147            assertEquals(Paths.get("t1/t1"), Paths.get("t1/t2/t3").subpath(1, 5));
148            fail();
149        } catch (IllegalArgumentException expected) {}
150    }
151
152    @Test
153    public void test_startsWith$String() {
154        assertTrue(Paths.get("t1/t2").startsWith("t1"));
155        assertTrue(dummyPath.toAbsolutePath().startsWith("/"));
156        assertTrue(Paths.get("t1/t2/t3").startsWith("t1/t2"));
157        assertFalse(Paths.get("t1/t2").startsWith("t2"));
158    }
159
160    @Test(expected = NullPointerException.class)
161    public void test_startsWith$String_NPE() {
162        filesSetup.getTestPath().startsWith((String) null);
163    }
164
165    @Test
166    public void test_startsWith$Path() {
167        assertTrue(Paths.get("t1/t2").startsWith(Paths.get("t1")));
168        assertTrue(dummyPath.toAbsolutePath().startsWith(Paths.get("/")));
169        assertTrue(Paths.get("t1/t2/t3").startsWith(Paths.get("t1/t2")));
170        assertFalse(Paths.get("t1/t2").startsWith(Paths.get("t2")));
171    }
172
173    @Test(expected = NullPointerException.class)
174    public void test_startsWith$Path_NPE() {
175        filesSetup.getTestPath().startsWith((Path) null);
176    }
177
178    @Test
179    public void test_endsWith$Path() {
180        assertTrue(Paths.get("t1/t2").endsWith(Paths.get("t2")));
181        assertTrue(Paths.get("t1/t2/t3").endsWith(Paths.get("t2/t3")));
182        assertFalse(Paths.get("t1/t2").endsWith(Paths.get("t1")));
183        assertTrue(Paths.get("/").endsWith(Paths.get("/")));
184        assertFalse(Paths.get("/data/").endsWith(Paths.get("/")));
185    }
186
187    @Test(expected = NullPointerException.class)
188    public void test_endsWith$Path_NPE() {
189        filesSetup.getTestPath().endsWith((Path)null);
190    }
191
192    @Test
193    public void test_endsWith$String() {
194        assertTrue(Paths.get("t1/t2").endsWith("t2"));
195        assertTrue(Paths.get("t1/t2/t3").endsWith("t2/t3"));
196        assertFalse(Paths.get("t1/t2").endsWith("t1"));
197        assertTrue(Paths.get("/").endsWith("/"));
198        assertFalse(Paths.get("/data/").endsWith("/"));
199    }
200
201    @Test(expected = NullPointerException.class)
202    public void test_endsWith$String_NPE() {
203        filesSetup.getTestPath().endsWith((String)null);
204    }
205
206    @Test
207    public void test_normalize() {
208        assertEquals(Paths.get("t2/t3"), Paths.get("t1/../t2/t3").normalize());
209        assertEquals(Paths.get("../t2/t3"), Paths.get("t1/../../t2/t3").normalize());
210        assertEquals(Paths.get("t1/t2/t3"), Paths.get("t1/./t2/t3").normalize());
211        assertEquals(Paths.get("t1/t2/t3"), Paths.get("t1/././t2/t3").normalize());
212        assertEquals(Paths.get("t1/t2/t3"), Paths.get("t1/././t2/t3").normalize());
213        assertEquals(Paths.get("t1"), Paths.get("t1/"));
214    }
215
216    @Test
217    public void test_resolve$Path() {
218        Path p = Paths.get("p");
219        Path p1 = Paths.get("p1");
220        Path p1p = Paths.get("p1/p");
221        assertEquals(p1p, p1.resolve(p));
222        assertEquals(p.toAbsolutePath(), p1.resolve(p.toAbsolutePath()));
223        assertEquals(p1p.toAbsolutePath(), p1.toAbsolutePath().resolve(p));
224    }
225
226    @Test(expected = NullPointerException.class)
227    public void test_resolve$Path_NPE() {
228        dummyPath.resolve((Path)null);
229    }
230
231    @Test
232    public void test_resolve$String() {
233        Path p = Paths.get("p");
234        Path p1 = Paths.get("p1");
235        Path p1p = Paths.get("p1/p");
236        assertEquals(p1p, p1.resolve("p"));
237        assertEquals(p1p.toAbsolutePath(), p1.toAbsolutePath().resolve("p"));
238    }
239
240    @Test(expected = NullPointerException.class)
241    public void test_resolve$String_NPE() {
242        dummyPath.resolve((String)null);
243    }
244
245    @Test
246    public void test_resolveSibling$Path() {
247        Path c2 = Paths.get("c2");
248        Path parent_c1 = Paths.get("parent/c1");
249        Path parent_c2 = Paths.get("parent/c2");
250        assertEquals(parent_c2, parent_c1.resolveSibling(c2));
251        assertEquals(c2.toAbsolutePath(), parent_c1.resolveSibling(c2.toAbsolutePath()));
252        assertEquals(parent_c2.toAbsolutePath(), parent_c1.toAbsolutePath().resolveSibling(c2));
253    }
254
255    @Test(expected = NullPointerException.class)
256    public void test_resolveSibling$String_Path() {
257        dummyPath.resolveSibling((Path) null);
258    }
259
260    @Test
261    public void test_resolveSibling$String() {
262        Path c2 = Paths.get("c2");
263        Path parent_c1 = Paths.get("parent/c1");
264        Path parent_c2 = Paths.get("parent/c2");
265        assertEquals(parent_c2, parent_c1.resolveSibling(c2.toString()));
266        assertEquals(c2.toAbsolutePath(), parent_c1.resolveSibling(c2.toAbsolutePath().toString()));
267        assertEquals(parent_c2.toAbsolutePath(), parent_c1.toAbsolutePath()
268                .resolveSibling(c2.toString()));
269    }
270
271    @Test(expected = NullPointerException.class)
272    public void test_resolveSibling$String_NPE() {
273        dummyPath.resolveSibling((String)null);
274    }
275
276    @Test
277    public void test_relativize() {
278        Path p1 = Paths.get("t1/t2/t3");
279        Path p2 = Paths.get("t1/t2");
280        assertEquals(Paths.get(".."), p1.relativize(p2));
281        assertEquals(Paths.get(".."), p1.toAbsolutePath().relativize(p2.toAbsolutePath()));
282        assertEquals(Paths.get("t3"), p2.relativize(p1));
283
284        // Can't be relativized as either of the paths are relative and the other is not.
285        try {
286            p1.relativize(p2.toAbsolutePath());
287            fail();
288        } catch (IllegalArgumentException expected) {}
289
290        try {
291            p1.toAbsolutePath().relativize(p2);
292            fail();
293        } catch (IllegalArgumentException expected) {}
294    }
295
296    @Test(expected = NullPointerException.class)
297    public void test_relativize_NPE() {
298        dummyPath.relativize(null);
299    }
300
301    @Test
302    public void test_toURI() throws URISyntaxException {
303        assertEquals(new URI("file://" + dummyPath.toAbsolutePath().toString()), dummyPath.toUri());
304        assertEquals(new URI("file:///"), Paths.get("/").toUri());
305        assertEquals(new URI("file:///dir/.."), Paths.get(("/dir/..")).toUri());
306        assertEquals(new URI("file:///../"), Paths.get(("/..")).toUri());
307        assertEquals(new URI("file:///dir/.."), Paths.get(("/dir/..")).toUri());
308        assertEquals(new URI("file:///./"), Paths.get(("/.")).toUri());
309        assertEquals(new URI("file:///dir/."), Paths.get(("/dir/.")).toUri());
310        // For unicode characters.
311        assertEquals(new URI("file:///%E0%A4%B0%E0%A4%BE%E0%A4%B9."), Paths.get(("/राह.")).toUri());
312    }
313
314    @Test
315    public void test_toAbsolutePath() {
316        assertFalse(dummyPath.isAbsolute());
317        assertTrue(dummyPath.toAbsolutePath().isAbsolute());
318    }
319
320    @Test
321    public void test_toRealPath() throws IOException {
322        // When file doesn't exist.
323        try {
324            dummyPath.toRealPath();
325            fail();
326        } catch (NoSuchFileException expected) {}
327        Files.createFile(filesSetup.getTestPath());
328        Path realPath = filesSetup.getTestPath().toRealPath();
329        assertTrue(Files.isSameFile(filesSetup.getTestPath().toAbsolutePath(), realPath));
330        assertTrue(realPath.isAbsolute());
331        assertFalse(Files.isSymbolicLink(realPath));
332
333        Path dir = Paths.get(filesSetup.getTestDir(), "dir1/dir2");
334        Path file = Paths.get(filesSetup.getTestDir(), "dir1/dir2/../../file");
335        Files.createDirectories(dir);
336        Files.createFile(file);
337        realPath = file.toRealPath();
338        assertTrue(Files.isSameFile(file.toAbsolutePath(), realPath));
339        assertTrue(realPath.isAbsolute());
340        assertFalse(Files.isSymbolicLink(realPath));
341
342        // Sym links.
343        Path symLink = Paths.get(filesSetup.getTestDir(), "symlink");
344        Files.createSymbolicLink(symLink, filesSetup.getTestPath().toAbsolutePath());
345        realPath = symLink.toRealPath();
346        assertTrue(Files.isSameFile(symLink, realPath));
347        assertTrue(realPath.isAbsolute());
348        assertFalse(Files.isSymbolicLink(realPath));
349
350        realPath = symLink.toRealPath(LinkOption.NOFOLLOW_LINKS);
351        assertTrue(Files.isSameFile(symLink, realPath));
352        assertTrue(realPath.isAbsolute());
353        assertTrue(Files.isSymbolicLink(realPath));
354    }
355
356    @Test
357    public void test_toFile() {
358        File file = dummyPath.toFile();
359        assertEquals(dummyPath.toAbsolutePath().toString(), file.getAbsolutePath());
360    }
361
362    @Test
363    public void test_register$WatchService$WatchEvent_Kind() throws IOException,
364            InterruptedException {
365        WatchService watchService = FileSystems.getDefault().newWatchService();
366        WatchEvent.Kind<?>[] events = {ENTRY_CREATE, ENTRY_DELETE};
367        Path file = Paths.get(filesSetup.getTestDir(), "directory/file");
368        assertFalse(Files.exists(file));
369        Path directory = Paths.get(filesSetup.getTestDir(), "directory");
370        Files.createDirectories(directory);
371        WatchKey key = directory.register(watchService, events);
372
373        // Creating, modifying and deleting the file.
374        Files.createFile(file);
375        assertTrue(Files.exists(file));
376        // EVENT_MODIFY should not be logged.
377        Files.newOutputStream(file).write("hello".getBytes());
378        Files.delete(file);
379        assertFalse(Files.exists(file));
380
381        assertTrue(key.isValid());
382        assertEquals(directory, key.watchable());
383        List<WatchEvent<?>> eventList = new ArrayList<>();
384
385        // Wait for the events to be recorded by WatchService.
386        while(true) {
387            eventList.addAll(key.pollEvents());
388            if (eventList.size() == 2) break;
389            Thread.sleep(1000);
390        }
391        // Wait for the events to be recorded by watchService.
392        assertEquals(2, eventList.size());
393        assertEquals(ENTRY_CREATE, eventList.get(0).kind());
394        assertEquals(ENTRY_DELETE, eventList.get(1).kind());
395    }
396
397    @Test
398    public void test_register$WatchService$WatchEvent_Kind_NPE() throws IOException,
399            InterruptedException {
400        WatchService watchService = FileSystems.getDefault().newWatchService();
401        WatchEvent.Kind<?>[] events = {ENTRY_CREATE, ENTRY_DELETE};
402        Path directory = Paths.get(filesSetup.getTestDir(), "directory");
403        Files.createDirectories(directory);
404        try {
405            directory.register(null, events);
406            fail();
407        } catch (NullPointerException expected) {}
408
409        try {
410            directory.register(watchService, (WatchEvent.Kind<?>) null);
411            fail();
412        } catch (NullPointerException expected) {}
413    }
414
415    @Test
416    public void test_register$WatchService$WatchEvent_Kind_Exception() throws IOException {
417        WatchService watchService = FileSystems.getDefault().newWatchService();
418        Path directory = Paths.get(filesSetup.getTestDir(), "directory1");
419        Files.createFile(directory);
420
421        // When file is not a directory.
422        try {
423            directory.register(watchService, ENTRY_CREATE);
424            fail();
425        } catch (NotDirectoryException expected) {}
426
427        // When the events are not supported.
428        Files.deleteIfExists(directory);
429        Files.createDirectories(directory);
430        WatchEvent.Kind<?>[] events = {new NonStandardEvent<>()};
431        try {
432            directory.register(watchService, events);
433            fail();
434        } catch (UnsupportedOperationException expected) {}
435
436        // When the watch service is closed.
437        watchService.close();
438        try {
439            directory.register(watchService, ENTRY_CREATE);
440            fail();
441        } catch (ClosedWatchServiceException expected) {}
442    }
443
444    @Test
445    public void test_register$WatchService$WatchEvent_Kind_Exception_NPE() throws IOException {
446        WatchService watchService = FileSystems.getDefault().newWatchService();
447        Path directory = Paths.get(filesSetup.getTestDir(), "directory1");
448        Files.createDirectories(directory);
449
450        // When file is not a directory.
451        try {
452            directory.register(null, ENTRY_CREATE);
453            fail();
454        } catch (NullPointerException expected) {}
455
456        try {
457            directory.register(watchService, null);
458            fail();
459        } catch (NullPointerException expected) {}
460    }
461
462    @Test
463    public void test_register$WatchService$WatchEvent_Kind$WatchEvent_Modifier() throws IOException
464    {
465        WatchService watchService = FileSystems.getDefault().newWatchService();
466        WatchEvent.Kind<?>[] events = {ENTRY_CREATE};
467        Path dirRoot = Paths.get(filesSetup.getTestDir(), "dir");
468        Files.createDirectories(dirRoot);
469        try {
470            WatchKey key = dirRoot.register(watchService, events,
471                    ExtendedWatchEventModifier.FILE_TREE);
472            fail();
473        } catch (UnsupportedOperationException expected) {
474            assertTrue(expected.getMessage().contains("Modifier not supported"));
475        }
476    }
477
478    @Test
479    public void test_register$WatchService$WatchEvent_Kind$WatchEvent_Modifier_NPE()
480            throws IOException {
481        WatchService watchService = FileSystems.getDefault().newWatchService();
482        WatchEvent.Kind<?>[] events = {ENTRY_CREATE};
483        Path dirRoot = Paths.get(filesSetup.getTestDir(), "dir");
484        Files.createDirectories(dirRoot);
485        try {
486            WatchKey key = dirRoot.register(null, events,
487                    ExtendedWatchEventModifier.FILE_TREE);
488            fail();
489        } catch (NullPointerException expected) {}
490
491        try {
492            WatchKey key = dirRoot.register(watchService, null,
493                    ExtendedWatchEventModifier.FILE_TREE);
494            fail();
495        } catch (NullPointerException expected) {}
496    }
497
498    @Test
499    public void test_iterator() {
500        Path p = Paths.get("f1/f2/f3");
501        Iterator<Path> pathIterator = p.iterator();
502        assertEquals(Paths.get("f1"), pathIterator.next());
503        assertEquals(Paths.get("f2"), pathIterator.next());
504        assertEquals(Paths.get("f3"), pathIterator.next());
505        assertFalse(pathIterator.hasNext());
506    }
507
508    @Test
509    public void test_iterator_hasRoot() {
510        Path p = Paths.get("/f1/f2/f3");
511        Iterator<Path> pathIterator = p.iterator();
512        assertEquals(Paths.get("f1"), pathIterator.next());
513        assertEquals(Paths.get("f2"), pathIterator.next());
514        assertEquals(Paths.get("f3"), pathIterator.next());
515        assertFalse(pathIterator.hasNext());
516    }
517
518    @Test
519    public void test_compareTo() {
520        Path p1 = Paths.get("d/a");
521        Path p2 = Paths.get("d/b");
522        assertTrue(p1.compareTo(p2) < 0);
523        assertTrue(p2.compareTo(p1) > 0);
524        assertTrue(p1.compareTo(p1) == 0);
525    }
526
527    @Test(expected = NullPointerException.class)
528    public void test_compareTo_NPE() {
529        filesSetup.getTestPath().compareTo(null);
530    }
531
532    @Test
533    public void test_equals() {
534        Path p1 = Paths.get("a/b");
535        Path p2 = Paths.get("a/../a/b");
536        Path p3 = p1.toAbsolutePath();
537        assertFalse(p1.equals(p2));
538        assertTrue(p1.equals(p1));
539        assertFalse(p1.equals(p3));
540    }
541
542    @Test
543    public void test_equals_NPE() {
544        // Should not throw NPE.
545        filesSetup.getTestPath().equals(null);
546    }
547
548    @Test
549    public void test_hashCode() {
550        Path p1 = Paths.get("f1/f2/f3");
551        assertEquals(-642657684, p1.hashCode());
552
553        // With root component.
554        Path p2 = Paths.get("/f1/f2/f3");
555        assertEquals(306328475, p2.hashCode());
556    }
557
558    @Test
559    public void test_toString() {
560        Path p = Paths.get("f1/f2/f3");
561        assertEquals("f1/f2/f3", p.toString());
562
563        p = Paths.get("");
564        assertEquals("", p.toString());
565
566        p = Paths.get("..");
567        assertEquals("..", p.toString());
568
569        p = Paths.get(".");
570        assertEquals(".", p.toString());
571
572        p = Paths.get("dir/");
573        assertEquals("dir", p.toString());
574
575        p = Paths.get("/dir");
576        assertEquals("/dir", p.toString());
577    }
578
579    private static class NonStandardEvent<T> implements WatchEvent.Kind<T> {
580
581        @Override
582        public String name() {
583            return null;
584        }
585
586        @Override
587        public Class<T> type() {
588            return null;
589        }
590    }
591}