1/*
2 * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/*
25 * @test
26 * @summary Basic test of jrt file system provider
27 * @run testng Basic
28 */
29
30import java.io.InputStream;
31import java.io.IOException;
32import java.io.DataInputStream;
33import java.nio.file.DirectoryStream;
34import java.nio.file.InvalidPathException;
35import java.nio.file.Files;
36import java.nio.file.FileSystem;
37import java.nio.file.FileSystems;
38import java.nio.file.Path;
39import java.nio.file.PathMatcher;
40import java.nio.file.Paths;
41import java.net.URI;
42import java.util.Collections;
43import java.util.HashMap;
44import java.util.Iterator;
45import java.util.Map;
46import java.util.NoSuchElementException;
47import java.util.stream.Stream;
48
49import org.testng.annotations.AfterClass;
50import org.testng.annotations.BeforeClass;
51import org.testng.annotations.DataProvider;
52import org.testng.annotations.Test;
53
54import static org.testng.Assert.assertEquals;
55import static org.testng.Assert.assertNotEquals;
56import static org.testng.Assert.assertNotNull;
57import static org.testng.Assert.assertTrue;
58import static org.testng.Assert.assertFalse;
59
60/**
61 * Basic tests for jrt:/ file system provider.
62 */
63
64public class Basic {
65
66    private FileSystem theFileSystem;
67    private FileSystem fs;
68    private boolean isExplodedBuild = false;
69
70    @BeforeClass
71    public void setup() {
72        theFileSystem = FileSystems.getFileSystem(URI.create("jrt:/"));
73        Path modulesPath = Paths.get(System.getProperty("java.home"),
74                "lib", "modules");
75        isExplodedBuild = Files.notExists(modulesPath);
76        if (isExplodedBuild) {
77            System.out.printf("%s doesn't exist.", modulesPath.toString());
78            System.out.println();
79            System.out.println("It is most probably an exploded build."
80                    + " Skip non-default FileSystem testing.");
81            return;
82        }
83
84        Map<String, String> env = new HashMap<>();
85        // set java.home property to be underlying java.home
86        // so that jrt-fs.jar loading is exercised.
87        env.put("java.home", System.getProperty("java.home"));
88        try {
89            fs = FileSystems.newFileSystem(URI.create("jrt:/"), env);
90        } catch (IOException ioExp) {
91            throw new RuntimeException(ioExp);
92        }
93    }
94
95    @AfterClass
96    public void tearDown() {
97        try {
98            fs.close();
99        } catch (Exception ignored) {}
100    }
101
102    private FileSystem selectFileSystem(boolean theDefault) {
103        return theDefault? theFileSystem : fs;
104    }
105
106    // Checks that the given FileSystem is a jrt file system.
107    private void checkFileSystem(FileSystem fs) {
108        assertTrue(fs.provider().getScheme().equalsIgnoreCase("jrt"));
109        assertTrue(fs.isOpen());
110        assertTrue(fs.isReadOnly());
111        assertEquals(fs.getSeparator(), "/");
112
113        // one root
114        Iterator<Path> roots = fs.getRootDirectories().iterator();
115        assertTrue(roots.next().toString().equals("/"));
116        assertFalse(roots.hasNext());
117    }
118
119    @Test
120    public void testGetFileSystem() {
121        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
122        checkFileSystem(fs);
123
124        // getFileSystem should return the same object each time
125        assertTrue(fs == FileSystems.getFileSystem(URI.create("jrt:/")));
126    }
127
128    @Test(expectedExceptions = UnsupportedOperationException.class)
129    public void testCloseFileSystem() throws Exception {
130        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
131        fs.close(); // should throw UOE
132    }
133
134    @Test
135    public void testNewFileSystem() throws Exception {
136        FileSystem theFileSystem = FileSystems.getFileSystem(URI.create("jrt:/"));
137        Map<String, ?> env = Collections.emptyMap();
138        try (FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"), env)) {
139            checkFileSystem(fs);
140            assertTrue(fs != theFileSystem);
141        }
142    }
143
144    @Test
145    public void testNewFileSystemWithJavaHome() throws Exception {
146        if (isExplodedBuild) {
147            System.out.println("Skip testNewFileSystemWithJavaHome"
148                    + " since this is an exploded build");
149            return;
150        }
151
152        Map<String, String> env = new HashMap<>();
153        // set java.home property to be underlying java.home
154        // so that jrt-fs.jar loading is exercised.
155        env.put("java.home", System.getProperty("java.home"));
156        try (FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"), env)) {
157            checkFileSystem(fs);
158            // jrt-fs.jar classes are loaded by another (non-boot) loader in this case
159            assertNotNull(fs.provider().getClass().getClassLoader());
160        }
161    }
162
163    @DataProvider(name = "knownClassFiles")
164    private Object[][] knownClassFiles() {
165        return new Object[][] {
166            { "/modules/java.base/java/lang/Object.class", true },
167            { "modules/java.base/java/lang/Object.class", true },
168            { "/modules/java.base/java/lang/Object.class", false },
169            { "modules/java.base/java/lang/Object.class", false },
170        };
171    }
172
173    @Test(dataProvider = "knownClassFiles")
174    public void testKnownClassFiles(String path, boolean theDefault) throws Exception {
175        if (isExplodedBuild && !theDefault) {
176            System.out.println("Skip testKnownClassFiles with non-default FileSystem");
177            return;
178        }
179
180        FileSystem fs = selectFileSystem(theDefault);
181        Path classFile = fs.getPath(path);
182
183        assertTrue(Files.isRegularFile(classFile));
184        assertTrue(Files.size(classFile) > 0L);
185
186        // check magic number
187        try (InputStream in = Files.newInputStream(classFile)) {
188            int magic = new DataInputStream(in).readInt();
189            assertEquals(magic, 0xCAFEBABE);
190        }
191    }
192
193    @DataProvider(name = "knownDirectories")
194    private Object[][] knownDirectories() {
195        return new Object[][] {
196            { "/", true                     },
197            { "." , true                    },
198            { "./", true                    },
199            { "/.", true                    },
200            { "/./", true                   },
201            { "/modules/java.base/..", true         },
202            { "/modules/java.base/../", true        },
203            { "/modules/java.base/../.", true       },
204            { "/modules/java.base", true            },
205            { "/modules/java.base/java/lang", true  },
206            { "modules/java.base/java/lang", true   },
207            { "/modules/java.base/java/lang/", true },
208            { "modules/java.base/java/lang/", true  },
209            { "/", false                     },
210            { "." , false                    },
211            { "./", false                    },
212            { "/.", false                    },
213            { "/./", false                   },
214            { "/modules/java.base/..", false         },
215            { "/modules/java.base/../", false        },
216            { "/modules/java.base/../.", false       },
217            { "/modules/java.base", false            },
218            { "/modules/java.base/java/lang", false  },
219            { "modules/java.base/java/lang", false   },
220            { "/modules/java.base/java/lang/", false },
221            { "modules/java.base/java/lang/", false  },
222        };
223    }
224
225    @Test(dataProvider = "knownDirectories")
226    public void testKnownDirectories(String path, boolean theDefault) throws Exception {
227        if (isExplodedBuild && !theDefault) {
228            System.out.println("Skip testKnownDirectories with non-default FileSystem");
229            return;
230        }
231
232        FileSystem fs = selectFileSystem(theDefault);
233        Path dir = fs.getPath(path);
234
235        assertTrue(Files.isDirectory(dir));
236
237        // directory should not be empty
238        try (Stream<Path> stream = Files.list(dir)) {
239            assertTrue(stream.count() > 0L);
240        }
241        try (Stream<Path> stream = Files.walk(dir)) {
242            assertTrue(stream.count() > 0L);
243        }
244    }
245
246    @DataProvider(name = "topLevelPkgDirs")
247    private Object[][] topLevelPkgDirs() {
248        return new Object[][] {
249            { "/java/lang" },
250            { "java/lang"  },
251            { "/java/util" },
252            { "java/util"  },
253        };
254    }
255
256    @Test(dataProvider = "topLevelPkgDirs")
257    public void testNotExists(String path) throws Exception {
258        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
259        Path dir = fs.getPath(path);
260
261        // package directories should not be there at top level
262        assertTrue(Files.notExists(dir));
263    }
264
265    /**
266     * Test the URI of every file in the jrt file system
267     */
268    @Test
269    public void testToAndFromUri() throws Exception {
270        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
271        Path top = fs.getPath("/");
272        try (Stream<Path> stream = Files.walk(top)) {
273            stream.forEach(path -> {
274                URI u = path.toUri();
275                assertTrue(u.getScheme().equalsIgnoreCase("jrt"));
276                assertFalse(u.isOpaque());
277                assertTrue(u.getAuthority() == null);
278                assertEquals(u.getPath(), path.toAbsolutePath().toString());
279                Path p = Paths.get(u);
280                assertEquals(p, path);
281            });
282        }
283    }
284
285    @Test
286    public void testDirectoryNames() throws Exception {
287        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
288        Path top = fs.getPath("/");
289        // check that directory names do not have trailing '/' char
290        try (Stream<Path> stream = Files.walk(top)) {
291            stream.skip(1).filter(Files::isDirectory).forEach(path -> {
292                assertFalse(path.toString().endsWith("/"));
293            });
294        }
295    }
296
297    @DataProvider(name = "pathPrefixs")
298    private Object[][] pathPrefixes() {
299        return new Object[][] {
300            { "/"                       },
301            { "modules/java.base/java/lang"     },
302            { "./modules/java.base/java/lang"   },
303            { "/modules/java.base/java/lang"    },
304            { "/./modules/java.base/java/lang"  },
305            { "modules/java.base/java/lang/"    },
306            { "./modules/java.base/java/lang/"  },
307            { "/./modules/java.base/java/lang/" },
308        };
309    }
310
311    // @Test(dataProvider = "pathPrefixes")
312    public void testParentInDirList(String dir) throws Exception {
313        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
314        Path base = fs.getPath(dir);
315        try (DirectoryStream<Path> stream = Files.newDirectoryStream(base)) {
316            for (Path entry: stream) {
317                assertTrue( entry.getParent().equals(base),
318                    base.toString() + "-> " + entry.toString() );
319            }
320        }
321    }
322
323    @DataProvider(name = "dirStreamStringFilterData")
324    private Object[][] dirStreamStringFilterData() {
325        return new Object[][] {
326            { "/modules/java.base/java/lang", "/reflect"      },
327            { "/modules/java.base/java/lang", "/Object.class" },
328            { "/modules/java.base/java/util", "/stream"       },
329            { "/modules/java.base/java/util", "/List.class"   },
330        };
331    }
332
333    @Test(dataProvider = "dirStreamStringFilterData")
334    public void testDirectoryStreamStringFilter(String dir, String filter) throws Exception {
335        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
336        Path base = fs.getPath(dir);
337        try (DirectoryStream<Path> stream =
338                Files.newDirectoryStream(base, p->!p.toString().endsWith(filter))) {
339            for (Path entry: stream) {
340                assertFalse(entry.toString().contains(filter),
341                    "filtered path seen: " + filter);
342            }
343        }
344
345        // make sure without filter, we do see that matching entry!
346        boolean seen = false;
347        try (DirectoryStream<Path> stream = Files.newDirectoryStream(base)) {
348            for (Path entry: stream) {
349                if (entry.toString().endsWith(filter)) {
350                    seen = true;
351                    break;
352                }
353            }
354        }
355
356        assertTrue(seen, "even without filter " + filter + " is missing");
357    }
358
359    @DataProvider(name = "dirStreamFilterData")
360    private Object[][] dirStreamFilterData() {
361        return new Object[][] {
362            {
363              "/",
364              (DirectoryStream.Filter<Path>)(Files::isDirectory),
365              "isDirectory"
366            },
367            {
368              "/modules/java.base/java/lang",
369              (DirectoryStream.Filter<Path>)(Files::isRegularFile),
370              "isFile"
371            }
372        };
373    }
374
375    @Test(dataProvider = "dirStreamFilterData")
376    private void testDirectoryStreamFilter(String dir, DirectoryStream.Filter filter,
377            String name) throws Exception {
378        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
379        Path base = fs.getPath(dir);
380        try (DirectoryStream<Path> stream = Files.newDirectoryStream(base, filter)) {
381            for (Path entry: stream) {
382                assertTrue(filter.accept(entry), "filtered path seen: " + name);
383            }
384        }
385
386        // make sure without filter, we do see that matching entry!
387        boolean seen = false;
388        try (DirectoryStream<Path> stream = Files.newDirectoryStream(base)) {
389            for (Path entry: stream) {
390                if (filter.accept(entry)) {
391                    seen = true;
392                    break;
393                }
394            }
395        }
396
397        assertTrue(seen, "even without filter " + name + " is missing");
398    }
399
400    @Test
401    private void testDirectoryStreamIterator() throws Exception {
402        // run the tests with null filter (no filter)
403        dirStreamIteratorTest(null);
404        // run the same tests with trivial "accept all" filter
405        dirStreamIteratorTest(p->true);
406        // two other non-trivial ones
407        dirStreamIteratorTest(Files::isDirectory);
408        dirStreamIteratorTest(Files::isRegularFile);
409    }
410
411    private void dirStreamIteratorTest(DirectoryStream.Filter<Path> filter)
412            throws Exception {
413        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
414        // This test assumes at least there are two elements in "java/lang"
415        // package with any filter passed. don't change to different path here!
416        Path dir = fs.getPath("/modules/java.base/java/lang");
417        try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, filter)) {
418            Iterator<Path> itr = stream.iterator();
419            itr.hasNext();
420            Path path1 = itr.next();
421            // missing second hasNext call
422            Path path2 = itr.next();
423            assertNotEquals(path1, path2);
424        }
425
426        try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, filter)) {
427            Iterator<Path> itr = stream.iterator();
428            // no hasNext calls at all
429            Path path1 = itr.next();
430            Path path2 = itr.next();
431            assertNotEquals(path1, path2);
432        }
433
434        int numEntries = 0;
435        try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, filter)) {
436            Iterator<Path> itr = stream.iterator();
437            while (itr.hasNext()) {
438                numEntries++;
439                itr.next();
440            }
441
442            // reached EOF, next call should result in exception
443            try {
444                itr.next();
445                throw new AssertionError("should have thrown exception");
446            } catch (NoSuchElementException nsee) {
447                System.out.println("got NoSuchElementException as expected");
448            }
449        }
450
451        // redundant hasNext calls
452        try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, filter)) {
453            Iterator<Path> itr = stream.iterator();
454            // any number of hasNext should definitely stay at first element
455            for (int i = 0; i < 2*numEntries; i++) {
456                itr.hasNext();
457            }
458
459            for (int j = 0; j < numEntries; j++) {
460                itr.next();
461            }
462            // exactly count number of entries!
463            assertFalse(itr.hasNext());
464        }
465    }
466
467    @DataProvider(name = "hiddenPaths")
468    private Object[][] hiddenPaths() {
469        return new Object[][] {
470            { "/META-INF" },
471            { "/META-INF/services" },
472            { "/META-INF/services/java.nio.file.spi.FileSystemProvider" },
473            { "/modules/java.base/packages.offsets" },
474            { "/modules/java.instrument/packages.offsets" },
475            { "/modules/jdk.zipfs/packages.offsets" },
476            { "/modules/java.base/_the.java.base.vardeps" },
477            { "/modules/java.base/_the.java.base_batch" },
478            { "/java/lang" },
479            { "/java/util" },
480        };
481    }
482
483    @Test(dataProvider = "hiddenPaths")
484    public void testHiddenPathsNotExposed(String path) throws Exception {
485        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
486        assertTrue(Files.notExists(fs.getPath(path)), path + " should not exist");
487    }
488
489    @DataProvider(name = "pathGlobPatterns")
490    private Object[][] pathGlobPatterns() {
491        return new Object[][] {
492            { "/modules/*", "/modules/java.base", true },
493            { "/modules/*", "/modules/java.base/java", false },
494            { "/modules/j*", "/modules/java.base", true },
495            { "/modules/J*", "/modules/java.base", false },
496            { "**.class", "/modules/java.base/java/lang/Object.class", true },
497            { "**.java", "/modules/java.base/java/lang/Object.class", false },
498            { "**java/*", "/modules/java.base/java/lang", true },
499            { "**java/lang/ref*", "/modules/java.base/java/lang/reflect", true },
500            { "**java/lang/ref*", "/modules/java.base/java/lang/ref", true },
501            { "**java/lang/ref?", "/modules/java.base/java/lang/ref", false },
502            { "**java/lang/{ref,refl*}", "/modules/java.base/java/lang/ref", true },
503            { "**java/lang/{ref,refl*}", "/modules/java.base/java/lang/reflect", true },
504            { "**java/[a-u]?*/*.class", "/modules/java.base/java/util/Map.class", true },
505            { "**java/util/[a-z]*.class", "/modules/java.base/java/util/TreeMap.class", false },
506        };
507    }
508
509    @Test(dataProvider = "pathGlobPatterns")
510    public void testGlobPathMatcher(String pattern, String path,
511            boolean expectMatch) throws Exception {
512        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
513        PathMatcher pm = fs.getPathMatcher("glob:" + pattern);
514        Path p = fs.getPath(path);
515        assertTrue(Files.exists(p), path);
516        assertTrue(!(pm.matches(p) ^ expectMatch),
517            p + (expectMatch? " should match " : " should not match ") +
518            pattern);
519    }
520
521    @DataProvider(name = "pathRegexPatterns")
522    private Object[][] pathRegexPatterns() {
523        return new Object[][] {
524            { "/modules/.*", "/modules/java.base", true },
525            { "/modules/[^/]*", "/modules/java.base/java", false },
526            { "/modules/j.*", "/modules/java.base", true },
527            { "/modules/J.*", "/modules/java.base", false },
528            { ".*\\.class", "/modules/java.base/java/lang/Object.class", true },
529            { ".*\\.java", "/modules/java.base/java/lang/Object.class", false },
530            { ".*java/.*", "/modules/java.base/java/lang", true },
531            { ".*java/lang/ref.*", "/modules/java.base/java/lang/reflect", true },
532            { ".*java/lang/ref.*", "/modules/java.base/java/lang/ref", true },
533            { ".*/java/lang/ref.+", "/modules/java.base/java/lang/ref", false },
534            { ".*/java/lang/(ref|refl.*)", "/modules/java.base/java/lang/ref", true },
535            { ".*/java/lang/(ref|refl.*)", "/modules/java.base/java/lang/reflect", true },
536            { ".*/java/[a-u]?.*/.*\\.class", "/modules/java.base/java/util/Map.class", true },
537            { ".*/java/util/[a-z]*\\.class", "/modules/java.base/java/util/TreeMap.class", false },
538        };
539    }
540
541    @Test(dataProvider = "pathRegexPatterns")
542    public void testRegexPathMatcher(String pattern, String path,
543            boolean expectMatch) throws Exception {
544        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
545        PathMatcher pm = fs.getPathMatcher("regex:" + pattern);
546        Path p = fs.getPath(path);
547        assertTrue(Files.exists(p), path);
548        assertTrue(!(pm.matches(p) ^ expectMatch),
549            p + (expectMatch? " should match " : " should not match ") +
550            pattern);
551    }
552
553    @Test
554    public void testPackagesAndModules() throws Exception {
555        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
556        assertTrue(Files.isDirectory(fs.getPath("/packages")));
557        assertTrue(Files.isDirectory(fs.getPath("/modules")));
558    }
559
560    @DataProvider(name = "packagesSubDirs")
561    private Object[][] packagesSubDirs() {
562        return new Object[][] {
563            { "java.lang" },
564            { "java.util" },
565            { "java.nio"  },
566            { "jdk.nashorn.api.scripting" }
567        };
568    }
569
570    @Test(dataProvider = "packagesSubDirs")
571    public void testPackagesSubDirs(String pkg) throws Exception {
572        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
573        assertTrue(Files.isDirectory(fs.getPath("/packages/" + pkg)),
574            pkg + " missing");
575    }
576
577    @DataProvider(name = "packagesLinks")
578    private Object[][] packagesLinks() {
579        return new Object[][] {
580            { "/packages/java.lang/java.base" },
581            { "/packages/java.lang/java.instrument" },
582            { "/packages/java/java.base" },
583            { "/packages/java/java.instrument" },
584            { "/packages/java/java.rmi"  },
585            { "/packages/java/java.sql"  },
586            { "/packages/javax/java.base"  },
587            { "/packages/javax/java.sql"  },
588            { "/packages/javax/java.xml"  },
589            { "/packages/javax/java.management"  },
590            { "/packages/java.util/java.base" },
591            { "/packages/jdk.nashorn.api.scripting/jdk.scripting.nashorn" },
592        };
593    }
594
595    @Test(dataProvider = "packagesLinks")
596    public void testPackagesLinks(String link) throws Exception {
597        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
598        Path path = fs.getPath(link);
599        assertTrue(Files.exists(path), link + " missing");
600        assertTrue(Files.isSymbolicLink(path), path + " is not a link");
601        path = Files.readSymbolicLink(path);
602        assertEquals(path.toString(), "/modules" + link.substring(link.lastIndexOf("/")));
603    }
604
605    @DataProvider(name = "modulesSubDirs")
606    private Object[][] modulesSubDirs() {
607        return new Object[][] {
608            { "java.base" },
609            { "java.sql" },
610            { "jdk.scripting.nashorn" },
611        };
612    }
613
614    @Test(dataProvider = "modulesSubDirs")
615    public void testModulesSubDirs(String module) throws Exception {
616        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
617        Path path = fs.getPath("/modules/" + module);
618        assertTrue(Files.isDirectory(path), module + " missing");
619        assertTrue(!Files.isSymbolicLink(path), path + " is a link");
620    }
621
622    @DataProvider(name="linkChases")
623    private Object[][] linkChases() {
624        return new Object[][] {
625            { "/modules/java.base/java/lang" },
626            { "/modules/java.base/java/util/Vector.class" },
627            { "/modules/jdk.scripting.nashorn/jdk/nashorn" },
628            { "/packages/java.lang/java.base/java/lang" },
629            { "/packages/java.util/java.base/java/util/Vector.class" },
630        };
631    }
632
633    @Test(dataProvider = "linkChases")
634    public void testLinkChases(String link) throws Exception {
635        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
636        Path path = fs.getPath(link);
637        assertTrue(Files.exists(path), link);
638    }
639
640    @Test
641    public void testSymlinkDirList() throws Exception {
642        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
643        Path path = fs.getPath("/packages/java.lang/java.base");
644        assertTrue(Files.isSymbolicLink(path));
645        assertTrue(Files.isDirectory(path));
646
647        boolean javaSeen = false, javaxSeen = false;
648        try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
649            for (Path p : stream) {
650                String str = p.toString();
651                if (str.endsWith("/java")) {
652                    javaSeen = true;
653                } else if (str.endsWith("javax")) {
654                    javaxSeen = true;
655                }
656            }
657        }
658        assertTrue(javaSeen);
659        assertTrue(javaxSeen);
660    }
661
662    @Test
663    public void invalidPathTest() {
664        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
665        InvalidPathException ipe = null;
666        try {
667            boolean res = Files.exists(fs.getPath("/packages/\ud834\udd7b"));
668            assertFalse(res);
669            return;
670        } catch (InvalidPathException e) {
671            ipe = e;
672        }
673        assertTrue(ipe != null);
674    }
675
676    @DataProvider(name="packagesLinkedDirs")
677    private Object[][] packagesLinkedDirs() {
678        return new Object[][] {
679            { "/packages/java.lang/java.base/java/lang/ref"             },
680            { "/./packages/java.lang/java.base/java/lang/ref"           },
681            { "packages/java.lang/java.base/java/lang/ref"              },
682            { "/packages/../packages/java.lang/java.base/java/lang/ref" },
683            { "/packages/java.lang/java.base/java/util/zip"             },
684            { "/./packages/java.lang/java.base/java/util/zip"           },
685            { "packages/java.lang/java.base/java/util/zip"              },
686            { "/packages/../packages/java.lang/java.base/java/util/zip" },
687            { "/packages/com.oracle/java.xml.ws/com"                    },
688            { "/./packages/com.oracle/java.xml.ws/com"                  },
689            { "packages/com.oracle/java.xml.ws/com"                     },
690            { "/packages/../packages/com.oracle/java.xml.ws/com"        }
691        };
692    }
693
694    // @bug 8141521: jrt file system's DirectoryStream reports child paths
695    // with wrong paths for directories under /packages
696    @Test(dataProvider = "packagesLinkedDirs")
697    public void dirStreamPackagesDirTest(String dirName) throws IOException {
698        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
699        Path path = fs.getPath(dirName);
700
701        int childCount = 0, dirPrefixOkayCount = 0;
702        try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(path)) {
703            for (Path child : dirStream) {
704                childCount++;
705                if (child.toString().startsWith(dirName)) {
706                    dirPrefixOkayCount++;
707                }
708            }
709        }
710
711        assertTrue(childCount != 0);
712        assertEquals(dirPrefixOkayCount, childCount);
713    }
714
715    @Test
716    public void objectClassSizeTest() throws Exception {
717        String path = "/modules/java.base/java/lang/Object.class";
718        FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
719        Path classFile = fs.getPath(path);
720
721        assertTrue(Files.size(classFile) > 0L);
722    }
723}
724
725