1 /*
   2  * Copyright (c) 2019, 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 import org.testng.annotations.AfterClass;
  25 import org.testng.annotations.BeforeClass;
  26 import org.testng.annotations.DataProvider;
  27 import org.testng.annotations.Test;
  28 
  29 import java.io.IOException;
  30 import java.nio.file.*;
  31 import java.nio.file.spi.FileSystemProvider;
  32 import java.util.Iterator;
  33 import java.util.Map;
  34 import java.util.NoSuchElementException;
  35 import java.util.regex.PatternSyntaxException;
  36 
  37 import static org.testng.Assert.*;
  38 
  39 /**
  40  * @test
  41  * @summary ZIP File System tests that leverage DirectoryStream
  42  * @modules jdk.zipfs
  43  * @compile ZipFsDirectoryStreamTests.java
  44  * @run testng ZipFsDirectoryStreamTests
  45  * @run testng/othervm/java.security.policy=test.policy  ZipFsDirectoryStreamTests
  46  */
  47 public class ZipFsDirectoryStreamTests {
  48 
  49     // Map to used for creating a ZIP archive
  50     private static final Map<String, String> ZIPFS_MAP = Map.of("create", "true");
  51 
  52     // Map to used for extracting a ZIP archive
  53     private static final Map<String, String> UNZIPFS_MAP = Map.of();
  54 
  55     // The ZIP file system provider
  56     private static final FileSystemProvider ZIPFS_PROVIDER = getZipFSProvider();
  57 
  58     // Primary jar file used for testing
  59     private static Path jarFile;
  60 
  61     // Jar file used to validate the behavior of the navigation of an empty directory
  62     private static Path emptyJarFile;
  63 
  64     /**
  65      * Create the JAR files used by the tests
  66      */
  67     @BeforeClass
  68     public void setUp() {
  69         emptyJarFile = Paths.get("emptyDir.jar");
  70         try (FileSystem zipfs = ZIPFS_PROVIDER.newFileSystem(emptyJarFile, ZIPFS_MAP);) {
  71 
  72             jarFile = Utils.createJarFile("basic.jar",
  73                     "META-INF/services/java.nio.file.spi.FileSystemProvider");
  74 
  75             Files.createDirectory(zipfs.getPath("emptyDir"));
  76         } catch (IOException e) {
  77             e.printStackTrace();
  78         }
  79     }
  80 
  81     /**
  82      * Remove JAR files used by test as part of clean-up
  83      */
  84     @AfterClass
  85     public void tearDown() {
  86         try {
  87             Files.deleteIfExists(jarFile);
  88             Files.deleteIfExists(emptyJarFile);
  89         } catch (Exception e) {
  90             e.printStackTrace();
  91         }
  92 
  93     }
  94 
  95     /**
  96      * Validate that you can specify a DirectoryStream.Filter using the ZIP File System and that the returned
  97      * Iterator correctly indicates whether the filer has been matched
  98      */
  99     @Test(dataProvider = "filterTestValues")
 100     public void test0000(String glob, boolean expectedResult, String errMsg) {
 101 
 102         try (FileSystem zipfs = ZIPFS_PROVIDER.newFileSystem(Paths.get("basic.jar"), ZIPFS_MAP);
 103              DirectoryStream<Path> ds = Files.newDirectoryStream(zipfs.getPath("/"),
 104                      new DirectoryStream.Filter<Path>() {
 105                          private PathMatcher matcher =
 106                                  zipfs.getPathMatcher("glob:"+ glob);
 107                          public boolean accept(Path file) {
 108                              return matcher.matches(file.getFileName());
 109                          }
 110 
 111                      })) {
 112 
 113             assertEquals(ds.iterator().hasNext(), expectedResult, errMsg);
 114 
 115         } catch (Exception e) {
 116             e.printStackTrace();
 117         }
 118     }
 119 
 120     /**
 121      * Validate that you can specify a glob using the ZIP File System and that the returned
 122      * Iterator correctly indicates whether the glob pattern has been matched
 123      */
 124     @Test(dataProvider = "filterTestValues")
 125     public void test0001(String glob, boolean expectedResult, String errMsg) {
 126 
 127         try (FileSystem zipfs = ZIPFS_PROVIDER.newFileSystem(Paths.get("basic.jar"), ZIPFS_MAP);
 128              DirectoryStream<Path> ds =
 129                      Files.newDirectoryStream(zipfs.getPath("/"), glob)) {
 130             assertEquals(ds.iterator().hasNext(), expectedResult, errMsg);
 131 
 132         } catch (Exception e) {
 133             e.printStackTrace();
 134         }
 135     }
 136 
 137     /**
 138      * Validate a PatternSyntaxException is thrown when specifying an invalid glob pattern
 139      * with the ZIP File system
 140      */
 141     @Test
 142     public void test0002() {
 143 
 144         try (FileSystem zipfs = ZIPFS_PROVIDER.newFileSystem(Paths.get("basic.jar"), ZIPFS_MAP);
 145              ) {
 146             assertThrows(PatternSyntaxException.class, () ->
 147                     Files.newDirectoryStream(zipfs.getPath("/"), "*[a-z"));
 148         } catch (Exception e) {
 149             e.printStackTrace();
 150         }
 151     }
 152 
 153     /**
 154      * Validate that the correct type of paths are returned when creating a DirectoryStream
 155      */
 156     @Test(dataProvider = "startPaths")
 157     public void test0003(String startPath, String expectedPath) throws IOException {
 158         try (FileSystem zipfs = ZIPFS_PROVIDER.newFileSystem(Paths.get("basic.jar"), ZIPFS_MAP);
 159              DirectoryStream<Path> stream = Files.newDirectoryStream(zipfs.getPath(startPath))) {
 160 
 161             for (Path entry : stream) {
 162                 assertTrue(entry.toString().equals(expectedPath),
 163                         String.format("Error: Expected path %s not found when starting at %s%n",
 164                                 expectedPath, entry));
 165             }
 166         }
 167     }
 168 
 169     /**
 170      * Validate an NotDirectoryException is thrown when specifying a file for the starting path for
 171      * creating a DirectoryStream with the ZIP File System
 172      */
 173     @Test
 174     public void test0004() {
 175 
 176         try (FileSystem zipfs = ZIPFS_PROVIDER.newFileSystem(Paths.get("basic.jar"), ZIPFS_MAP)) {
 177             assertThrows(NotDirectoryException.class,
 178                     () -> Files.newDirectoryStream(
 179                             zipfs.getPath("META-INF/services/java.nio.file.spi.FileSystemProvider")));
 180 
 181         } catch (Exception e) {
 182             e.printStackTrace();
 183         }
 184     }
 185 
 186     /**
 187      * Validate a IllegalStateException is thrown when accessing the Iterator more than once
 188      * with the ZIP File System
 189      */
 190     @Test
 191     public void test0005() {
 192 
 193         try (FileSystem zipfs = ZIPFS_PROVIDER.newFileSystem(Paths.get("basic.jar"), ZIPFS_MAP);
 194              DirectoryStream<Path> ds =
 195                      Files.newDirectoryStream(zipfs.getPath("/"))) {
 196             ds.iterator();
 197             assertThrows(IllegalStateException.class, () -> ds.iterator());
 198 
 199         } catch (Exception e) {
 200             e.printStackTrace();
 201         }
 202     }
 203 
 204     /**
 205      * Validate a IllegalStateException is thrown when accessing the Iterator after the DirectoryStream
 206      * has been closed with the ZIP File System
 207      */
 208     @Test
 209     public void test0006() {
 210 
 211         try (FileSystem zipfs = ZIPFS_PROVIDER.newFileSystem(Paths.get("basic.jar"), ZIPFS_MAP);
 212              DirectoryStream<Path> ds =
 213                      Files.newDirectoryStream(zipfs.getPath("/"))) {
 214             ds.close();
 215             assertThrows(IllegalStateException.class, () -> ds.iterator());
 216 
 217             // ZipDirectoryStream.iterator() throws ClosedDirectoryStream when obtaining an Iterator
 218             // when the DirectoryStream is closed
 219             assertThrows(ClosedDirectoryStreamException.class, () -> ds.iterator());
 220 
 221         } catch (Exception e) {
 222             e.printStackTrace();
 223         }
 224     }
 225 
 226     /**
 227      * Validate an UnsupportedOperationException is thrown when invoking an Iterator operation that is not
 228      * supported with the ZIP File System
 229      */
 230     @Test
 231     public void test0007() {
 232 
 233         try (FileSystem zipfs = ZIPFS_PROVIDER.newFileSystem(Paths.get("basic.jar"), ZIPFS_MAP);
 234              DirectoryStream<Path> ds =
 235                      Files.newDirectoryStream(zipfs.getPath("/"))) {
 236             Iterator<Path> i = ds.iterator();
 237 
 238             //System.out.println("Path: "+ i.next());
 239             assertThrows(UnsupportedOperationException.class, () -> i.remove());
 240         } catch (Exception e) {
 241             e.printStackTrace();
 242         }
 243     }
 244 
 245     /**
 246      * Validate an NoSuchElementException is thrown when invoking an Iterator.next() on a closed
 247      * DirectoryStream with the ZIP File System
 248      */
 249     @Test
 250     public void test0008() {
 251 
 252         try (FileSystem zipfs = ZIPFS_PROVIDER.newFileSystem(Paths.get("basic.jar"), ZIPFS_MAP);
 253              DirectoryStream<Path> ds =
 254                      Files.newDirectoryStream(zipfs.getPath("/"))) {
 255             Iterator<Path> i = ds.iterator();
 256             ds.close();
 257             assertThrows(NoSuchElementException.class, () -> i.next());
 258         } catch (Exception e) {
 259             e.printStackTrace();
 260         }
 261     }
 262 
 263     /**
 264      * Validate Iterator.hasNext() returns false when the directory is empty with the ZIP File System
 265      */
 266     @Test
 267     public void test0009() {
 268         try (FileSystem zipfs = ZIPFS_PROVIDER.newFileSystem(emptyJarFile, ZIPFS_MAP);
 269              DirectoryStream<Path> ds = Files.newDirectoryStream(zipfs.getPath("emptyDir"))) {
 270             assertFalse(ds.iterator().hasNext(), "Error: directory was not empty!");
 271 
 272         } catch(Exception e) {
 273             e.printStackTrace();
 274 
 275         }
 276     }
 277 
 278     /**
 279      * Validate Iterator.hasNext() returns false when the DirectoryStream is closed
 280      * with the ZIP File System
 281      */
 282     @Test
 283     public void test0010() {
 284 
 285         try (FileSystem zipfs = ZIPFS_PROVIDER.newFileSystem(Paths.get("basic.jar"), ZIPFS_MAP);
 286              DirectoryStream<Path> ds =
 287                      Files.newDirectoryStream(zipfs.getPath("/"))) {
 288             Iterator<Path> i = ds.iterator();
 289             ds.close();
 290             assertFalse(i.hasNext(), "Error: false should be returned as DirectoryStream is closed!");
 291         } catch (Exception e) {
 292             e.printStackTrace();
 293         }
 294     }
 295 
 296     /**
 297      * Validate that an IOException thrown by a filter is returned as the cause
 298      * via a DirectoryIteratorException
 299      */
 300     @Test
 301     public void test0011() {
 302 
 303         try (FileSystem zipfs = ZIPFS_PROVIDER.newFileSystem(Paths.get("basic.jar"), ZIPFS_MAP);
 304              DirectoryStream<Path> ds = Files.newDirectoryStream(zipfs.getPath("/"),
 305                      new DirectoryStream.Filter<Path>() {
 306                          private PathMatcher matcher =
 307                                  zipfs.getPathMatcher("glob:M*");
 308                          public boolean accept(Path file) throws IOException {
 309                              throw new java.util.zip.ZipException();
 310                          }
 311                      }))
 312         {
 313 
 314             for (Path entry: ds) {
 315                 throw new RuntimeException("Expected DirectoryIteratorException not thrown");
 316             }
 317         } catch (DirectoryIteratorException x) {
 318             IOException cause = x.getCause();
 319             if (!(cause instanceof java.util.zip.ZipException))
 320                 throw new RuntimeException("Expected IOException not propagated");
 321         } catch (IOException e) {
 322             e.printStackTrace();
 323         }
 324     }
 325 
 326     /**
 327      * Glob values to use to validate filtering
 328      */
 329     @DataProvider(name = "filterTestValues")
 330     public static Object[][] filterValues() {
 331 
 332         String expectedMsg = "Error: Matching entries were expected but not found!!!";
 333         String notExpectedMsg = "Error: No matching entries expected but were found!!!";
 334         return new Object[][]{
 335 
 336                 {"M*", true, expectedMsg},
 337                 {"I*", false, notExpectedMsg}
 338         };
 339     }
 340 
 341     /**
 342      * Starting Path for the DirectoryStream and the expected path to be returned when traversing the stream
 343      */
 344     @DataProvider(name = "startPaths")
 345     public static Object[][] Name() {
 346         return new Object[][]{
 347 
 348                 {"META-INF", "META-INF/services"},
 349                 {"/META-INF", "/META-INF/services"},
 350                 {"./META-INF", "./META-INF/services"},
 351                 {"", "META-INF"},
 352                 {"/", "/META-INF"},
 353                 {".", "./META-INF"},
 354                 {"./", "./META-INF"}
 355         };
 356     }
 357 
 358     /**
 359      * Returns the Zip FileSystem Provider
 360      */
 361     private static FileSystemProvider getZipFSProvider() {
 362         for (FileSystemProvider fsProvider : FileSystemProvider.installedProviders()) {
 363             if ("jar".equals(fsProvider.getScheme())) {
 364                 return fsProvider;
 365             }
 366         }
 367         return null;
 368     }
 369 
 370 }