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.
  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;
  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;
  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();
  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
  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 
 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     }
 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 
 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")
 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         },
 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;
 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
 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/" },
 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")
 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             }
 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)) {
 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
 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);
 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