1 /*
   2  * Copyright (c) 2014, 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.DataInputStream;
  32 import java.nio.file.DirectoryStream;
  33 import java.nio.file.InvalidPathException;
  34 import java.nio.file.Files;
  35 import java.nio.file.FileSystem;
  36 import java.nio.file.FileSystems;
  37 import java.nio.file.Path;
  38 import java.nio.file.PathMatcher;
  39 import java.nio.file.Paths;
  40 import java.net.URI;
  41 import java.util.Collections;
  42 import java.util.Iterator;
  43 import java.util.Map;
  44 import java.util.NoSuchElementException;
  45 import java.util.stream.Stream;
  46 
  47 import org.testng.annotations.DataProvider;
  48 import org.testng.annotations.Test;
  49 
  50 import static org.testng.Assert.assertEquals;
  51 import static org.testng.Assert.assertNotEquals;
  52 import static org.testng.Assert.assertTrue;
  53 import static org.testng.Assert.assertFalse;
  54 
  55 /**
  56  * Basic tests for jrt:/ file system provider.
  57  */
  58 
  59 public class Basic {
  60 
  61     // Checks that the given FileSystem is a jrt file system.
  62     private void checkFileSystem(FileSystem fs) {
  63         assertTrue(fs.provider().getScheme().equalsIgnoreCase("jrt"));
  64         assertTrue(fs.isOpen());
  65         assertTrue(fs.isReadOnly());
  66         assertEquals(fs.getSeparator(), "/");
  67 
  68         // one root
  69         Iterator<Path> roots = fs.getRootDirectories().iterator();
  70         assertTrue(roots.next().toString().equals("/"));
  71         assertFalse(roots.hasNext());
  72     }
  73 
  74     @Test
  75     public void testGetFileSystem() {
  76         FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
  77         checkFileSystem(fs);
  78 
  79         // getFileSystem should return the same object each time
  80         assertTrue(fs == FileSystems.getFileSystem(URI.create("jrt:/")));
  81     }
  82 
  83     @Test(expectedExceptions = UnsupportedOperationException.class)
  84     public void testCloseFileSystem() throws Exception {
  85         FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
  86         fs.close(); // should throw UOE
  87     }
  88 
  89     @Test
  90     public void testNewFileSystem() throws Exception {
  91         FileSystem theFileSystem = FileSystems.getFileSystem(URI.create("jrt:/"));
  92         Map<String, ?> env = Collections.emptyMap();
  93         try (FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"), env)) {
  94             checkFileSystem(fs);
  95             assertTrue(fs != theFileSystem);
  96         }
  97     }
  98 
  99     @DataProvider(name = "knownClassFiles")
 100     private Object[][] knownClassFiles() {
 101         return new Object[][] {
 102             { "/modules/java.base/java/lang/Object.class" },
 103             { "modules/java.base/java/lang/Object.class" },
 104         };
 105     }
 106 
 107     @Test(dataProvider = "knownClassFiles")
 108     public void testKnownClassFiles(String path) throws Exception {
 109         FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
 110         Path classFile = fs.getPath(path);
 111 
 112         assertTrue(Files.isRegularFile(classFile));
 113         assertTrue(Files.size(classFile) > 0L);
 114 
 115         // check magic number
 116         try (InputStream in = Files.newInputStream(classFile)) {
 117             int magic = new DataInputStream(in).readInt();
 118             assertEquals(magic, 0xCAFEBABE);
 119         }
 120     }
 121 
 122     @DataProvider(name = "knownDirectories")
 123     private Object[][] knownDirectories() {
 124         return new Object[][] {
 125             { "/"                     },
 126             { "."                     },
 127             { "./"                    },
 128             { "/."                    },
 129             { "/./"                   },
 130             { "/modules/java.base/.."         },
 131             { "/modules/java.base/../"        },
 132             { "/modules/java.base/../."       },
 133             { "/modules/java.base"            },
 134             { "/modules/java.base/java/lang"  },
 135             { "modules/java.base/java/lang"   },
 136             { "/modules/java.base/java/lang/" },
 137             { "modules/java.base/java/lang/"  }
 138         };
 139     }
 140 
 141     @Test(dataProvider = "knownDirectories")
 142     public void testKnownDirectories(String path) throws Exception {
 143         FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
 144         Path dir = fs.getPath(path);
 145 
 146         assertTrue(Files.isDirectory(dir));
 147 
 148         // directory should not be empty
 149         try (Stream<Path> stream = Files.list(dir)) {
 150             assertTrue(stream.count() > 0L);
 151         }
 152         try (Stream<Path> stream = Files.walk(dir)) {
 153             assertTrue(stream.count() > 0L);
 154         }
 155     }
 156 
 157     @DataProvider(name = "topLevelPkgDirs")
 158     private Object[][] topLevelPkgDirs() {
 159         return new Object[][] {
 160             { "/java/lang" },
 161             { "java/lang"  },
 162             { "/java/util" },
 163             { "java/util"  },
 164         };
 165     }
 166 
 167     @Test(dataProvider = "topLevelPkgDirs")
 168     public void testNotExists(String path) throws Exception {
 169         FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
 170         Path dir = fs.getPath(path);
 171 
 172         // package directories should not be there at top level
 173         assertTrue(Files.notExists(dir));
 174     }
 175 
 176     /**
 177      * Test the URI of every file in the jrt file system
 178      */
 179     @Test
 180     public void testToAndFromUri() throws Exception {
 181         FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
 182         Path top = fs.getPath("/");
 183         try (Stream<Path> stream = Files.walk(top)) {
 184             stream.forEach(path -> {
 185                 URI u = path.toUri();
 186                 assertTrue(u.getScheme().equalsIgnoreCase("jrt"));
 187                 assertFalse(u.isOpaque());
 188                 assertTrue(u.getAuthority() == null);
 189                 assertEquals(u.getPath(), path.toAbsolutePath().toString());
 190                 Path p = Paths.get(u);
 191                 assertEquals(p, path);
 192             });
 193         }
 194     }
 195 
 196     @Test
 197     public void testDirectoryNames() throws Exception {
 198         FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
 199         Path top = fs.getPath("/");
 200         // check that directory names do not have trailing '/' char
 201         try (Stream<Path> stream = Files.walk(top)) {
 202             stream.skip(1).filter(Files::isDirectory).forEach(path -> {
 203                 assertFalse(path.toString().endsWith("/"));
 204             });
 205         }
 206     }
 207 
 208     @DataProvider(name = "pathPrefixs")
 209     private Object[][] pathPrefixes() {
 210         return new Object[][] {
 211             { "/"                       },
 212             { "modules/java.base/java/lang"     },
 213             { "./modules/java.base/java/lang"   },
 214             { "/modules/java.base/java/lang"    },
 215             { "/./modules/java.base/java/lang"  },
 216             { "modules/java.base/java/lang/"    },
 217             { "./modules/java.base/java/lang/"  },
 218             { "/./modules/java.base/java/lang/" },
 219         };
 220     }
 221 
 222     // @Test(dataProvider = "pathPrefixes")
 223     public void testParentInDirList(String dir) throws Exception {
 224         FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
 225         Path base = fs.getPath(dir);
 226         try (DirectoryStream<Path> stream = Files.newDirectoryStream(base)) {
 227             for (Path entry: stream) {
 228                 assertTrue( entry.getParent().equals(base),
 229                     base.toString() + "-> " + entry.toString() );
 230             }
 231         }
 232     }
 233 
 234     @DataProvider(name = "dirStreamStringFilterData")
 235     private Object[][] dirStreamStringFilterData() {
 236         return new Object[][] {
 237             { "/modules/java.base/java/lang", "/reflect"      },
 238             { "/modules/java.base/java/lang", "/Object.class" },
 239             { "/modules/java.base/java/util", "/stream"       },
 240             { "/modules/java.base/java/util", "/List.class"   },
 241         };
 242     }
 243 
 244     @Test(dataProvider = "dirStreamStringFilterData")
 245     public void testDirectoryStreamStringFilter(String dir, String filter) throws Exception {
 246         FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
 247         Path base = fs.getPath(dir);
 248         try (DirectoryStream<Path> stream =
 249                 Files.newDirectoryStream(base, p->!p.toString().endsWith(filter))) {
 250             for (Path entry: stream) {
 251                 assertFalse(entry.toString().contains(filter),
 252                     "filtered path seen: " + filter);
 253             }
 254         }
 255 
 256         // make sure without filter, we do see that matching entry!
 257         boolean seen = false;
 258         try (DirectoryStream<Path> stream = Files.newDirectoryStream(base)) {
 259             for (Path entry: stream) {
 260                 if (entry.toString().endsWith(filter)) {
 261                     seen = true;
 262                     break;
 263                 }
 264             }
 265         }
 266 
 267         assertTrue(seen, "even without filter " + filter + " is missing");
 268     }
 269 
 270     @DataProvider(name = "dirStreamFilterData")
 271     private Object[][] dirStreamFilterData() {
 272         return new Object[][] {
 273             {
 274               "/",
 275               (DirectoryStream.Filter<Path>)(Files::isDirectory),
 276               "isDirectory"
 277             },
 278             {
 279               "/modules/java.base/java/lang",
 280               (DirectoryStream.Filter<Path>)(Files::isRegularFile),
 281               "isFile"
 282             }
 283         };
 284     }
 285 
 286     @Test(dataProvider = "dirStreamFilterData")
 287     private void testDirectoryStreamFilter(String dir, DirectoryStream.Filter filter,
 288             String name) throws Exception {
 289         FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
 290         Path base = fs.getPath(dir);
 291         try (DirectoryStream<Path> stream = Files.newDirectoryStream(base, filter)) {
 292             for (Path entry: stream) {
 293                 assertTrue(filter.accept(entry), "filtered path seen: " + name);
 294             }
 295         }
 296 
 297         // make sure without filter, we do see that matching entry!
 298         boolean seen = false;
 299         try (DirectoryStream<Path> stream = Files.newDirectoryStream(base)) {
 300             for (Path entry: stream) {
 301                 if (filter.accept(entry)) {
 302                     seen = true;
 303                     break;
 304                 }
 305             }
 306         }
 307 
 308         assertTrue(seen, "even without filter " + name + " is missing");
 309     }
 310 
 311     @Test
 312     private void testDirectoryStreamIterator() throws Exception {
 313         // run the tests with null filter (no filter)
 314         dirStreamIteratorTest(null);
 315         // run the same tests with trivial "accept all" filter
 316         dirStreamIteratorTest(p->true);
 317         // two other non-trivial ones
 318         dirStreamIteratorTest(Files::isDirectory);
 319         dirStreamIteratorTest(Files::isRegularFile);
 320     }
 321 
 322     private void dirStreamIteratorTest(DirectoryStream.Filter<Path> filter)
 323             throws Exception {
 324         FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
 325         // This test assumes at least there are two elements in "java/lang"
 326         // package with any filter passed. don't change to different path here!
 327         Path dir = fs.getPath("/modules/java.base/java/lang");
 328         try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, filter)) {
 329             Iterator<Path> itr = stream.iterator();
 330             itr.hasNext();
 331             Path path1 = itr.next();
 332             // missing second hasNext call
 333             Path path2 = itr.next();
 334             assertNotEquals(path1, path2);
 335         }
 336 
 337         try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, filter)) {
 338             Iterator<Path> itr = stream.iterator();
 339             // no hasNext calls at all
 340             Path path1 = itr.next();
 341             Path path2 = itr.next();
 342             assertNotEquals(path1, path2);
 343         }
 344 
 345         int numEntries = 0;
 346         try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, filter)) {
 347             Iterator<Path> itr = stream.iterator();
 348             while (itr.hasNext()) {
 349                 numEntries++;
 350                 itr.next();
 351             }
 352 
 353             // reached EOF, next call should result in exception
 354             try {
 355                 itr.next();
 356                 throw new AssertionError("should have thrown exception");
 357             } catch (NoSuchElementException nsee) {
 358                 System.out.println("got NoSuchElementException as expected");
 359             }
 360         }
 361 
 362         // redundant hasNext calls
 363         try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, filter)) {
 364             Iterator<Path> itr = stream.iterator();
 365             // any number of hasNext should definitely stay at first element
 366             for (int i = 0; i < 2*numEntries; i++) {
 367                 itr.hasNext();
 368             }
 369 
 370             for (int j = 0; j < numEntries; j++) {
 371                 itr.next();
 372             }
 373             // exactly count number of entries!
 374             assertFalse(itr.hasNext());
 375         }
 376     }
 377 
 378     @DataProvider(name = "hiddenPaths")
 379     private Object[][] hiddenPaths() {
 380         return new Object[][] {
 381             { "/META-INF" },
 382             { "/META-INF/services" },
 383             { "/META-INF/services/java.nio.file.spi.FileSystemProvider" },
 384             { "/modules/java.base/packages.offsets" },
 385             { "/modules/java.instrument/packages.offsets" },
 386             { "/modules/jdk.zipfs/packages.offsets" },
 387             { "/modules/java.base/_the.java.base.vardeps" },
 388             { "/modules/java.base/_the.java.base_batch" },
 389             { "/java/lang" },
 390             { "/java/util" },
 391         };
 392     }
 393 
 394     @Test(dataProvider = "hiddenPaths")
 395     public void testHiddenPathsNotExposed(String path) throws Exception {
 396         FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
 397         assertTrue(Files.notExists(fs.getPath(path)), path + " should not exist");
 398     }
 399 
 400     @DataProvider(name = "pathGlobPatterns")
 401     private Object[][] pathGlobPatterns() {
 402         return new Object[][] {
 403             { "/modules/*", "/modules/java.base", true },
 404             { "/modules/*", "/modules/java.base/java", false },
 405             { "/modules/j*", "/modules/java.base", true },
 406             { "/modules/J*", "/modules/java.base", false },
 407             { "**.class", "/modules/java.base/java/lang/Object.class", true },
 408             { "**.java", "/modules/java.base/java/lang/Object.class", false },
 409             { "**java/*", "/modules/java.base/java/lang", true },
 410             { "**java/lang/ref*", "/modules/java.base/java/lang/reflect", true },
 411             { "**java/lang/ref*", "/modules/java.base/java/lang/ref", true },
 412             { "**java/lang/ref?", "/modules/java.base/java/lang/ref", false },
 413             { "**java/lang/{ref,refl*}", "/modules/java.base/java/lang/ref", true },
 414             { "**java/lang/{ref,refl*}", "/modules/java.base/java/lang/reflect", true },
 415             { "**java/[a-u]?*/*.class", "/modules/java.base/java/util/Map.class", true },
 416             { "**java/util/[a-z]*.class", "/modules/java.base/java/util/TreeMap.class", false },
 417         };
 418     }
 419 
 420     @Test(dataProvider = "pathGlobPatterns")
 421     public void testGlobPathMatcher(String pattern, String path,
 422             boolean expectMatch) throws Exception {
 423         FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
 424         PathMatcher pm = fs.getPathMatcher("glob:" + pattern);
 425         Path p = fs.getPath(path);
 426         assertTrue(Files.exists(p), path);
 427         assertTrue(!(pm.matches(p) ^ expectMatch),
 428             p + (expectMatch? " should match " : " should not match ") +
 429             pattern);
 430     }
 431 
 432     @DataProvider(name = "pathRegexPatterns")
 433     private Object[][] pathRegexPatterns() {
 434         return new Object[][] {
 435             { "/modules/.*", "/modules/java.base", true },
 436             { "/modules/[^/]*", "/modules/java.base/java", false },
 437             { "/modules/j.*", "/modules/java.base", true },
 438             { "/modules/J.*", "/modules/java.base", false },
 439             { ".*\\.class", "/modules/java.base/java/lang/Object.class", true },
 440             { ".*\\.java", "/modules/java.base/java/lang/Object.class", false },
 441             { ".*java/.*", "/modules/java.base/java/lang", true },
 442             { ".*java/lang/ref.*", "/modules/java.base/java/lang/reflect", true },
 443             { ".*java/lang/ref.*", "/modules/java.base/java/lang/ref", true },
 444             { ".*/java/lang/ref.+", "/modules/java.base/java/lang/ref", false },
 445             { ".*/java/lang/(ref|refl.*)", "/modules/java.base/java/lang/ref", true },
 446             { ".*/java/lang/(ref|refl.*)", "/modules/java.base/java/lang/reflect", true },
 447             { ".*/java/[a-u]?.*/.*\\.class", "/modules/java.base/java/util/Map.class", true },
 448             { ".*/java/util/[a-z]*\\.class", "/modules/java.base/java/util/TreeMap.class", false },
 449         };
 450     }
 451 
 452     @Test(dataProvider = "pathRegexPatterns")
 453     public void testRegexPathMatcher(String pattern, String path,
 454             boolean expectMatch) throws Exception {
 455         FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
 456         PathMatcher pm = fs.getPathMatcher("regex:" + pattern);
 457         Path p = fs.getPath(path);
 458         assertTrue(Files.exists(p), path);
 459         assertTrue(!(pm.matches(p) ^ expectMatch),
 460             p + (expectMatch? " should match " : " should not match ") +
 461             pattern);
 462     }
 463 
 464     @Test
 465     public void testPackagesAndModules() throws Exception {
 466         FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
 467         assertTrue(Files.isDirectory(fs.getPath("/packages")));
 468         assertTrue(Files.isDirectory(fs.getPath("/modules")));
 469     }
 470 
 471     @DataProvider(name = "packagesSubDirs")
 472     private Object[][] packagesSubDirs() {
 473         return new Object[][] {
 474             { "java.lang" },
 475             { "java.util" },
 476             { "java.nio"  },
 477             { "jdk.nashorn.api.scripting" }
 478         };
 479     }
 480 
 481     @Test(dataProvider = "packagesSubDirs")
 482     public void testPackagesSubDirs(String pkg) throws Exception {
 483         FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
 484         assertTrue(Files.isDirectory(fs.getPath("/packages/" + pkg)),
 485             pkg + " missing");
 486     }
 487 
 488     @DataProvider(name = "packagesLinks")
 489     private Object[][] packagesLinks() {
 490         return new Object[][] {
 491             { "/packages/java.lang/java.base" },
 492             { "/packages/java.lang/java.instrument" },
 493             { "/packages/java/java.base" },
 494             { "/packages/java/java.instrument" },
 495             { "/packages/java/java.rmi"  },
 496             { "/packages/java/java.sql"  },
 497             { "/packages/javax/java.base"  },
 498             { "/packages/javax/java.sql"  },
 499             { "/packages/javax/java.xml"  },
 500             { "/packages/javax/java.management"  },
 501             { "/packages/java.util/java.base" },
 502             { "/packages/jdk.nashorn.api.scripting/jdk.scripting.nashorn" },
 503         };
 504     }
 505 
 506     @Test(dataProvider = "packagesLinks")
 507     public void testPackagesLinks(String link) throws Exception {
 508         FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
 509         Path path = fs.getPath(link);
 510         assertTrue(Files.exists(path), link + " missing");
 511         assertTrue(Files.isSymbolicLink(path), path + " is not a link");
 512         path = Files.readSymbolicLink(path);
 513         assertEquals(path.toString(), "/modules" + link.substring(link.lastIndexOf("/")));
 514     }
 515 
 516     @DataProvider(name = "modulesSubDirs")
 517     private Object[][] modulesSubDirs() {
 518         return new Object[][] {
 519             { "java.base" },
 520             { "java.sql" },
 521             { "jdk.scripting.nashorn" },
 522         };
 523     }
 524 
 525     @Test(dataProvider = "modulesSubDirs")
 526     public void testModulesSubDirs(String module) throws Exception {
 527         FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
 528         Path path = fs.getPath("/modules/" + module);
 529         assertTrue(Files.isDirectory(path), module + " missing");
 530         assertTrue(!Files.isSymbolicLink(path), path + " is a link");
 531     }
 532 
 533     @DataProvider(name="linkChases")
 534     private Object[][] linkChases() {
 535         return new Object[][] {
 536             { "/modules/java.base/java/lang" },
 537             { "/modules/java.base/java/util/Vector.class" },
 538             { "/modules/jdk.scripting.nashorn/jdk/nashorn" },
 539             { "/packages/java.lang/java.base/java/lang" },
 540             { "/packages/java.util/java.base/java/util/Vector.class" },
 541         };
 542     }
 543 
 544     @Test(dataProvider = "linkChases")
 545     public void testLinkChases(String link) throws Exception {
 546         FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
 547         Path path = fs.getPath(link);
 548         assertTrue(Files.exists(path), link);
 549     }
 550 
 551     @Test
 552     public void testSymlinkDirList() throws Exception {
 553         FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
 554         Path path = fs.getPath("/packages/java.lang/java.base");
 555         assertTrue(Files.isSymbolicLink(path));
 556         assertTrue(Files.isDirectory(path));
 557 
 558         boolean javaSeen = false, javaxSeen = false;
 559         try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
 560             for (Path p : stream) {
 561                 String str = p.toString();
 562                 if (str.endsWith("/java")) {
 563                     javaSeen = true;
 564                 } else if (str.endsWith("javax")) {
 565                     javaxSeen = true;
 566                 }
 567             }
 568         }
 569         assertTrue(javaSeen);
 570         assertTrue(javaxSeen);
 571     }
 572 
 573     @Test
 574     public void invalidPathTest() {
 575         FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
 576         InvalidPathException ipe = null;
 577         try {
 578             boolean res = Files.exists(fs.getPath("/packages/\ud834\udd7b"));
 579             assertFalse(res);
 580             return;
 581         } catch (InvalidPathException e) {
 582             ipe = e;
 583         }
 584         assertTrue(ipe != null);
 585     }
 586 }