test/jdk/jdk/nio/zipfs/CopyMoveTests.java
changeset 58845 e492513d3630
child 58941 00878bee8f4b
equal deleted inserted replaced
58844:5a0e0d0b3a27 58845:e492513d3630
       
     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 
       
    25 import org.testng.annotations.DataProvider;
       
    26 import org.testng.annotations.Test;
       
    27 
       
    28 import java.io.IOException;
       
    29 import java.io.InputStream;
       
    30 import java.nio.charset.StandardCharsets;
       
    31 import java.nio.file.*;
       
    32 import java.security.SecureRandom;
       
    33 import java.util.Arrays;
       
    34 import java.util.Map;
       
    35 import java.util.zip.ZipEntry;
       
    36 import java.util.zip.ZipFile;
       
    37 
       
    38 import static java.lang.String.format;
       
    39 import static java.util.stream.Collectors.joining;
       
    40 import static org.testng.Assert.*;
       
    41 
       
    42 /**
       
    43  * @test
       
    44  * @bug 8223771
       
    45  * @summary Test Files::copy and Files::move with Zip FS
       
    46  * @modules jdk.zipfs
       
    47  * @run testng/othervm CopyMoveTests
       
    48  */
       
    49 public class CopyMoveTests {
       
    50     // Enable debugging output
       
    51     private static final boolean DEBUG = false;
       
    52     // Path to current directory
       
    53     private static final Path HERE = Path.of(".");
       
    54     // Value to use when creating Zip Entries
       
    55     private static final String ZIP_FILE_VALUE = "US Open 2019";
       
    56     // Value used to create the OS file to be copied into/from a Zip File
       
    57     private static final String OS_FILE_VALUE = "Hello World!";
       
    58     private static final SecureRandom random = new SecureRandom();
       
    59 
       
    60     /*
       
    61      * DataProvider used to verify that a FileAlreadyExistsException is
       
    62      * thrown with copying a file without the REPLACE_EXISTING option
       
    63      */
       
    64     @DataProvider(name = "zipfsMap")
       
    65     private Object[][] zipfsMap() {
       
    66         return new Object[][]{
       
    67                 {Map.of("create", "true"), ZipEntry.DEFLATED},
       
    68                 {Map.of("create", "true", "noCompression", "true"),
       
    69                         ZipEntry.STORED},
       
    70                 {Map.of("create", "true", "noCompression", "false"),
       
    71                         ZipEntry.DEFLATED}
       
    72         };
       
    73     }
       
    74 
       
    75     /*
       
    76      * DataProvider used to verify that an entry may be copied or moved within
       
    77      * a Zip file system with the correct compression method
       
    78      */
       
    79     @DataProvider(name = "copyMoveMap")
       
    80     private Object[][] copyMoveMap() {
       
    81         return new Object[][]{
       
    82                 {Map.of("create", "true"), ZipEntry.DEFLATED, ZipEntry.STORED},
       
    83                 {Map.of("create", "true", "noCompression", "true"),
       
    84                         ZipEntry.STORED, ZipEntry.DEFLATED},
       
    85                 {Map.of("create", "true", "noCompression", "false"),
       
    86                         ZipEntry.DEFLATED, ZipEntry.STORED}
       
    87         };
       
    88     }
       
    89 
       
    90     /**
       
    91      * Validate that an entry that is copied within a Zip file is copied with
       
    92      * the correct compression
       
    93      *
       
    94      * @param createMap           Zip FS properties to use when creating the Zip File
       
    95      * @param compression         The compression used when writing the initial entries
       
    96      * @param expectedCompression The compression to be used when copying the entry
       
    97      * @throws Exception If an error occurs
       
    98      */
       
    99     @Test(dataProvider = "copyMoveMap", enabled = true)
       
   100     public void copyTest(Map<String, String> createMap, int compression,
       
   101                          int expectedCompression) throws Exception {
       
   102         Entry e0 = Entry.of("Entry-0", compression, ZIP_FILE_VALUE);
       
   103         Entry e1 = Entry.of("Entry-1", compression, ZIP_FILE_VALUE);
       
   104         Entry e00 = Entry.of("Entry-00", expectedCompression, ZIP_FILE_VALUE);
       
   105 
       
   106         Path zipFile = generatePath(HERE, "test", ".zip");
       
   107         Files.deleteIfExists(zipFile);
       
   108 
       
   109         // Create the Zip File with the initial entries
       
   110         createZipFile(zipFile, createMap, e0, e1);
       
   111         try (FileSystem zipfs = FileSystems.newFileSystem(zipFile,
       
   112                 Map.of("noCompression", expectedCompression == ZipEntry.STORED))) {
       
   113             Files.copy(zipfs.getPath(e0.name), zipfs.getPath(e00.name));
       
   114         }
       
   115         // Verify entries e0, e1 and e00 exist
       
   116         verify(zipFile, e0, e1, e00);
       
   117         Files.deleteIfExists(zipFile);
       
   118 
       
   119     }
       
   120 
       
   121     /**
       
   122      * Validate that an entry that is copied from one Zip file to another,
       
   123      * is copied with the correct compression
       
   124      *
       
   125      * @param createMap           Zip FS properties to use when creating the Zip File
       
   126      * @param compression         The compression used when writing the initial entries
       
   127      * @param expectedCompression The compression to be used when copying the entry
       
   128      * @throws Exception If an error occurs
       
   129      */
       
   130     @Test(dataProvider = "copyMoveMap", enabled = true)
       
   131     public void copyZipToZipTest(Map<String, String> createMap, int compression,
       
   132                             int expectedCompression) throws Exception {
       
   133         Entry e0 = Entry.of("Entry-0", compression, ZIP_FILE_VALUE);
       
   134         Entry e1 = Entry.of("Entry-1", compression, ZIP_FILE_VALUE);
       
   135         Entry e00 = Entry.of("Entry-00", expectedCompression, ZIP_FILE_VALUE);
       
   136 
       
   137         Path zipFile = generatePath(HERE, "test", ".zip");
       
   138         Path zipFile2 = generatePath(HERE, "test", ".zip");
       
   139         Files.deleteIfExists(zipFile);
       
   140         Files.deleteIfExists(zipFile2);
       
   141 
       
   142         createZipFile(zipFile, createMap, e0, e1);
       
   143         try (FileSystem zipfs = FileSystems.newFileSystem(zipFile, createMap);
       
   144              FileSystem zipfsTarget = FileSystems.newFileSystem(zipFile2,
       
   145                      Map.of("create", "true", "noCompression",
       
   146                              expectedCompression == ZipEntry.STORED))) {
       
   147             Files.copy(zipfs.getPath(e0.name), zipfsTarget.getPath(e00.name));
       
   148         }
       
   149         // Only 1 entry copied to the secondary Zip file
       
   150         verify(zipFile2, e00);
       
   151         // Verify  entries e0 and e1 remain in the original Zip file
       
   152         verify(zipFile, e0, e1);
       
   153         Files.deleteIfExists(zipFile);
       
   154         Files.deleteIfExists(zipFile2);
       
   155     }
       
   156 
       
   157     /**
       
   158      * Validate that an external file copied to a Zip file is copied with
       
   159      * the correct compression
       
   160      *
       
   161      * @param createMap           Zip FS properties to use when creating the Zip File
       
   162      * @param compression         The compression used when writing the initial entries
       
   163      * @param expectedCompression The compression to be used when copying the entry
       
   164      * @throws Exception If an error occurs
       
   165      */
       
   166     @Test(dataProvider = "copyMoveMap", enabled = true)
       
   167     public void copyFromOsTest(Map<String, String> createMap, int compression,
       
   168                            int expectedCompression) throws Exception {
       
   169 
       
   170         Path osFile = generatePath(HERE, "test", ".txt");
       
   171         Files.deleteIfExists(osFile);
       
   172         Files.writeString(osFile, OS_FILE_VALUE);
       
   173         Entry e0 = Entry.of("Entry-0", compression, ZIP_FILE_VALUE);
       
   174         Entry e1 = Entry.of("Entry-1", compression, ZIP_FILE_VALUE);
       
   175         Entry e00 = Entry.of("Entry-00", expectedCompression, OS_FILE_VALUE);
       
   176 
       
   177         Path zipFile = generatePath(HERE, "test", ".zip");
       
   178         Files.deleteIfExists(zipFile);
       
   179 
       
   180         createZipFile(zipFile, createMap, e0, e1);
       
   181         try (FileSystem zipfs = FileSystems.newFileSystem(zipFile,
       
   182                 Map.of("noCompression", expectedCompression == ZipEntry.STORED))) {
       
   183             Files.copy(osFile, zipfs.getPath(e00.name));
       
   184         }
       
   185         verify(zipFile, e0, e1, e00);
       
   186         Files.deleteIfExists(osFile);
       
   187         Files.deleteIfExists(zipFile);
       
   188     }
       
   189 
       
   190     /**
       
   191      * Validate that an entry that is copied from a Zip file to an OS file contains
       
   192      * the correct bytes and the file remains in the Zip file
       
   193      *
       
   194      * @param createMap           Zip FS properties to use when creating the Zip File
       
   195      * @param compression         The compression used when writing the initial entries
       
   196      * @param expectedCompression The compression to be used when moving the entry
       
   197      * @throws Exception If an error occurs
       
   198      */
       
   199     @Test(dataProvider = "copyMoveMap", enabled = true)
       
   200     public void CopyFromZipTest(Map<String, String> createMap, int compression,
       
   201                                 int expectedCompression) throws Exception {
       
   202 
       
   203         Entry e0 = Entry.of("Entry-0", compression, ZIP_FILE_VALUE);
       
   204         Entry e1 = Entry.of("Entry-1", compression, ZIP_FILE_VALUE);
       
   205 
       
   206         Path zipFile = generatePath(HERE, "test", ".zip");
       
   207         Path osFile = generatePath(HERE, "test", ".txt");
       
   208         Files.deleteIfExists(zipFile);
       
   209         Files.deleteIfExists(osFile);
       
   210 
       
   211         createZipFile(zipFile, createMap, e0, e1);
       
   212         try (FileSystem zipfs = FileSystems.newFileSystem(zipFile, Map.of())) {
       
   213             Files.copy(zipfs.getPath(e0.name), osFile);
       
   214         }
       
   215 
       
   216         // Entries e0 and e1 should exist
       
   217         verify(zipFile, e0, e1);
       
   218         // Check to see if the file exists and the bytes match
       
   219         assertTrue(Files.isRegularFile(osFile));
       
   220         assertEquals(Files.readAllBytes(osFile), e0.bytes);
       
   221         Files.deleteIfExists(zipFile);
       
   222         Files.deleteIfExists(osFile);
       
   223     }
       
   224 
       
   225     /**
       
   226      * Validate that an entry that is moved within a Zip file is moved with
       
   227      * the correct compression
       
   228      *
       
   229      * @param createMap           Zip FS properties to use when creating the Zip File
       
   230      * @param compression         The compression used when writing the initial entries
       
   231      * @param expectedCompression The compression to be used when moving the entry
       
   232      * @throws Exception If an error occurs
       
   233      */
       
   234     @Test(dataProvider = "copyMoveMap", enabled = true)
       
   235     public void moveTest(Map<String, String> createMap, int compression,
       
   236                          int expectedCompression) throws Exception {
       
   237 
       
   238         Entry e0 = Entry.of("Entry-0", compression, ZIP_FILE_VALUE);
       
   239         Entry e1 = Entry.of("Entry-1", compression, ZIP_FILE_VALUE);
       
   240         Entry e00 = Entry.of("Entry-00", expectedCompression, ZIP_FILE_VALUE);
       
   241 
       
   242         Path zipFile = generatePath(HERE, "test", ".zip");
       
   243         Files.deleteIfExists(zipFile);
       
   244 
       
   245         createZipFile(zipFile, createMap, e0, e1);
       
   246         try (FileSystem zipfs = FileSystems.newFileSystem(zipFile,
       
   247                 Map.of("noCompression", expectedCompression == ZipEntry.STORED))) {
       
   248             Files.move(zipfs.getPath(e0.name), zipfs.getPath(e00.name));
       
   249         }
       
   250         // Entry e0 should not exist but Entry e00 should
       
   251         verify(zipFile, e1, e00);
       
   252         Files.deleteIfExists(zipFile);
       
   253     }
       
   254 
       
   255     /**
       
   256      * Validate that an entry that is moved one Zip file to another is moved with
       
   257      * the correct compression
       
   258      *
       
   259      * @param createMap           Zip FS properties to use when creating the Zip File
       
   260      * @param compression         The compression used when writing the initial entries
       
   261      * @param expectedCompression The compression to be used when moving the entry
       
   262      * @throws Exception If an error occurs
       
   263      */
       
   264     @Test(dataProvider = "copyMoveMap", enabled = true)
       
   265     public void moveZipToZipTest(Map<String, String> createMap, int compression,
       
   266                             int expectedCompression) throws Exception {
       
   267 
       
   268         Entry e0 = Entry.of("Entry-0", compression, ZIP_FILE_VALUE);
       
   269         Entry e1 = Entry.of("Entry-1", compression, ZIP_FILE_VALUE);
       
   270         Entry e00 = Entry.of("Entry-00", expectedCompression, ZIP_FILE_VALUE);
       
   271 
       
   272         Path zipFile = generatePath(HERE, "test", ".zip");
       
   273         Path zipFile2 = generatePath(HERE, "test", ".zip");
       
   274         Files.deleteIfExists(zipFile);
       
   275         Files.deleteIfExists(zipFile2);
       
   276 
       
   277         createZipFile(zipFile, createMap, e0, e1);
       
   278         try (FileSystem zipfs = FileSystems.newFileSystem(zipFile,
       
   279                 Map.of("noCompression", expectedCompression == ZipEntry.STORED));
       
   280              FileSystem zipfsTarget = FileSystems.newFileSystem(zipFile2,
       
   281                      Map.of("create", "true", "noCompression",
       
   282                              expectedCompression == ZipEntry.STORED))) {
       
   283             Files.move(zipfs.getPath(e0.name), zipfsTarget.getPath(e00.name));
       
   284         }
       
   285         // Only Entry e00 should exist
       
   286         verify(zipFile2, e00);
       
   287         // Only Entry e1 should exist
       
   288         verify(zipFile, e1);
       
   289         Files.deleteIfExists(zipFile);
       
   290         Files.deleteIfExists(zipFile2);
       
   291     }
       
   292 
       
   293     /**
       
   294      * Validate that an entry that is moved from a Zip file to an OS file contains
       
   295      * the correct bytes and is removed from the Zip file
       
   296      *
       
   297      * @param createMap           Zip FS properties to use when creating the Zip File
       
   298      * @param compression         The compression used when writing the initial entries
       
   299      * @param expectedCompression The compression to be used when moving the entry
       
   300      * @throws Exception If an error occurs
       
   301      */
       
   302     @Test(dataProvider = "copyMoveMap", enabled = true)
       
   303     public void moveFromZipTest(Map<String, String> createMap, int compression,
       
   304                             int expectedCompression) throws Exception {
       
   305 
       
   306         Entry e0 = Entry.of("Entry-0", compression, ZIP_FILE_VALUE);
       
   307         Entry e1 = Entry.of("Entry-1", compression, ZIP_FILE_VALUE);
       
   308 
       
   309         Path zipFile = generatePath(HERE, "test", ".zip");
       
   310         Path osFile = generatePath(HERE, "test", ".txt");
       
   311         Files.deleteIfExists(zipFile);
       
   312         Files.deleteIfExists(osFile);
       
   313 
       
   314         createZipFile(zipFile, createMap, e0, e1);
       
   315         try (FileSystem zipfs = FileSystems.newFileSystem(zipFile, Map.of())) {
       
   316             Files.move(zipfs.getPath(e0.name), osFile);
       
   317         }
       
   318 
       
   319         // Only Entry e1 should exist
       
   320         verify(zipFile, e1);
       
   321         // Check to see if the file exists and the bytes match
       
   322         assertTrue(Files.isRegularFile(osFile));
       
   323         assertEquals(Files.readAllBytes(osFile), e0.bytes);
       
   324         Files.deleteIfExists(zipFile);
       
   325         Files.deleteIfExists(osFile);
       
   326     }
       
   327 
       
   328     /**
       
   329      * Validate that a FileAlreadyExistsException is thrown when copying a
       
   330      * file and not specifying the REPLACE_EXISTING option.
       
   331      *
       
   332      * @param createMap Properties used for creating the ZIP Filesystem
       
   333      * @throws Exception if an error occurs
       
   334      */
       
   335     @Test(dataProvider = "zipfsMap", enabled = true)
       
   336     public void testFAEWithCopy(Map<String, String> createMap,
       
   337                                 int compression) throws Exception {
       
   338         if (DEBUG) {
       
   339             System.out.printf("ZIP FS Map = %s%n ", formatMap(createMap));
       
   340         }
       
   341         Entry e0 = Entry.of("Entry-0", compression, ZIP_FILE_VALUE);
       
   342         Entry e1 = Entry.of("Entry-1", compression, ZIP_FILE_VALUE);
       
   343 
       
   344         Path zipFile = generatePath(HERE, "test", ".zip");
       
   345         Files.deleteIfExists(zipFile);
       
   346 
       
   347         createZipFile(zipFile, createMap, e0, e1);
       
   348         try (FileSystem zipfs =
       
   349                      FileSystems.newFileSystem(zipFile, createMap)) {
       
   350             assertThrows(FileAlreadyExistsException.class, () ->
       
   351                     Files.copy(zipfs.getPath(e0.name),
       
   352                             zipfs.getPath(e1.name)));
       
   353         }
       
   354         Files.deleteIfExists(zipFile);
       
   355     }
       
   356 
       
   357     /**
       
   358      * Generate a temporary file Path
       
   359      *
       
   360      * @param dir    Directory used to create the path
       
   361      * @param prefix The prefix string used to create the path
       
   362      * @param suffix The suffix string used to create the path
       
   363      * @return Path that was generated
       
   364      */
       
   365     private static Path generatePath(Path dir, String prefix, String suffix) {
       
   366         long n = random.nextLong();
       
   367         String s = prefix + Long.toUnsignedString(n) + suffix;
       
   368         Path name = dir.getFileSystem().getPath(s);
       
   369         // the generated name should be a simple file name
       
   370         if (name.getParent() != null)
       
   371             throw new IllegalArgumentException("Invalid prefix or suffix");
       
   372         return dir.resolve(name);
       
   373     }
       
   374 
       
   375     /**
       
   376      * Utility method to return a formatted String of the key:value entries for
       
   377      * a Map
       
   378      *
       
   379      * @param env Map to format
       
   380      * @return Formatted string of the Map entries
       
   381      */
       
   382     private static String formatMap(Map<String, String> env) {
       
   383         return env.entrySet().stream()
       
   384                 .map(e -> format("(%s:%s)", e.getKey(), e.getValue()))
       
   385                 .collect(joining(", "));
       
   386     }
       
   387 
       
   388     /**
       
   389      * Create a Zip file using the Zip File System with the specified
       
   390      * Zip File System properties
       
   391      *
       
   392      * @param zipFile Path to the Zip File to create
       
   393      * @param env     Properties used for creating the Zip Filesystem
       
   394      * @param entries The entries to add to the Zip File
       
   395      * @throws IOException If an error occurs while creating the Zip file
       
   396      */
       
   397     private void createZipFile(Path zipFile, Map<String, String> env,
       
   398                                Entry... entries) throws IOException {
       
   399         if (DEBUG) {
       
   400             System.out.printf("Creating Zip file: %s with the Properties: %s%n",
       
   401                     zipFile, formatMap(env));
       
   402         }
       
   403         try (FileSystem zipfs =
       
   404                      FileSystems.newFileSystem(zipFile, env)) {
       
   405             for (Entry e : entries) {
       
   406                 Files.writeString(zipfs.getPath(e.name), new String(e.bytes));
       
   407             }
       
   408         }
       
   409     }
       
   410 
       
   411     /**
       
   412      * Represents an entry in a Zip file. An entry encapsulates a name, a
       
   413      * compression method, and its contents/data.
       
   414      */
       
   415     static class Entry {
       
   416         private final String name;
       
   417         private final int method;
       
   418         private final byte[] bytes;
       
   419 
       
   420         Entry(String name, int method, String contents) {
       
   421             this.name = name;
       
   422             this.method = method;
       
   423             this.bytes = contents.getBytes(StandardCharsets.UTF_8);
       
   424         }
       
   425 
       
   426         static Entry of(String name, int method, String contents) {
       
   427             return new Entry(name, method, contents);
       
   428         }
       
   429 
       
   430         /**
       
   431          * Returns a new Entry with the same name and compression method as this
       
   432          * Entry but with the given content.
       
   433          */
       
   434         Entry content(String contents) {
       
   435             return new Entry(name, method, contents);
       
   436         }
       
   437     }
       
   438 
       
   439     /**
       
   440      * Verify that the given path is a Zip file containing exactly the
       
   441      * given entries.
       
   442      */
       
   443     private static void verify(Path zipfile, Entry... entries) throws IOException {
       
   444         // check entries with zip API
       
   445         try (ZipFile zf = new ZipFile(zipfile.toFile())) {
       
   446             // check entry count
       
   447             assertEquals(entries.length, zf.size());
       
   448 
       
   449             // check compression method and content of each entry
       
   450             for (Entry e : entries) {
       
   451                 ZipEntry ze = zf.getEntry(e.name);
       
   452                 //System.out.printf("Name: %s, method: %s, Expected Method: %s%n", e.name, ze.getMethod(), e.method);
       
   453                 assertNotNull(ze);
       
   454                 assertEquals(e.method, ze.getMethod());
       
   455                 try (InputStream in = zf.getInputStream(ze)) {
       
   456                     byte[] bytes = in.readAllBytes();
       
   457                     assertTrue(Arrays.equals(bytes, e.bytes));
       
   458                 }
       
   459             }
       
   460         }
       
   461 
       
   462         // check entries with FileSystem API
       
   463         try (FileSystem fs = FileSystems.newFileSystem(zipfile)) {
       
   464             // check entry count
       
   465             Path top = fs.getPath("/");
       
   466             long count = Files.find(top, Integer.MAX_VALUE,
       
   467                     (path, attrs) -> attrs.isRegularFile()).count();
       
   468             assertEquals(entries.length, count);
       
   469 
       
   470             // check content of each entry
       
   471             for (Entry e : entries) {
       
   472                 Path file = fs.getPath(e.name);
       
   473                 byte[] bytes = Files.readAllBytes(file);
       
   474                 assertTrue(Arrays.equals(bytes, e.bytes));
       
   475             }
       
   476         }
       
   477     }
       
   478 }