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.*; 26 27 import java.io.IOException; 28 import java.io.InputStream; 29 import java.nio.charset.StandardCharsets; 30 import java.nio.file.FileSystem; 31 import java.nio.file.FileSystems; 32 import java.nio.file.Files; 33 import java.nio.file.Path; 34 import java.security.SecureRandom; 35 import java.util.Arrays; 36 import java.util.HashMap; 37 import java.util.Map; 38 import java.util.zip.ZipEntry; 39 import java.util.zip.ZipFile; 40 41 import static java.lang.String.format; 42 import static java.util.stream.Collectors.joining; 43 import static org.testng.Assert.*; 44 45 /** 46 * @test 47 * @bug 8231093 48 * @summary Test Zip FS compressionMethod property 49 * @modules jdk.zipfs 50 * @run testng CompressionModeTest 51 */ 52 public class CompressionModeTest { 53 54 private static final Path HERE = Path.of("."); 55 56 /** 57 * Number of ZIP entries to create 58 */ 59 private static final int ENTRIES = 5; 60 61 /** 62 * Value used for creating the required entries in a ZIP or JAR file 63 */ 64 private static final String ZIP_FILE_VALUE = "US Open 2019"; 65 private static final byte[] ZIP_FILE_ENTRY = 66 ZIP_FILE_VALUE.getBytes(StandardCharsets.UTF_8); 67 68 private static final SecureRandom random = new SecureRandom(); 69 70 /** 71 * Validate that you can create a ZIP file with and without compression 72 * and that entries are created with the specified compression method. 73 * 74 * @param env Properties used for creating the ZIP Filesystem 75 * @param compression Indicates whether the files are DEFLATED(default) 76 * or STORED 77 * @throws Exception If an error occurs during the creation, verification or 78 * deletion of the ZIP file 79 */ 80 @Test(dataProvider = "validCompressionMethods", enabled = true) 81 public void testValidCompressionMehods(Map<String, String> env, 82 int compression) throws Exception { 83 84 System.out.printf("ZIP FS Map = %s, Compression mode= %s%n ", 85 formatMap(env), compression); 86 87 Path zipfile = generatePath(HERE, "test", ".zip"); 88 Files.deleteIfExists(zipfile); 89 createZipFile(zipfile, env, ENTRIES); 90 verify(zipfile, compression, ENTRIES, 0); 91 Files.deleteIfExists(zipfile); 92 } 93 94 /** 95 * Validate that an IllegalArgumentException is thrown when an invalid 96 * value is specified for the compressionMethod property. 97 * 98 * @param env Properties used for creating the ZIP Filesystem 99 * @throws Exception if an error occurs other than the expected 100 * IllegalArgumentException 101 */ 102 @Test(dataProvider = "invalidCompressionMethod") 103 public void testInvalidCompressionMethod(Map<String, String> env) throws Exception { 104 System.out.printf("ZIP FS Map = %s%n ", formatMap(env)); 105 Path zipfile = generatePath(HERE, "test", ".zip"); 106 Files.deleteIfExists(zipfile); 107 assertThrows(IllegalArgumentException.class, () -> 108 createZipFile(zipfile, env, ENTRIES)); 109 Files.deleteIfExists(zipfile); 110 } 111 112 /** 113 * Create a ZIP File System using the specified properties and a ZIP file 114 * with the specified number of entries 115 * 116 * @param zipFile Path to the ZIP File to create 117 * @param env Properties used for creating the ZIP Filesystem 118 * @param entries Number of entries to add to the ZIP File 119 * @throws IOException If an error occurs while creating the ZIP file 120 */ 121 private void createZipFile(Path zipFile, Map<String, String> env, 122 int entries) throws IOException { 123 System.out.printf("Creating file = %s%n", zipFile); 124 try (FileSystem zipfs = 125 FileSystems.newFileSystem(zipFile, env)) { 126 127 for (int i = 0; i < entries; i++) { 128 Files.writeString(zipfs.getPath("Entry-" + i), ZIP_FILE_VALUE); 129 } 130 } 131 } 132 133 /** 134 * DataProvider used to validate that you can create a ZIP file with and 135 * without compression. 136 */ 137 @DataProvider(name = "validCompressionMethods") 138 private Object[][] validCompressionMethods() { 139 return new Object[][]{ 140 {Map.of("create", "true"), ZipEntry.DEFLATED}, 141 {Map.of("create", "true", "noCompression", "true"), 142 ZipEntry.STORED}, 143 {Map.of("create", "true", "noCompression", "false"), 144 ZipEntry.DEFLATED}, 145 {Map.of("create", "true", "compressionMethod", "STORED"), 146 ZipEntry.STORED}, 147 {Map.of("create", "true", "compressionMethod", "DEFLATED"), 148 ZipEntry.DEFLATED}, 149 {Map.of("create", "true", "compressionMethod", "stored"), 150 ZipEntry.STORED}, 151 {Map.of("create", "true", "compressionMethod", "deflated"), 152 ZipEntry.DEFLATED} 153 154 }; 155 } 156 157 /** 158 * DataProvider used to validate that an IllegalArgumentException is thrown 159 * for an invalid value for the compressionMethod property. 160 */ 161 @DataProvider(name = "invalidCompressionMethod") 162 private Object[][] invalidCompressionMethod() { 163 HashMap<String, String> map = new HashMap<>(); 164 map.put("create", "true"); 165 map.put("compressionMethod", null); 166 return new Object[][]{ 167 {map}, 168 {Map.of("create", "true", "compressionMethod", "")}, 169 {Map.of("create", "true", "compressionMethod", "invalid")} 170 171 }; 172 } 173 174 /** 175 * Verify that the given path is a ZIP file containing the 176 * expected entries. 177 * 178 * @param zipfile ZIP file to be validated 179 * @param method Expected Compression method: STORED or DEFLATED 180 * @param entries Number of expected entries 181 * @param start Starting number for verifying entries 182 * @throws Exception If an error occurs while examining the ZIP file 183 */ 184 private static void verify(Path zipfile, int method, int entries, 185 int start) throws Exception { 186 // check entries with ZIP API 187 try (ZipFile zf = new ZipFile(zipfile.toFile())) { 188 // check entry count 189 assertEquals(entries, zf.size()); 190 191 // check compression method and content of each entry 192 for (int i = start; i < entries; i++) { 193 ZipEntry ze = zf.getEntry("Entry-" + i); 194 assertNotNull(ze); 195 assertEquals(method, ze.getMethod()); 196 try (InputStream is = zf.getInputStream(ze)) { 197 byte[] bytes = is.readAllBytes(); 198 assertTrue(Arrays.equals(bytes, ZIP_FILE_ENTRY)); 199 } 200 } 201 } 202 // check entries with FileSystem API 203 try (FileSystem fs = FileSystems.newFileSystem(zipfile)) { 204 205 // check entry count 206 Path top = fs.getPath("/"); 207 long count = Files.find(top, Integer.MAX_VALUE, (path, attrs) -> 208 attrs.isRegularFile() || (attrs.isDirectory() && 209 path.getFileName() != null && 210 path.getFileName().toString().equals("META-INF"))) 211 .count(); 212 assertEquals(entries, count); 213 214 // check content of each entry 215 for (int i = start; i < entries; i++) { 216 Path file = fs.getPath("Entry-" + i); 217 byte[] bytes = Files.readAllBytes(file); 218 assertTrue(Arrays.equals(bytes, ZIP_FILE_ENTRY)); 219 } 220 } 221 } 222 223 /** 224 * Generate a temporary file Path 225 * 226 * @param dir Directory used to create the path 227 * @param prefix The prefix string used to create the path 228 * @param suffix The suffix string used to create the path 229 * @return Path that was generated 230 */ 231 private static Path generatePath(Path dir, String prefix, String suffix) { 232 long n = random.nextLong(); 233 String s = prefix + Long.toUnsignedString(n) + suffix; 234 Path name = dir.getFileSystem().getPath(s); 235 // the generated name should be a simple file name 236 if (name.getParent() != null) 237 throw new IllegalArgumentException("Invalid prefix or suffix"); 238 return dir.resolve(name); 239 } 240 241 /** 242 * Utility method to return a formatted String of the key:value entries for 243 * a Map 244 * 245 * @param env Map to format 246 * @return Formatted string of the Map entries 247 */ 248 private static String formatMap(Map<String, String> env) { 249 return env.entrySet().stream() 250 .map(e -> format("(%s:%s)", e.getKey(), e.getValue())) 251 .collect(joining(", ")); 252 } 253 }