--- old/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipDirectoryStream.java 2019-01-09 19:20:22.000000000 -0500 +++ new/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipDirectoryStream.java 2019-01-09 19:20:21.000000000 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,7 @@ import java.io.IOException; import java.nio.file.ClosedDirectoryStreamException; +import java.nio.file.DirectoryIteratorException; import java.nio.file.DirectoryStream; import java.nio.file.NotDirectoryException; import java.nio.file.Path; @@ -67,7 +68,7 @@ try { itr = zipfs.iteratorOf(dir, filter); } catch (IOException e) { - throw new IllegalStateException(e); + throw new DirectoryIteratorException(e); } return new Iterator() { --- old/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java 2019-01-09 19:20:23.000000000 -0500 +++ new/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipFileSystem.java 2019-01-09 19:20:23.000000000 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -428,11 +428,19 @@ // (3) if parent "dir" is relative when ZipDirectoryStream // is created, the returned child path needs to be relative // as well. + // (4) if parent "dir" starts with './' or is '.' when ZipDirectoryStream + // is created, the returned child path needs to also start + // with './' as well. byte[] cname = child.name; - if (!dir.isAbsolute()) { - cname = Arrays.copyOfRange(cname, 1, cname.length); + ZipPath zpath; + + if (dir.isAbsolute()) { + zpath = new ZipPath(this, cname, true); + } else { + ZipPath childPath = new ZipPath(this, cname, true); + ZipPath childFileName = childPath.getFileName(); + zpath = dir.resolve(childFileName); } - ZipPath zpath = new ZipPath(this, cname, true); if (filter == null || filter.accept(zpath)) list.add(zpath); child = child.sibling; --- old/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipPath.java 2019-01-09 19:20:25.000000000 -0500 +++ new/src/jdk.zipfs/share/classes/jdk/nio/zipfs/ZipPath.java 2019-01-09 19:20:24.000000000 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -93,7 +93,7 @@ } @Override - public Path getFileName() { + public ZipPath getFileName() { int off = path.length; if (off == 0 || off == 1 && path[0] == '/') return null; --- old/test/jdk/jdk/nio/zipfs/Basic.java 2019-01-09 19:20:26.000000000 -0500 +++ new/test/jdk/jdk/nio/zipfs/Basic.java 2019-01-09 19:20:26.000000000 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,6 @@ import java.nio.file.AccessMode; import java.nio.file.ClosedFileSystemException; -import java.nio.file.DirectoryStream; import java.nio.file.FileStore; import java.nio.file.FileSystem; import java.nio.file.FileSystems; @@ -87,32 +86,6 @@ // Test: exercise directory iterator and retrieval of basic attributes Files.walkFileTree(fs.getPath("/"), new FileTreePrinter()); - // Test: DirectoryStream - found = false; - - try (DirectoryStream stream = Files.newDirectoryStream(fs.getPath("/"))) { - for (Path entry: stream) { - found = entry.toString().equals("/META-INF"); - if (found) break; - } - } - if (!found) - throw new RuntimeException("Expected file not found"); - - try (DirectoryStream stream = Files.newDirectoryStream(fs.getPath("META-INF"))) { - for (Path entry: stream) { - if (entry.toString().equals("/META-INF/services")) - throw new RuntimeException("child path should be relative"); - } - } - - try (DirectoryStream stream = Files.newDirectoryStream(fs.getPath("/META-INF"))) { - for (Path entry: stream) { - if (entry.toString().equals("META-INF/services")) - throw new RuntimeException("child path should be absolute"); - } - } - // Test: copy file from zip file to current (scratch) directory Path source = fs.getPath("/META-INF/services/java.nio.file.spi.FileSystemProvider"); if (Files.exists(source)) { --- /dev/null 2019-01-09 19:20:27.000000000 -0500 +++ new/test/jdk/jdk/nio/zipfs/ZipFsDirectoryStreamTests.java 2019-01-09 19:20:27.000000000 -0500 @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.nio.file.*; +import java.nio.file.spi.FileSystemProvider; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.regex.PatternSyntaxException; + +import static org.testng.Assert.*; + +/** + * @test + * @summary ZIP File System tests that leverage DirectoryStream + * @modules jdk.zipfs + * @compile ZipFsDirectoryStreamTests.java + * @run testng ZipFsDirectoryStreamTests + * @run testng/othervm/java.security.policy=test.policy ZipFsDirectoryStreamTests + */ +public class ZipFsDirectoryStreamTests { + + // Map to used for creating a ZIP archive + private static final Map ZIPFS_MAP = Map.of("create", "true"); + + // Map to used for extracting a ZIP archive + private static final Map UNZIPFS_MAP = Map.of(); + + // The ZIP file system provider + private static final FileSystemProvider ZIPFS_PROVIDER = getZipFSProvider(); + + // Primary jar file used for testing + private static Path jarFile; + + // Jar file used to validate the behavior of the navigation of an empty directory + private static Path emptyJarFile; + + /** + * Create the JAR files used by the tests + */ + @BeforeClass + public void setUp() { + emptyJarFile = Paths.get("emptyDir.jar"); + try (FileSystem zipfs = ZIPFS_PROVIDER.newFileSystem(emptyJarFile, ZIPFS_MAP);) { + + jarFile = Utils.createJarFile("basic.jar", + "META-INF/services/java.nio.file.spi.FileSystemProvider"); + + Files.createDirectory(zipfs.getPath("emptyDir")); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Remove JAR files used by test as part of clean-up + */ + @AfterClass + public void tearDown() { + try { + Files.deleteIfExists(jarFile); + Files.deleteIfExists(emptyJarFile); + } catch (Exception e) { + e.printStackTrace(); + } + + } + + /** + * Validate that you can specify a DirectoryStream.Filter using the ZIP File System and that the returned + * Iterator correctly indicates whether the filer has been matched + */ + @Test(dataProvider = "filterTestValues") + public void test0000(String glob, boolean expectedResult, String errMsg) { + + try (FileSystem zipfs = ZIPFS_PROVIDER.newFileSystem(Paths.get("basic.jar"), ZIPFS_MAP); + DirectoryStream ds = Files.newDirectoryStream(zipfs.getPath("/"), + new DirectoryStream.Filter() { + private PathMatcher matcher = + zipfs.getPathMatcher("glob:"+ glob); + public boolean accept(Path file) { + return matcher.matches(file.getFileName()); + } + + })) { + + assertEquals(ds.iterator().hasNext(), expectedResult, errMsg); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Validate that you can specify a glob using the ZIP File System and that the returned + * Iterator correctly indicates whether the glob pattern has been matched + */ + @Test(dataProvider = "filterTestValues") + public void test0001(String glob, boolean expectedResult, String errMsg) { + + try (FileSystem zipfs = ZIPFS_PROVIDER.newFileSystem(Paths.get("basic.jar"), ZIPFS_MAP); + DirectoryStream ds = + Files.newDirectoryStream(zipfs.getPath("/"), glob)) { + assertEquals(ds.iterator().hasNext(), expectedResult, errMsg); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Validate a PatternSyntaxException is thrown when specifying an invalid glob pattern + * with the ZIP File system + */ + @Test + public void test0002() { + + try (FileSystem zipfs = ZIPFS_PROVIDER.newFileSystem(Paths.get("basic.jar"), ZIPFS_MAP); + ) { + assertThrows(PatternSyntaxException.class, () -> + Files.newDirectoryStream(zipfs.getPath("/"), "*[a-z")); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Validate that the correct type of paths are returned when creating a DirectoryStream + */ + @Test(dataProvider = "startPaths") + public void test0003(String startPath, String expectedPath) throws IOException { + try (FileSystem zipfs = ZIPFS_PROVIDER.newFileSystem(Paths.get("basic.jar"), ZIPFS_MAP); + DirectoryStream stream = Files.newDirectoryStream(zipfs.getPath(startPath))) { + + for (Path entry : stream) { + assertTrue(entry.toString().equals(expectedPath), + String.format("Error: Expected path %s not found when starting at %s%n", + expectedPath, entry)); + } + } + } + + /** + * Validate an NotDirectoryException is thrown when specifying a file for the starting path for + * creating a DirectoryStream with the ZIP File System + */ + @Test + public void test0004() { + + try (FileSystem zipfs = ZIPFS_PROVIDER.newFileSystem(Paths.get("basic.jar"), ZIPFS_MAP)) { + assertThrows(NotDirectoryException.class, + () -> Files.newDirectoryStream( + zipfs.getPath("META-INF/services/java.nio.file.spi.FileSystemProvider"))); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Validate a IllegalStateException is thrown when accessing the Iterator more than once + * with the ZIP File System + */ + @Test + public void test0005() { + + try (FileSystem zipfs = ZIPFS_PROVIDER.newFileSystem(Paths.get("basic.jar"), ZIPFS_MAP); + DirectoryStream ds = + Files.newDirectoryStream(zipfs.getPath("/"))) { + ds.iterator(); + assertThrows(IllegalStateException.class, () -> ds.iterator()); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Validate a IllegalStateException is thrown when accessing the Iterator after the DirectoryStream + * has been closed with the ZIP File System + */ + @Test + public void test0006() { + + try (FileSystem zipfs = ZIPFS_PROVIDER.newFileSystem(Paths.get("basic.jar"), ZIPFS_MAP); + DirectoryStream ds = + Files.newDirectoryStream(zipfs.getPath("/"))) { + ds.close(); + assertThrows(IllegalStateException.class, () -> ds.iterator()); + + // ZipDirectoryStream.iterator() throws ClosedDirectoryStream when obtaining an Iterator + // when the DirectoryStream is closed + assertThrows(ClosedDirectoryStreamException.class, () -> ds.iterator()); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Validate an UnsupportedOperationException is thrown when invoking an Iterator operation that is not + * supported with the ZIP File System + */ + @Test + public void test0007() { + + try (FileSystem zipfs = ZIPFS_PROVIDER.newFileSystem(Paths.get("basic.jar"), ZIPFS_MAP); + DirectoryStream ds = + Files.newDirectoryStream(zipfs.getPath("/"))) { + Iterator i = ds.iterator(); + + //System.out.println("Path: "+ i.next()); + assertThrows(UnsupportedOperationException.class, () -> i.remove()); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Validate an NoSuchElementException is thrown when invoking an Iterator.next() on a closed + * DirectoryStream with the ZIP File System + */ + @Test + public void test0008() { + + try (FileSystem zipfs = ZIPFS_PROVIDER.newFileSystem(Paths.get("basic.jar"), ZIPFS_MAP); + DirectoryStream ds = + Files.newDirectoryStream(zipfs.getPath("/"))) { + Iterator i = ds.iterator(); + ds.close(); + assertThrows(NoSuchElementException.class, () -> i.next()); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Validate Iterator.hasNext() returns false when the directory is empty with the ZIP File System + */ + @Test + public void test0009() { + try (FileSystem zipfs = ZIPFS_PROVIDER.newFileSystem(emptyJarFile, ZIPFS_MAP); + DirectoryStream ds = Files.newDirectoryStream(zipfs.getPath("emptyDir"))) { + assertFalse(ds.iterator().hasNext(), "Error: directory was not empty!"); + + } catch(Exception e) { + e.printStackTrace(); + + } + } + + /** + * Validate Iterator.hasNext() returns false when the DirectoryStream is closed + * with the ZIP File System + */ + @Test + public void test0010() { + + try (FileSystem zipfs = ZIPFS_PROVIDER.newFileSystem(Paths.get("basic.jar"), ZIPFS_MAP); + DirectoryStream ds = + Files.newDirectoryStream(zipfs.getPath("/"))) { + Iterator i = ds.iterator(); + ds.close(); + assertFalse(i.hasNext(), "Error: false should be returned as DirectoryStream is closed!"); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Validate that an IOException thrown by a filter is returned as the cause + * via a DirectoryIteratorException + */ + @Test + public void test0011() { + + try (FileSystem zipfs = ZIPFS_PROVIDER.newFileSystem(Paths.get("basic.jar"), ZIPFS_MAP); + DirectoryStream ds = Files.newDirectoryStream(zipfs.getPath("/"), + new DirectoryStream.Filter() { + private PathMatcher matcher = + zipfs.getPathMatcher("glob:M*"); + public boolean accept(Path file) throws IOException { + throw new java.util.zip.ZipException(); + } + })) + { + + for (Path entry: ds) { + throw new RuntimeException("Expected DirectoryIteratorException not thrown"); + } + } catch (DirectoryIteratorException x) { + IOException cause = x.getCause(); + if (!(cause instanceof java.util.zip.ZipException)) + throw new RuntimeException("Expected IOException not propagated"); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * Glob values to use to validate filtering + */ + @DataProvider(name = "filterTestValues") + public static Object[][] filterValues() { + + String expectedMsg = "Error: Matching entries were expected but not found!!!"; + String notExpectedMsg = "Error: No matching entries expected but were found!!!"; + return new Object[][]{ + + {"M*", true, expectedMsg}, + {"I*", false, notExpectedMsg} + }; + } + + /** + * Starting Path for the DirectoryStream and the expected path to be returned when traversing the stream + */ + @DataProvider(name = "startPaths") + public static Object[][] Name() { + return new Object[][]{ + + {"META-INF", "META-INF/services"}, + {"/META-INF", "/META-INF/services"}, + {"./META-INF", "./META-INF/services"}, + {"", "META-INF"}, + {"/", "/META-INF"}, + {".", "./META-INF"}, + {"./", "./META-INF"} + }; + } + + /** + * Returns the Zip FileSystem Provider + */ + private static FileSystemProvider getZipFSProvider() { + for (FileSystemProvider fsProvider : FileSystemProvider.installedProviders()) { + if ("jar".equals(fsProvider.getScheme())) { + return fsProvider; + } + } + return null; + } + +}