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