8211917: Zip FS should add META-INF/MANIFEST.FS at the start of the Zip/JAR
Reviewed-by: clanger, jpai
This commit is contained in:
parent
db91be2ee1
commit
3607ddd55a
@ -1718,8 +1718,33 @@ class ZipFileSystem extends FileSystem {
|
||||
byte[] buf = null;
|
||||
Entry e;
|
||||
|
||||
final IndexNode manifestInode = inodes.get(
|
||||
IndexNode.keyOf(getBytes("/META-INF/MANIFEST.MF")));
|
||||
final Iterator<IndexNode> inodeIterator = inodes.values().iterator();
|
||||
boolean manifestProcessed = false;
|
||||
|
||||
// write loc
|
||||
for (IndexNode inode : inodes.values()) {
|
||||
while (inodeIterator.hasNext()) {
|
||||
final IndexNode inode;
|
||||
|
||||
// write the manifest inode (if any) first so that
|
||||
// java.util.jar.JarInputStream can find it
|
||||
if (manifestInode == null) {
|
||||
inode = inodeIterator.next();
|
||||
} else {
|
||||
if (manifestProcessed) {
|
||||
// advance to next node, filtering out the manifest
|
||||
// which was already written
|
||||
inode = inodeIterator.next();
|
||||
if (inode == manifestInode) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
inode = manifestInode;
|
||||
manifestProcessed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (inode instanceof Entry) { // an updated inode
|
||||
e = (Entry)inode;
|
||||
try {
|
||||
|
4
test/jdk/jdk/nio/zipfs/testng/TEST.properties
Normal file
4
test/jdk/jdk/nio/zipfs/testng/TEST.properties
Normal file
@ -0,0 +1,4 @@
|
||||
# Zip FS unit tests uses TestNG
|
||||
modules = jdk.zipfs
|
||||
TestNG.dirs = .
|
||||
|
528
test/jdk/jdk/nio/zipfs/testng/test/ManifestOrderTest.java
Normal file
528
test/jdk/jdk/nio/zipfs/testng/test/ManifestOrderTest.java
Normal file
@ -0,0 +1,528 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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.
|
||||
*
|
||||
*/
|
||||
package test;
|
||||
|
||||
import org.testng.annotations.BeforeSuite;
|
||||
import org.testng.annotations.Test;
|
||||
import util.ZipFsBaseTest;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.jar.*;
|
||||
import java.util.jar.Attributes.Name;
|
||||
import java.util.spi.ToolProvider;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8211917
|
||||
* @summary Validate that Zip FS will always add META-INF/MANIFEST.MF to the
|
||||
* beginning of a Zip file allowing the Manifest be found and processed
|
||||
* by java.util.jar.JarInputStream.
|
||||
|
||||
*/
|
||||
public class ManifestOrderTest extends ZipFsBaseTest {
|
||||
|
||||
// Manifest PATH within a JAR
|
||||
public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
|
||||
|
||||
// Manifests used by the tests
|
||||
private static String MANIFEST_ENTRY;
|
||||
private static String MANIFEST_ENTRY2;
|
||||
|
||||
// Manifest Attributes used by the tests
|
||||
private static Map<Name, String> MANIFEST_ATTRS;
|
||||
private static Map<Name, String> MANIFEST_ATTRS2;
|
||||
|
||||
// Used when the test does not expect to find a Manifest
|
||||
private static final Map<Name, String> NO_ATTRIBUTES = Map.of();
|
||||
|
||||
// JAR Tool via ToolProvider API
|
||||
private static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar")
|
||||
.orElseThrow(() -> new RuntimeException("jar tool not found")
|
||||
);
|
||||
|
||||
/**
|
||||
* Create the Manifests and Map of attributes included in the Manifests
|
||||
*/
|
||||
@BeforeSuite
|
||||
public void setup() {
|
||||
String jdkVendor = System.getProperty("java.vendor");
|
||||
String jdkVersion = System.getProperty("java.version");
|
||||
String attributeKey = "Player";
|
||||
String attributeValue = "Rafael Nadal";
|
||||
String attributeKey2 = "Country";
|
||||
String attributeValue2 = "Spain";
|
||||
String jdkVendorVersion = jdkVersion + " (" + jdkVendor + ")";
|
||||
MANIFEST_ENTRY = "Manifest-Version: 1.0"
|
||||
+ System.lineSeparator()
|
||||
+ "Created-By: " + jdkVendorVersion
|
||||
+ System.lineSeparator()
|
||||
+ attributeKey + ": " + attributeValue
|
||||
+ System.lineSeparator();
|
||||
MANIFEST_ENTRY2 = MANIFEST_ENTRY
|
||||
+ attributeKey2 + ": " + attributeValue2
|
||||
+ System.lineSeparator();
|
||||
|
||||
MANIFEST_ATTRS =
|
||||
Map.of(Name.MANIFEST_VERSION, "1.0",
|
||||
new Name("Created-By"), jdkVendorVersion,
|
||||
new Name(attributeKey), attributeValue);
|
||||
MANIFEST_ATTRS2 = new HashMap<>();
|
||||
MANIFEST_ATTRS.forEach(MANIFEST_ATTRS2::put);
|
||||
MANIFEST_ATTRS2.put(new Name(attributeKey2), attributeValue2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that JarInputStream can find META-INF/MANIFEST.MF when its written
|
||||
* as the first entry within a JAR using Zip FS
|
||||
*
|
||||
* @param env Zip FS properties to use when creating the Zip File
|
||||
* @param compression The compression used when writing the entries
|
||||
* @throws Exception If an error occurs
|
||||
*/
|
||||
@Test(dataProvider = "zipfsMap")
|
||||
public void testJarWithManifestAddedFirst(final Map<String, String> env,
|
||||
final int compression)
|
||||
throws Exception {
|
||||
final Path jarPath = generatePath(HERE, "test", ".jar");
|
||||
Files.deleteIfExists(jarPath);
|
||||
// Create the initial JAR writing out the Manifest first
|
||||
final Entry[] entries = newEntries(compression);
|
||||
Entry manifest = Entry.of(MANIFEST_NAME, compression, MANIFEST_ENTRY);
|
||||
zip(jarPath, env, manifest, entries[0], entries[1], entries[2]);
|
||||
verify(jarPath, MANIFEST_ATTRS, compression, entries);
|
||||
|
||||
// Add an additional entry and re-verify
|
||||
Entry e00 = Entry.of("Entry-00", compression, "Roger Federer");
|
||||
zip(jarPath, Map.of("noCompression", compression == ZipEntry.STORED),
|
||||
e00);
|
||||
verify(jarPath, MANIFEST_ATTRS, compression, entries[0], entries[1],
|
||||
entries[2], e00);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that JarInputStream can find META-INF/MANIFEST.MF when its written
|
||||
* as the last entry within a JAR using Zip FS
|
||||
*
|
||||
* @param env Zip FS properties to use when creating the Zip File
|
||||
* @param compression The compression used when writing the entries
|
||||
* @throws Exception If an error occurs
|
||||
*/
|
||||
@Test(dataProvider = "zipfsMap")
|
||||
public void testJarWithManifestAddedLast(final Map<String, String> env,
|
||||
final int compression) throws Exception {
|
||||
final Path jarPath = generatePath(HERE, "test", ".jar");
|
||||
Files.deleteIfExists(jarPath);
|
||||
// Create the initial JAR writing out the Manifest last
|
||||
final Entry[] entries = newEntries(compression);
|
||||
Entry manifest = Entry.of(MANIFEST_NAME, compression, MANIFEST_ENTRY);
|
||||
zip(jarPath, env, entries[0], entries[1], entries[2], manifest);
|
||||
verify(jarPath, MANIFEST_ATTRS, compression, entries);
|
||||
|
||||
// Add an additional entry and re-verify
|
||||
Entry e00 = Entry.of("Entry-00", compression, "Roger Federer");
|
||||
zip(jarPath, Map.of("noCompression", compression == ZipEntry.STORED),
|
||||
e00);
|
||||
verify(jarPath, MANIFEST_ATTRS, compression, entries[0], entries[1],
|
||||
entries[2], e00);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that JarInputStream can find META-INF/MANIFEST.MF when its written
|
||||
* between other entries within a JAR using Zip FS
|
||||
*
|
||||
* @param env Zip FS properties to use when creating the Zip File
|
||||
* @param compression The compression used when writing the entries
|
||||
* @throws Exception If an error occurs
|
||||
*/
|
||||
@Test(dataProvider = "zipfsMap")
|
||||
public void testJarWithManifestAddedInBetween(final Map<String, String> env,
|
||||
final int compression)
|
||||
throws Exception {
|
||||
final Path jarPath = generatePath(HERE, "test", ".jar");
|
||||
Files.deleteIfExists(jarPath);
|
||||
// Create the initial JAR writing out the Manifest in between other entries
|
||||
final Entry[] entries = newEntries(compression);
|
||||
Entry manifest = Entry.of(MANIFEST_NAME, compression, MANIFEST_ENTRY);
|
||||
zip(jarPath, env, entries[0], entries[1], manifest, entries[2]);
|
||||
verify(jarPath, MANIFEST_ATTRS, compression, entries);
|
||||
|
||||
// Add an additional entry and re-verify
|
||||
Entry e00 = Entry.of("Entry-00", compression, "Roger Federer");
|
||||
zip(jarPath, Map.of("noCompression", compression == ZipEntry.STORED),
|
||||
e00);
|
||||
verify(jarPath, MANIFEST_ATTRS, compression, entries[0], entries[1],
|
||||
entries[2], e00);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that JarInputStream can read all entries from a JAR created
|
||||
* using Zip FS without adding a Manifest
|
||||
*
|
||||
* @param env Zip FS properties to use when creating the Zip File
|
||||
* @param compression The compression used when writing the entries
|
||||
* @throws Exception If an error occurs
|
||||
*/
|
||||
@Test(dataProvider = "zipfsMap")
|
||||
public void testJarWithNoManifest(final Map<String, String> env,
|
||||
final int compression) throws Exception {
|
||||
final Path jarPath = generatePath(HERE, "test", ".jar");
|
||||
Files.deleteIfExists(jarPath);
|
||||
// Create the initial JAR writing without a Manifest
|
||||
final Entry[] entries = newEntries(compression);
|
||||
zip(jarPath, env, entries[0], entries[1], entries[2]);
|
||||
verify(jarPath, NO_ATTRIBUTES, compression, entries);
|
||||
|
||||
// Add an additional entry and re-verify
|
||||
Entry e00 = Entry.of("Entry-00", compression, "Roger Federer");
|
||||
zip(jarPath, Map.of("noCompression", compression == ZipEntry.STORED),
|
||||
e00);
|
||||
verify(jarPath, NO_ATTRIBUTES, compression, entries[0], entries[1],
|
||||
entries[2], e00);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that JarInputStream can read META-INF/MANIFEST.MF when the
|
||||
* the Manfiest is copied to the JAR using Files::copy
|
||||
*
|
||||
* @param env Zip FS properties to use when creating the Zip File
|
||||
* @param compression The compression used when writing the entries
|
||||
* @throws Exception If an error occurs
|
||||
*/
|
||||
@Test(dataProvider = "zipfsMap")
|
||||
public void testManifestCopiedFromOSFile(final Map<String, String> env,
|
||||
final int compression) throws Exception {
|
||||
final Path jarPath = generatePath(HERE, "test", ".jar");
|
||||
Files.deleteIfExists(jarPath);
|
||||
final Path manifest = Paths.get(".", "test-MANIFEST.MF");
|
||||
Files.deleteIfExists(manifest);
|
||||
|
||||
// Create initial JAR without a Manifest
|
||||
Files.writeString(manifest, MANIFEST_ENTRY);
|
||||
final Entry[] entries = newEntries(compression);
|
||||
zip(jarPath, env, entries[0], entries[1], entries[2]);
|
||||
verify(jarPath, NO_ATTRIBUTES, compression, entries);
|
||||
|
||||
// Add the Manifest via Files::copy and verify
|
||||
try (FileSystem zipfs =
|
||||
FileSystems.newFileSystem(jarPath, env)) {
|
||||
Files.copy(manifest, zipfs.getPath("META-INF", "MANIFEST.MF"));
|
||||
}
|
||||
verify(jarPath, MANIFEST_ATTRS, compression, entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that JarInputStream can find META-INF/MANIFEST.MF when the
|
||||
* entries are copied from one JAR to another JAR using Zip FS and Files::copy
|
||||
*
|
||||
* @param env Zip FS properties to use when creating the Zip File
|
||||
* @param compression The compression used when writing the entries
|
||||
* @throws Exception If an error occurs
|
||||
*/
|
||||
@Test(dataProvider = "zipfsMap")
|
||||
public void copyJarToJarTest(final Map<String, String> env, final int compression)
|
||||
throws Exception {
|
||||
final Path jarPath = generatePath(HERE, "test", ".jar");
|
||||
final Path jarPath2 = generatePath(HERE, "test", ".jar");
|
||||
Files.deleteIfExists(jarPath);
|
||||
Files.deleteIfExists(jarPath2);
|
||||
|
||||
// Create initial JAR with a Manifest
|
||||
final Entry[] entries = newEntries(compression);
|
||||
Entry manifest = Entry.of(MANIFEST_NAME, compression, MANIFEST_ENTRY);
|
||||
zip(jarPath, env, manifest, entries[0], entries[1], entries[2]);
|
||||
verify(jarPath, MANIFEST_ATTRS, compression, entries);
|
||||
|
||||
// Create the another JAR via Files::copy and verify
|
||||
try (FileSystem zipfs = FileSystems.newFileSystem(jarPath, env);
|
||||
FileSystem zipfsTarget = FileSystems.newFileSystem(jarPath2,
|
||||
Map.of("create", "true", "noCompression",
|
||||
compression == ZipEntry.STORED))) {
|
||||
Path mPath = zipfsTarget.getPath(manifest.name);
|
||||
if (mPath.getParent() != null) {
|
||||
Files.createDirectories(mPath.getParent());
|
||||
}
|
||||
Files.copy(zipfs.getPath(manifest.name), mPath);
|
||||
for (Entry e : entries) {
|
||||
Path target = zipfsTarget.getPath(e.name);
|
||||
if (target.getParent() != null) {
|
||||
Files.createDirectories(target.getParent());
|
||||
}
|
||||
Files.copy(zipfs.getPath(e.name), target);
|
||||
}
|
||||
}
|
||||
verify(jarPath2, MANIFEST_ATTRS, compression, entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a JAR created using the jar tool and is updated using Zip FS
|
||||
* contains the expected entries and Manifest
|
||||
*
|
||||
* @param compression The compression used when writing the entries
|
||||
* @throws Exception If an error occurs
|
||||
*/
|
||||
@Test(dataProvider = "compressionMethods")
|
||||
public void testJarToolGeneratedJarWithManifest(final int compression)
|
||||
throws Exception {
|
||||
|
||||
final Path jarPath = generatePath(HERE, "test", ".jar");
|
||||
Files.deleteIfExists(jarPath);
|
||||
final Entry[] entries = newEntries(compression);
|
||||
final Path tmpdir = Paths.get("tmp");
|
||||
rmdir(tmpdir);
|
||||
Files.createDirectory(tmpdir);
|
||||
// Create a directory to hold the files to bad added to the JAR
|
||||
for (final Entry entry : entries) {
|
||||
Path p = Path.of("tmp", entry.name);
|
||||
if (p.getParent() != null) {
|
||||
Files.createDirectories(p.getParent());
|
||||
}
|
||||
Files.write(Path.of("tmp", entry.name), entry.bytes);
|
||||
}
|
||||
// Create a file containing the Manifest
|
||||
final Path manifestFile = Paths.get(".", "test-jar-MANIFEST.MF");
|
||||
Files.deleteIfExists(manifestFile);
|
||||
Files.writeString(manifestFile, MANIFEST_ENTRY);
|
||||
|
||||
// Create a JAR via the jar tool and verify
|
||||
final int exitCode = JAR_TOOL.run(System.out, System.err, "cvfm"
|
||||
+ (compression == ZipEntry.STORED ? "0" : ""),
|
||||
jarPath.getFileName().toString(),
|
||||
manifestFile.toAbsolutePath().toString(),
|
||||
"-C", tmpdir.toAbsolutePath().toString(), ".");
|
||||
assertEquals(exitCode, 0, "jar tool exited with failure");
|
||||
verify(jarPath, MANIFEST_ATTRS, compression, entries);
|
||||
|
||||
// Add an additional entry and re-verify
|
||||
Entry e00 = Entry.of("Entry-00", compression, "Roger Federer");
|
||||
zip(jarPath, Map.of("noCompression", compression == ZipEntry.STORED),
|
||||
entries[0], entries[1], e00, entries[2]);
|
||||
verify(jarPath, MANIFEST_ATTRS, compression, entries[0], entries[1],
|
||||
e00, entries[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that JarInputStream can read all entries from a JAR created
|
||||
* using Zip FS with a Manifest created by java.util.jar.Manifest
|
||||
*
|
||||
* @param env Zip FS properties to use when creating the Zip File
|
||||
* @param compression The compression used when writing the entries
|
||||
* @throws Exception If an error occurs
|
||||
*/
|
||||
@Test(dataProvider = "zipfsMap")
|
||||
public void createWithManifestTest(final Map<String, String> env,
|
||||
final int compression) throws Exception {
|
||||
Path jarPath = generatePath(HERE, "test", ".jar");
|
||||
Files.deleteIfExists(jarPath);
|
||||
|
||||
// Create the JAR and verify
|
||||
Entry e00 = Entry.of("Entry-00", compression, "Indian Wells");
|
||||
try (FileSystem zipfs = FileSystems.newFileSystem(jarPath, env)) {
|
||||
// Add our Manifest using Manifest::write
|
||||
Path manifestPath = zipfs.getPath("META-INF", "MANIFEST.MF");
|
||||
if (manifestPath.getParent() != null) {
|
||||
Files.createDirectories(manifestPath.getParent());
|
||||
}
|
||||
try (final OutputStream os = Files.newOutputStream(manifestPath)) {
|
||||
final Manifest manifest = new Manifest();
|
||||
final Attributes attributes = manifest.getMainAttributes();
|
||||
// Populate the Manifest Attributes
|
||||
MANIFEST_ATTRS.forEach(attributes::put);
|
||||
manifest.write(os);
|
||||
}
|
||||
Files.write(zipfs.getPath(e00.name), e00.bytes);
|
||||
|
||||
}
|
||||
verify(jarPath, MANIFEST_ATTRS, compression, e00);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that JarInputStream can find META-INF/MANIFEST.MF when it has
|
||||
* been updated by Zip FS
|
||||
*
|
||||
* @param env Zip FS properties to use when creating the Zip File
|
||||
* @param compression The compression used when writing the entries
|
||||
* @throws Exception If an error occurs
|
||||
*/
|
||||
@Test(dataProvider = "zipfsMap")
|
||||
public void updateManifestTest(final Map<String, String> env,
|
||||
final int compression) throws Exception {
|
||||
final Path jarPath = generatePath(HERE, "test", ".jar");
|
||||
Files.deleteIfExists(jarPath);
|
||||
// Create the initial JAR with a Manifest
|
||||
final Entry[] entries = newEntries(compression);
|
||||
Entry manifest = Entry.of(MANIFEST_NAME, compression, MANIFEST_ENTRY);
|
||||
zip(jarPath, env, manifest, entries[0], entries[1], entries[2]);
|
||||
verify(jarPath, MANIFEST_ATTRS, compression, entries);
|
||||
|
||||
// Add an additional entry, update the Manifest and re-verify
|
||||
Entry e00 = Entry.of("Entry-00", compression, "Roger Federer");
|
||||
Entry revisedManifest = manifest.content(MANIFEST_ENTRY2);
|
||||
zip(jarPath, Map.of("noCompression", compression == ZipEntry.STORED),
|
||||
revisedManifest, e00);
|
||||
verify(jarPath, MANIFEST_ATTRS2, compression, entries[0], entries[1],
|
||||
entries[2], e00);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an Entry array used when creating a JAR using Zip FS
|
||||
*
|
||||
* @param compressionMethod The compression method used by the test
|
||||
* @return the Entry array
|
||||
*/
|
||||
private static Entry[] newEntries(final int compressionMethod) {
|
||||
return new Entry[]{new Entry("hello.txt", compressionMethod, "hello"),
|
||||
new Entry("META-INF/bar/world.txt", compressionMethod, "world"),
|
||||
new Entry("META-INF/greeting.txt", compressionMethod, "greeting")};
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the entries including the Manifest in a JAR
|
||||
*
|
||||
* @param jar Path to the JAR
|
||||
* @param attributes A Map containing the attributes expected in the Manifest;
|
||||
* otherwise empty
|
||||
* @param entries Entries to validate in the JAR
|
||||
* @param compression The compression used when writing the entries
|
||||
* @throws Exception If an error occurs
|
||||
*/
|
||||
private static void verify(final Path jar, final Map<?, ?> attributes,
|
||||
final int compression, Entry... entries)
|
||||
throws Exception {
|
||||
// If the compression method is not STORED, then use JarInputStream
|
||||
// to validate the entries in the JAR. The current JAR/ZipInputStream
|
||||
// implementation supports PKZIP version 2.04g which only supports
|
||||
// bit 8 for DEFLATED entries(JDK-8143613). Zip FS will set this bit
|
||||
// for STORED entries as is now allowed by the PKZIP spec, resulting
|
||||
// in the error "only DEFLATED entries can have EXT descriptor"
|
||||
if (ZipEntry.STORED != compression) {
|
||||
try (final JarInputStream jis =
|
||||
new JarInputStream(Files.newInputStream(jar))) {
|
||||
// Verify the Manifest
|
||||
validateManifest(attributes, jis.getManifest());
|
||||
// Verify the rest of the expected entries are present
|
||||
final Map<String, Entry> expected = Arrays.stream(entries)
|
||||
.collect(Collectors.toMap(entry -> entry.name, entry -> entry));
|
||||
JarEntry je = jis.getNextJarEntry();
|
||||
assertNotNull(je, "Jar is empty");
|
||||
while (je != null) {
|
||||
if (je.isDirectory()) {
|
||||
// skip directories
|
||||
je = jis.getNextJarEntry();
|
||||
continue;
|
||||
}
|
||||
final Entry e = expected.remove(je.getName());
|
||||
assertNotNull(e, "Unexpected entry in jar ");
|
||||
assertEquals(je.getMethod(), e.method, "Compression method mismatch");
|
||||
assertEquals(jis.readAllBytes(), e.bytes);
|
||||
|
||||
je = jis.getNextJarEntry();
|
||||
}
|
||||
assertEquals(expected.size(), 0, "Missing entries in jar!");
|
||||
}
|
||||
}
|
||||
|
||||
// Verify using JarFile
|
||||
try (final JarFile jf = new JarFile(jar.toFile())) {
|
||||
// Validate Manifest
|
||||
validateManifest(attributes, jf.getManifest());
|
||||
for (Entry e : entries) {
|
||||
JarEntry je = jf.getJarEntry(e.name);
|
||||
assertNotNull(je, "Entry does not exist");
|
||||
if (DEBUG) {
|
||||
System.out.printf("Entry Name: %s, method: %s, Expected Method: %s%n",
|
||||
e.name, je.getMethod(), e.method);
|
||||
}
|
||||
assertEquals(e.method, je.getMethod(), "Compression methods mismatch");
|
||||
try (InputStream in = jf.getInputStream(je)) {
|
||||
byte[] bytes = in.readAllBytes();
|
||||
if (DEBUG) {
|
||||
System.out.printf("bytes= %s, actual=%s%n",
|
||||
new String(bytes), new String(e.bytes));
|
||||
}
|
||||
assertTrue(Arrays.equals(bytes, e.bytes), "Entries do not match");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check entries with FileSystem API
|
||||
try (FileSystem fs = FileSystems.newFileSystem(jar)) {
|
||||
// Check entry count
|
||||
Path top = fs.getPath("/");
|
||||
long count = Files.find(top, Integer.MAX_VALUE,
|
||||
(path, attrs) -> attrs.isRegularFile()).count();
|
||||
assertEquals(entries.length + (!attributes.isEmpty() ? 1 : 0), count);
|
||||
Path mf = fs.getPath("META-INF", "MANIFEST.MF");
|
||||
Manifest m = null;
|
||||
|
||||
if (!attributes.isEmpty()) {
|
||||
assertTrue(Files.exists(mf));
|
||||
m = new Manifest(Files.newInputStream(mf));
|
||||
}
|
||||
validateManifest(attributes, m);
|
||||
|
||||
// Check content of each entry
|
||||
for (Entry e : entries) {
|
||||
Path file = fs.getPath(e.name);
|
||||
if (DEBUG) {
|
||||
System.out.printf("Entry name = %s, bytes= %s, actual=%s%n", e.name,
|
||||
new String(Files.readAllBytes(file)), new String(e.bytes));
|
||||
}
|
||||
assertEquals(Files.readAllBytes(file), e.bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate whether the Manifest contains the expected attributes
|
||||
*
|
||||
* @param attributes A Map containing the attributes expected in the Manifest;
|
||||
* otherwise empty
|
||||
* @param m The Manifest to validate
|
||||
*/
|
||||
private static void validateManifest(Map<?, ?> attributes, Manifest m) {
|
||||
if (!attributes.isEmpty()) {
|
||||
assertNotNull(m, "Manifest is missing!");
|
||||
Attributes attrs = m.getMainAttributes();
|
||||
attributes.forEach((k, v) ->
|
||||
{
|
||||
if (DEBUG) {
|
||||
System.out.printf("Key: %s, Value: %s%n", k, v);
|
||||
}
|
||||
assertTrue(attrs.containsKey(k));
|
||||
assertEquals(v, attrs.get(k));
|
||||
});
|
||||
} else {
|
||||
assertNull(m, "Manifest was found!");
|
||||
}
|
||||
}
|
||||
}
|
257
test/jdk/jdk/nio/zipfs/testng/util/ZipFsBaseTest.java
Normal file
257
test/jdk/jdk/nio/zipfs/testng/util/ZipFsBaseTest.java
Normal file
@ -0,0 +1,257 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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.
|
||||
*
|
||||
*/
|
||||
package util;
|
||||
|
||||
import org.testng.annotations.DataProvider;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class ZipFsBaseTest {
|
||||
|
||||
protected static final Path HERE = Path.of(".");
|
||||
// Enable for permissions output
|
||||
protected static final boolean DEBUG = false;
|
||||
private static final SecureRandom random = new SecureRandom();
|
||||
|
||||
/**
|
||||
* DataProvider used to specify the Zip FS properties to use when creating
|
||||
* the Zip File along with the compression method used
|
||||
*
|
||||
* @return Zip FS properties and compression method used by the tests
|
||||
*/
|
||||
@DataProvider(name = "zipfsMap")
|
||||
protected Object[][] zipfsMap() {
|
||||
return new Object[][]{
|
||||
{Map.of("create", "true"), ZipEntry.DEFLATED},
|
||||
{Map.of("create", "true", "noCompression", "true"),
|
||||
ZipEntry.STORED},
|
||||
{Map.of("create", "true", "noCompression", "false"),
|
||||
ZipEntry.DEFLATED}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* DataProvider with the compression methods to be used for a given test run
|
||||
*
|
||||
* @return Compression methods to test with
|
||||
*/
|
||||
@DataProvider(name = "compressionMethods")
|
||||
protected Object[][] compressionMethods() {
|
||||
return new Object[][]{
|
||||
{ZipEntry.DEFLATED},
|
||||
{ZipEntry.STORED}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to return a formatted String of the key:value entries for
|
||||
* a Map
|
||||
*
|
||||
* @param env Map to format
|
||||
* @return Formatted string of the Map entries
|
||||
*/
|
||||
private static String formatMap(Map<String, ?> env) {
|
||||
return env.entrySet().stream()
|
||||
.map(e -> format("(%s:%s)", e.getKey(), e.getValue()))
|
||||
.collect(joining(", "));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a temporary file Path
|
||||
*
|
||||
* @param dir Directory used to create the path
|
||||
* @param prefix The prefix string used to create the path
|
||||
* @param suffix The suffix string used to create the path
|
||||
* @return Path that was generated
|
||||
*/
|
||||
protected static Path generatePath(Path dir, String prefix, String suffix) {
|
||||
long n = random.nextLong();
|
||||
String s = prefix + Long.toUnsignedString(n) + suffix;
|
||||
Path name = dir.getFileSystem().getPath(s);
|
||||
// the generated name should be a simple file name
|
||||
if (name.getParent() != null)
|
||||
throw new IllegalArgumentException("Invalid prefix or suffix");
|
||||
return dir.resolve(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the given path is a Zip file containing exactly the
|
||||
* given entries.
|
||||
*/
|
||||
protected static void verify(Path zipfile, Entry... entries) throws IOException {
|
||||
// check entries with Zip API
|
||||
try (ZipFile zf = new ZipFile(zipfile.toFile())) {
|
||||
// check entry count
|
||||
assertEquals(entries.length, zf.size());
|
||||
|
||||
// Check compression method and content of each entry
|
||||
for (Entry e : entries) {
|
||||
ZipEntry ze = zf.getEntry(e.name);
|
||||
assertNotNull(ze);
|
||||
if (DEBUG) {
|
||||
System.out.printf("Entry Name: %s, method: %s, Expected Method: %s%n",
|
||||
e.name, ze.getMethod(), e.method);
|
||||
}
|
||||
assertEquals(e.method, ze.getMethod());
|
||||
try (InputStream in = zf.getInputStream(ze)) {
|
||||
byte[] bytes = in.readAllBytes();
|
||||
if (DEBUG) {
|
||||
System.out.printf("bytes= %s, actual=%s%n",
|
||||
new String(bytes), new String(e.bytes));
|
||||
}
|
||||
|
||||
assertTrue(Arrays.equals(bytes, e.bytes));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check entries with FileSystem API
|
||||
try (FileSystem fs = FileSystems.newFileSystem(zipfile)) {
|
||||
// cCheck entry count
|
||||
Path top = fs.getPath("/");
|
||||
long count = Files.find(top, Integer.MAX_VALUE,
|
||||
(path, attrs) -> attrs.isRegularFile()).count();
|
||||
assertEquals(entries.length, count);
|
||||
|
||||
// Check content of each entry
|
||||
for (Entry e : entries) {
|
||||
Path file = fs.getPath(e.name);
|
||||
if (DEBUG) {
|
||||
System.out.printf("Entry name = %s, bytes= %s, actual=%s%n", e.name,
|
||||
new String(Files.readAllBytes(file)), new String(e.bytes));
|
||||
}
|
||||
assertEquals(Files.readAllBytes(file), e.bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Zip File System using the specified properties and a Zip file
|
||||
* with the specified number of entries
|
||||
*
|
||||
* @param zipFile Path to the Zip File to create/update
|
||||
* @param env Properties used for creating the Zip Filesystem
|
||||
* @param source The path of the file to add to the Zip File
|
||||
* @throws IOException If an error occurs while creating/updating the Zip file
|
||||
*/
|
||||
protected void zip(Path zipFile, Map<String, String> env, Path source) throws IOException {
|
||||
if (DEBUG) {
|
||||
System.out.printf("File:%s, adding:%s%n", zipFile.toAbsolutePath(), source);
|
||||
}
|
||||
try (FileSystem zipfs =
|
||||
FileSystems.newFileSystem(zipFile, env)) {
|
||||
Files.copy(source, zipfs.getPath(source.getFileName().toString()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Zip File System using the specified properties and a Zip file
|
||||
* with the specified number of entries
|
||||
*
|
||||
* @param zipFile Path to the Zip File to create
|
||||
* @param env Properties used for creating the Zip Filesystem
|
||||
* @param entries The entries to add to the Zip File
|
||||
* @throws IOException If an error occurs while creating the Zip file
|
||||
*/
|
||||
protected void zip(Path zipFile, Map<String, ?> env,
|
||||
Entry... entries) throws IOException {
|
||||
if (DEBUG) {
|
||||
System.out.printf("Creating file: %s, env: %s%n", zipFile, formatMap(env));
|
||||
}
|
||||
try (FileSystem zipfs = FileSystems.newFileSystem(zipFile, env)) {
|
||||
for (Entry e : entries) {
|
||||
Path path = zipfs.getPath(e.name);
|
||||
if (path.getParent() != null) {
|
||||
Files.createDirectories(path.getParent());
|
||||
}
|
||||
Files.write(path, e.bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively remove a Directory
|
||||
*
|
||||
* @param dir Directory to delete
|
||||
* @throws IOException If an error occurs
|
||||
*/
|
||||
protected static void rmdir(Path dir) throws IOException {
|
||||
// Nothing to do if the the file does not exist
|
||||
if (!Files.exists(dir)) {
|
||||
return;
|
||||
}
|
||||
try (Stream<Path> walk = Files.walk(dir)) {
|
||||
walk.sorted(Comparator.reverseOrder())
|
||||
.map(Path::toFile)
|
||||
.peek(System.out::println)
|
||||
.forEach(File::delete);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an entry in a Zip file. An entry encapsulates a name, a
|
||||
* compression method, and its contents/data.
|
||||
*/
|
||||
public static class Entry {
|
||||
public final String name;
|
||||
public final int method;
|
||||
public final byte[] bytes;
|
||||
|
||||
public Entry(String name, int method, String contents) {
|
||||
this.name = name;
|
||||
this.method = method;
|
||||
this.bytes = contents.getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public static Entry of(String name, int method, String contents) {
|
||||
return new Entry(name, method, contents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new Entry with the same name and compression method as this
|
||||
* Entry but with the given content.
|
||||
*/
|
||||
public Entry content(String contents) {
|
||||
return new Entry(name, method, contents);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user