diff options
author | Almaz Mingaleev <mingaleev@google.com> | 2024-02-27 10:16:02 +0000 |
---|---|---|
committer | Almaz Mingaleev <mingaleev@google.com> | 2024-02-28 12:10:52 +0000 |
commit | 6af4c212931472a5ad536ace23a7005d194602de (patch) | |
tree | c1936df3c2921fba7b2c2a3bc5772306e516afcd | |
parent | f93de51afd8b835f6aca020330005b33a277d907 (diff) | |
parent | ab03ae0edd58d170f101f83fe12848436b00cefe (diff) | |
download | libcore-6af4c212931472a5ad536ace23a7005d194602de.tar.gz |
Merge test.java.util.zip from jdk-17.0.10-ga into the aosp/main branch
List of files:
ojluni/src/test/java/util/zip/CloseInflaterDeflaterTest.java
ojluni/src/test/java/util/zip/DeInflate.java
ojluni/src/test/java/util/zip/DeflaterDictionaryTests.java
ojluni/src/test/java/util/zip/ZipFile/TestCleaner.java
Bug: 254213857
Test: atest CtsLibcoreOjTestCases:test.java.util.zip
Change-Id: I04abc0265ace2654a70e323bd9e22e6ce7f59022
-rw-r--r-- | EXPECTED_UPSTREAM | 4 | ||||
-rw-r--r-- | ojluni/src/test/java/util/zip/CloseInflaterDeflaterTest.java | 211 | ||||
-rw-r--r-- | ojluni/src/test/java/util/zip/DeInflate.java | 335 | ||||
-rw-r--r-- | ojluni/src/test/java/util/zip/DeflaterDictionaryTests.java | 254 | ||||
-rw-r--r-- | ojluni/src/test/java/util/zip/ZipFile/TestCleaner.java | 178 |
5 files changed, 982 insertions, 0 deletions
diff --git a/EXPECTED_UPSTREAM b/EXPECTED_UPSTREAM index b3970e68f64..bce103573b9 100644 --- a/EXPECTED_UPSTREAM +++ b/EXPECTED_UPSTREAM @@ -2610,11 +2610,15 @@ ojluni/src/test/java/util/stream/testlib/org/openjdk/testlib/java/util/Spliterat ojluni/src/test/java/util/stream/testlib/org/openjdk/testlib/java/util/stream/DefaultMethodStreams.java,jdk17u/jdk-17.0.6-ga,test/jdk/lib/testlibrary/bootlib/java.base/java/util/stream/DefaultMethodStreams.java ojluni/src/test/java/util/stream/testlib/org/openjdk/testlib/java/util/stream/ThrowableHelper.java,jdk17u/jdk-17.0.6-ga,test/jdk/lib/testlibrary/bootlib/java.base/java/util/stream/ThrowableHelper.java ojluni/src/test/java/util/zip/ChecksumBase.java,jdk17u/jdk-17.0.6-ga,test/jdk/java/util/zip/ChecksumBase.java +ojluni/src/test/java/util/zip/CloseInflaterDeflaterTest.java,jdk17u/jdk-17.0.10-ga,test/jdk/java/util/zip/CloseInflaterDeflaterTest.java +ojluni/src/test/java/util/zip/DeInflate.java,jdk17u/jdk-17.0.10-ga,test/jdk/java/util/zip/DeInflate.java +ojluni/src/test/java/util/zip/DeflaterDictionaryTests.java,jdk17u/jdk-17.0.10-ga,test/jdk/java/util/zip/DeflaterDictionaryTests.java ojluni/src/test/java/util/zip/TestCRC32.java,jdk17u/jdk-17.0.6-ga,test/jdk/java/util/zip/TestCRC32.java ojluni/src/test/java/util/zip/TestCRC32C.java,jdk17u/jdk-17.0.6-ga,test/jdk/java/util/zip/TestCRC32C.java ojluni/src/test/java/util/zip/TestChecksum.java,jdk17u/jdk-17.0.6-ga,test/jdk/java/util/zip/TestChecksum.java ojluni/src/test/java/util/zip/TestExtraTime.java,jdk17u/jdk-17.0.6-ga,test/jdk/java/util/zip/TestExtraTime.java ojluni/src/test/java/util/zip/TestLocalTime.java,jdk17u/jdk-17.0.6-ga,test/jdk/java/util/zip/TestLocalTime.java +ojluni/src/test/java/util/zip/ZipFile/TestCleaner.java,jdk17u/jdk-17.0.10-ga,test/jdk/java/util/zip/ZipFile/TestCleaner.java ojluni/src/test/java/util/zip/ZipFile/Zip64SizeTest.java,jdk11u/jdk-11.0.13-ga,test/jdk/java/util/zip/ZipFile/Zip64SizeTest.java ojluni/src/tools/make/gensrc/GensrcBuffer.gmk,jdk17u/jdk-17.0.6-ga,make/modules/java.base/gensrc/GensrcBuffer.gmk ojluni/src/tools/make/gensrc/GensrcCharsetCoder.gmk,jdk17u/jdk-17.0.6-ga,make/modules/java.base/gensrc/GensrcCharsetCoder.gmk diff --git a/ojluni/src/test/java/util/zip/CloseInflaterDeflaterTest.java b/ojluni/src/test/java/util/zip/CloseInflaterDeflaterTest.java new file mode 100644 index 00000000000..1dae06c897e --- /dev/null +++ b/ojluni/src/test/java/util/zip/CloseInflaterDeflaterTest.java @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2021, 2022, 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. + */ + +/** + * @test + * @bug 8193682 8278794 8284771 + * @summary Test Infinite loop while writing on closed Deflater and Inflater. + * @run testng CloseInflaterDeflaterTest + */ +package test.java.util.zip; + +import java.io.*; +import java.util.Random; +import java.util.jar.JarOutputStream; +import java.util.zip.DeflaterInputStream; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.GZIPOutputStream; +import java.util.zip.InflaterOutputStream; +import java.util.zip.ZipOutputStream; +import java.util.zip.ZipEntry; + +import org.testng.annotations.BeforeTest; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import static org.testng.Assert.assertThrows; + + +public class CloseInflaterDeflaterTest { + + // Number of bytes to write/read from Deflater/Inflater + private static final int INPUT_LENGTH= 512; + // OutputStream that will throw an exception during a write operation + private static OutputStream outStream = new OutputStream() { + @Override + public void write(byte[] b, int off, int len) throws IOException { + throw new IOException(); + } + @Override + public void write(byte[] b) throws IOException {} + @Override + public void write(int b) throws IOException {} + }; + // InputStream that will throw an exception during a read operation + private static InputStream inStream = new InputStream() { + @Override + public int read(byte[] b, int off, int len) throws IOException { + throw new IOException(); + } + @Override + public int read(byte[] b) throws IOException { throw new IOException();} + @Override + public int read() throws IOException { throw new IOException();} + }; + // Input bytes for read/write operation + private static byte[] inputBytes = new byte[INPUT_LENGTH]; + // Random function to add bytes to inputBytes + private static Random rand = new Random(); + + /** + * DataProvider to specify whether to use close() or finish() of OutputStream + * + * @return Entry object indicating which method to use for closing OutputStream + */ + @DataProvider + public Object[][] testOutputStreams() { + return new Object[][] { + { true }, + { false }, + }; + } + + /** + * DataProvider to specify on which outputstream closeEntry() has to be called + * + * @return Entry object returning either JarOutputStream or ZipOutputStream + */ + @DataProvider + public Object[][] testZipAndJar() throws IOException{ + return new Object[][] { + { new JarOutputStream(outStream)}, + { new ZipOutputStream(outStream)}, + }; + } + + /** + * Add inputBytes array with random bytes to write into OutputStream + */ + @BeforeTest + public void before_test() + { + rand.nextBytes(inputBytes); + } + + /** + * Test for infinite loop by writing bytes to closed GZIPOutputStream + * + * @param useCloseMethod indicates whether to use Close() or finish() method + * @throws IOException if an error occurs + */ + @Test(dataProvider = "testOutputStreams") + public void testGZip(boolean useCloseMethod) throws IOException { + GZIPOutputStream gzip = new GZIPOutputStream(outStream); + gzip.write(inputBytes, 0, INPUT_LENGTH); + assertThrows(IOException.class, () -> { + // Close GZIPOutputStream + if (useCloseMethod) { + gzip.close(); + } else { + gzip.finish(); + } + }); + // Write on a closed GZIPOutputStream, closed Deflater IOException expected + assertThrows(NullPointerException.class , () -> gzip.write(inputBytes, 0, INPUT_LENGTH)); + } + + /** + * Test for infinite loop by writing bytes to closed DeflaterOutputStream + * + * @param useCloseMethod indicates whether to use Close() or finish() method + * @throws IOException if an error occurs + */ + @Test(dataProvider = "testOutputStreams") + public void testDeflaterOutputStream(boolean useCloseMethod) throws IOException { + DeflaterOutputStream def = new DeflaterOutputStream(outStream); + assertThrows(IOException.class , () -> def.write(inputBytes, 0, INPUT_LENGTH)); + assertThrows(IOException.class, () -> { + // Close DeflaterOutputStream + if (useCloseMethod) { + def.close(); + } else { + def.finish(); + } + }); + // Write on a closed DeflaterOutputStream, 'Deflater has been closed' NPE is expected + assertThrows(NullPointerException.class , () -> def.write(inputBytes, 0, INPUT_LENGTH)); + } + + /** + * Test for infinite loop by reading bytes from closed DeflaterInputStream + * + * @throws IOException if an error occurs + */ + @Test + public void testDeflaterInputStream() throws IOException { + DeflaterInputStream def = new DeflaterInputStream(inStream); + assertThrows(IOException.class , () -> def.read(inputBytes, 0, INPUT_LENGTH)); + // Close DeflaterInputStream + def.close(); + // Read from a closed DeflaterInputStream, closed Deflater IOException expected + assertThrows(IOException.class , () -> def.read(inputBytes, 0, INPUT_LENGTH)); + } + + /** + * Test for infinite loop by writing bytes to closed InflaterOutputStream + * + * Note: Disabling this test as it is failing intermittently. + * @param useCloseMethod indicates whether to use Close() or finish() method + * @throws IOException if an error occurs + */ + @Test(dataProvider = "testOutputStreams",enabled=false) + public void testInflaterOutputStream(boolean useCloseMethod) throws IOException { + InflaterOutputStream inf = new InflaterOutputStream(outStream); + assertThrows(IOException.class , () -> inf.write(inputBytes, 0, INPUT_LENGTH)); + assertThrows(IOException.class , () -> { + // Close InflaterOutputStream + if (useCloseMethod) { + inf.close(); + } else { + inf.finish(); + } + }); + // Write on a closed InflaterOutputStream , closed Inflater IOException expected + assertThrows(IOException.class , () -> inf.write(inputBytes, 0, INPUT_LENGTH)); + } + + /** + * Test for infinite loop by writing bytes to closed ZipOutputStream/JarOutputStream + * + * @param zip will be the instance of either JarOutputStream or ZipOutputStream + * @throws IOException if an error occurs + */ + @Test(dataProvider = "testZipAndJar") + public void testZipCloseEntry(ZipOutputStream zip) throws IOException { + assertThrows(IOException.class , () -> zip.putNextEntry(new ZipEntry(""))); + zip.write(inputBytes, 0, INPUT_LENGTH); + assertThrows(IOException.class , () -> zip.closeEntry()); + // Write on a closed ZipOutputStream , 'Deflater has been closed' NPE is expected + assertThrows(NullPointerException.class , () -> zip.write(inputBytes, 0, INPUT_LENGTH)); + } + +} diff --git a/ojluni/src/test/java/util/zip/DeInflate.java b/ojluni/src/test/java/util/zip/DeInflate.java new file mode 100644 index 00000000000..ce48ccfa756 --- /dev/null +++ b/ojluni/src/test/java/util/zip/DeInflate.java @@ -0,0 +1,335 @@ +/* + * Copyright (c) 2011, 2023, 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. + */ + +/** + * @test + * @bug 7110149 8184306 6341887 + * @summary Test basic deflater & inflater functionality + * @key randomness + */ + +package test.java.util.zip; + +import java.io.*; +import java.nio.*; +import java.util.*; +import java.util.zip.*; + + +public class DeInflate { + + private static Random rnd = new Random(); + + + static void checkStream(Deflater def, byte[] in, int len, + byte[] out1, byte[] out2, boolean nowrap) + throws Throwable + { + Arrays.fill(out1, (byte)0); + Arrays.fill(out2, (byte)0); + + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + try (DeflaterOutputStream defos = new DeflaterOutputStream(baos, def)) { + defos.write(in, 0, len); + } + out1 = baos.toByteArray(); + } + int m = out1.length; + + Inflater inf = new Inflater(nowrap); + inf.setInput(out1, 0, m); + int n = inf.inflate(out2); + + if (n != len || + !Arrays.equals(Arrays.copyOf(in, len), Arrays.copyOf(out2, len)) || + inf.inflate(out2) != 0) { + System.out.printf("m=%d, n=%d, len=%d, eq=%b%n", + m, n, len, Arrays.equals(in, out2)); + throw new RuntimeException("De/inflater failed:" + def); + } + } + + static void checkByteBuffer(Deflater def, Inflater inf, + ByteBuffer in, ByteBuffer out1, ByteBuffer out2, + byte[] expected, int len, byte[] result, + boolean out1ReadOnlyWhenInflate) + throws Throwable { + def.reset(); + inf.reset(); + + def.setInput(in); + def.finish(); + int m = def.deflate(out1); + + out1.flip(); + if (out1ReadOnlyWhenInflate) + out1 = out1.asReadOnlyBuffer(); + inf.setInput(out1); + int n = inf.inflate(out2); + + out2.flip(); + out2.get(result, 0, n); + + if (n != len || out2.position() != len || + !Arrays.equals(Arrays.copyOf(expected, len), Arrays.copyOf(result, len)) || + inf.inflate(result) != 0) { + throw new RuntimeException("De/inflater(buffer) failed:" + def); + } + } + + static void checkByteBufferReadonly(Deflater def, Inflater inf, + ByteBuffer in, ByteBuffer out1, ByteBuffer out2) + throws Throwable { + def.reset(); + inf.reset(); + def.setInput(in); + def.finish(); + int m = -1; + if (!out2.isReadOnly()) + out2 = out2.asReadOnlyBuffer(); + try { + m = def.deflate(out2); + throw new RuntimeException("deflater: ReadOnlyBufferException: failed"); + } catch (ReadOnlyBufferException robe) {} + m = def.deflate(out1); + out1.flip(); + inf.setInput(out1); + try { + inf.inflate(out2); + throw new RuntimeException("inflater: ReadOnlyBufferException: failed"); + } catch (ReadOnlyBufferException robe) {} + } + + /** + * Uses the {@code def} deflater to deflate the input data {@code in} of length {@code len}. + * A new {@link Inflater} is then created within this method to inflate the deflated data. The + * inflated data is then compared with the {@code in} to assert that it matches the original + * input data. + * This method repeats these checks for the different overloaded methods of + * {@code Deflater.deflate(...)} and {@code Inflater.inflate(...)} + * + * @param def the deflater to use for deflating the contents in {@code in} + * @param in the input content + * @param len the length of the input content to use + * @param nowrap will be passed to the constructor of the {@code Inflater} used in this + * method + * @throws Throwable if any error occurs during the check + */ + static void check(Deflater def, byte[] in, int len, boolean nowrap) + throws Throwable + { + byte[] tempBuffer = new byte[1024]; + byte[] out1, out2; + int m = 0, n = 0; + Inflater inf = new Inflater(nowrap); + def.setInput(in, 0, len); + def.finish(); + + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + while (!def.finished()) { + int temp_counter = def.deflate(tempBuffer); + m += temp_counter; + baos.write(tempBuffer, 0, temp_counter); + } + out1 = baos.toByteArray(); + baos.reset(); + + inf.setInput(out1, 0, m); + + while (!inf.finished()) { + int temp_counter = inf.inflate(tempBuffer); + n += temp_counter; + baos.write(tempBuffer, 0, temp_counter); + } + out2 = baos.toByteArray(); + if (n != len || + !Arrays.equals(in, 0, len, out2, 0, len) || + inf.inflate(out2) != 0) { + System.out.printf("m=%d, n=%d, len=%d, eq=%b%n", + m, n, len, Arrays.equals(in, out2)); + throw new RuntimeException("De/inflater failed:" + def); + } + } + + // readable + Arrays.fill(out1, (byte)0); + Arrays.fill(out2, (byte)0); + ByteBuffer bbIn = ByteBuffer.wrap(in, 0, len); + ByteBuffer bbOut1 = ByteBuffer.wrap(out1); + ByteBuffer bbOut2 = ByteBuffer.wrap(out2); + checkByteBuffer(def, inf, bbIn, bbOut1, bbOut2, in, len, out2, false); + checkByteBufferReadonly(def, inf, bbIn, bbOut1, bbOut2); + + // readonly in + Arrays.fill(out1, (byte)0); + Arrays.fill(out2, (byte)0); + bbIn = ByteBuffer.wrap(in, 0, len).asReadOnlyBuffer(); + bbOut1 = ByteBuffer.wrap(out1); + bbOut2 = ByteBuffer.wrap(out2); + checkByteBuffer(def, inf, bbIn, bbOut1, bbOut2, in, len, out2, false); + checkByteBufferReadonly(def, inf, bbIn, bbOut1, bbOut2); + + // readonly out1 when inflate + Arrays.fill(out1, (byte)0); + Arrays.fill(out2, (byte)0); + bbIn = ByteBuffer.wrap(in, 0, len); + bbOut1 = ByteBuffer.wrap(out1); + bbOut2 = ByteBuffer.wrap(out2); + checkByteBuffer(def, inf, bbIn, bbOut1, bbOut2, in, len, out2, true); + checkByteBufferReadonly(def, inf, bbIn, bbOut1, bbOut2); + + // direct + bbIn = ByteBuffer.allocateDirect(in.length); + bbIn.put(in, 0, n).flip(); + bbOut1 = ByteBuffer.allocateDirect(out1.length); + bbOut2 = ByteBuffer.allocateDirect(out2.length); + checkByteBuffer(def, inf, bbIn, bbOut1, bbOut2, in, len, out2, false); + checkByteBufferReadonly(def, inf, bbIn, bbOut1, bbOut2); + } + + static void checkDict(Deflater def, Inflater inf, byte[] src, + byte[] dstDef, byte[] dstInf, + ByteBuffer dictDef, ByteBuffer dictInf) throws Throwable { + def.reset(); + inf.reset(); + + def.setDictionary(dictDef); + def.setInput(src); + def.finish(); + int n = def.deflate(dstDef); + + inf.setInput(dstDef, 0, n); + n = inf.inflate(dstInf); + if (n != 0 || !inf.needsDictionary()) { + throw new RuntimeException("checkDict failed: need dict to continue"); + } + inf.setDictionary(dictInf); + n = inf.inflate(dstInf); + // System.out.println("result: " + new String(dstInf, 0, n)); + if (n != src.length || !Arrays.equals(Arrays.copyOf(dstInf, n), src)) { + throw new RuntimeException("checkDict failed: inflate result"); + } + } + + static void checkDict(int level, int strategy) throws Throwable { + + Deflater def = newDeflater(level, strategy, false, new byte[0]); + Inflater inf = new Inflater(); + + byte[] src = "hello world, hello world, hello sherman".getBytes(); + byte[] dict = "hello".getBytes(); + + byte[] dstDef = new byte[1024]; + byte[] dstInf = new byte[1024]; + + def.setDictionary(dict); + def.setInput(src); + def.finish(); + int n = def.deflate(dstDef); + + inf.setInput(dstDef, 0, n); + n = inf.inflate(dstInf); + if (n != 0 || !inf.needsDictionary()) { + throw new RuntimeException("checkDict failed: need dict to continue"); + } + inf.setDictionary(dict); + n = inf.inflate(dstInf); + //System.out.println("result: " + new String(dstInf, 0, n)); + if (n != src.length || !Arrays.equals(Arrays.copyOf(dstInf, n), src)) { + throw new RuntimeException("checkDict failed: inflate result"); + } + + ByteBuffer dictDef = ByteBuffer.wrap(dict); + ByteBuffer dictInf = ByteBuffer.wrap(dict); + checkDict(def, inf, src, dstDef, dstInf, dictDef, dictInf); + + dictDef = ByteBuffer.allocateDirect(dict.length); + dictInf = ByteBuffer.allocateDirect(dict.length); + dictDef.put(dict).flip(); + dictInf.put(dict).flip(); + checkDict(def, inf, src, dstDef, dstInf, dictDef, dictInf); + + def.end(); + inf.end(); + } + + private static Deflater newDeflater(int level, int strategy, boolean dowrap, byte[] tmp) { + Deflater def = new Deflater(level, dowrap); + if (strategy != Deflater.DEFAULT_STRATEGY) { + def.setStrategy(strategy); + // The first invocation after setLevel/Strategy() + // with a different level/stragety returns 0, if + // there is no need to flush out anything for the + // previous setting/"data", this is tricky and + // appears un-documented. + def.deflate(tmp); + } + return def; + } + + private static Deflater resetDeflater(Deflater def, int level, int strategy) { + def.setLevel(level); + def.setStrategy(strategy); + def.reset(); + return def; + } + + public static void main(String[] args) throws Throwable { + + byte[] dataIn = new byte[1024 * 512]; + rnd.nextBytes(dataIn); + byte[] dataOut1 = new byte[dataIn.length + 1024]; + byte[] dataOut2 = new byte[dataIn.length]; + + Deflater defNotWrap = new Deflater(Deflater.DEFAULT_COMPRESSION, false); + Deflater defWrap = new Deflater(Deflater.DEFAULT_COMPRESSION, true); + + for (int level = Deflater.DEFAULT_COMPRESSION; + level <= Deflater.BEST_COMPRESSION; level++) { + for (int strategy = Deflater.DEFAULT_STRATEGY; + strategy <= Deflater.HUFFMAN_ONLY; strategy++) { + for (boolean dowrap : new boolean[] { false, true }) { + System.out.println("level:" + level + + ", strategy: " + strategy + + ", dowrap: " + dowrap); + for (int i = 0; i < 5; i++) { + int len = (i == 0)? dataIn.length + : new Random().nextInt(dataIn.length); + System.out.println("iteration: " + (i + 1) + " input length: " + len); + // use a new deflater + Deflater def = newDeflater(level, strategy, dowrap, dataOut2); + check(def, dataIn, len, dowrap); + def.end(); + + // reuse the deflater (with reset) and test on stream, which + // uses a "smaller" buffer (smaller than the overall data) + def = resetDeflater(dowrap ? defWrap : defNotWrap, level, strategy); + checkStream(def, dataIn, len, dataOut1, dataOut2, dowrap); + } + } + // test setDictionary() + checkDict(level, strategy); + } + } + } +} diff --git a/ojluni/src/test/java/util/zip/DeflaterDictionaryTests.java b/ojluni/src/test/java/util/zip/DeflaterDictionaryTests.java new file mode 100644 index 00000000000..f0a7d52b719 --- /dev/null +++ b/ojluni/src/test/java/util/zip/DeflaterDictionaryTests.java @@ -0,0 +1,254 @@ +/* + * 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.java.util.zip; + +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.zip.Deflater; +import java.util.zip.Inflater; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.testng.Assert.assertThrows; + +/** + * @test + * @bug 8252739 + * @summary Verify Deflater.setDictionary(dictionary, offset, length) uses the offset + * @run testng/othervm DeflaterDictionaryTests + */ +public class DeflaterDictionaryTests { + // Output buffer size + private static final int RESULT_SIZE = 1024; + // Data to compress + private static final String SRC_DATA = "Welcome to the US Open;".repeat(6); + // Dictionary to be used + private static final String DICTIONARY = "US Open"; + private static final int DICTIONARY_OFFSET = 1; + private static final int DICTIONARY_LENGTH = 3; + + /** + * DataProvider with offsets which should be valid for setDictionary + * + * @return valid offset values + */ + @DataProvider(name = "validDictionaryOffsets") + protected Object[][] validDictionaryOffsets() { + return new Object[][]{ + {0}, + {DICTIONARY_OFFSET}, + {DICTIONARY_LENGTH} + }; + } + + /** + * DataProvider with invalid offsets for setDictionary + * + * @return invalid offset values + */ + @DataProvider(name = "invalidDictionaryOffsets") + protected Object[][] invalidDictionaryOffsets() { + return new Object[][]{ + {-1}, + {DICTIONARY_LENGTH + 2}, + {DICTIONARY.length()} + }; + } + + /** + * Validate that an offset can be used with Deflater::setDictionary + * + * @param dictionary_offset offset value to be used + * @throws Exception if an error occurs + */ + @Test(dataProvider = "validDictionaryOffsets") + public void testByteArray(int dictionary_offset) throws Exception { + byte[] input = SRC_DATA.getBytes(UTF_8); + byte[] output = new byte[RESULT_SIZE]; + Deflater deflater = new Deflater(); + Inflater inflater = new Inflater(); + try { + // Compress the bytes + deflater.setDictionary(DICTIONARY.getBytes(UTF_8), dictionary_offset, DICTIONARY_LENGTH); + deflater.setInput(input); + deflater.finish(); + int compressedDataLength = deflater.deflate(output, 0, output.length, Deflater.NO_FLUSH); + System.out.printf("Deflater::getTotalOut:%s, Deflater::getAdler: %s," + + " compressed length: %s%n", deflater.getTotalOut(), + deflater.getTotalOut(), compressedDataLength); + deflater.finished(); + + // Decompress the bytes + inflater.setInput(output, 0, compressedDataLength); + byte[] result = new byte[RESULT_SIZE]; + int resultLength = inflater.inflate(result); + if (inflater.needsDictionary()) { + System.out.println("Specifying Dictionary"); + inflater.setDictionary(DICTIONARY.getBytes(UTF_8), dictionary_offset, DICTIONARY_LENGTH); + resultLength = inflater.inflate(result); + } else { + System.out.println("Did not need to use a Dictionary"); + } + inflater.finished(); + System.out.printf("Inflater::getAdler:%s, length: %s%n", + inflater.getAdler(), resultLength); + + Assert.assertEquals(SRC_DATA.length(), resultLength); + Assert.assertEquals(input, Arrays.copyOf(result, resultLength)); + } finally { + // Release Resources + deflater.end(); + inflater.end(); + } + } + + /** + * Validate that a ByteBuffer can be used with Deflater::setDictionary + * + * @throws Exception if an error occurs + */ + @Test + public void testHeapByteBuffer() throws Exception { + byte[] input = SRC_DATA.getBytes(UTF_8); + byte[] output = new byte[RESULT_SIZE]; + ByteBuffer dictDef = ByteBuffer.wrap(DICTIONARY.getBytes(UTF_8), DICTIONARY_OFFSET, DICTIONARY_LENGTH); + ByteBuffer dictInf = ByteBuffer.wrap(DICTIONARY.getBytes(UTF_8), DICTIONARY_OFFSET, DICTIONARY_LENGTH); + Deflater deflater = new Deflater(); + Inflater inflater = new Inflater(); + try { + // Compress the bytes + deflater.setDictionary(dictDef); + deflater.setInput(input); + deflater.finish(); + int compressedDataLength = deflater.deflate(output, 0, output.length, Deflater.NO_FLUSH); + System.out.printf("Deflater::getTotalOut:%s, Deflater::getAdler: %s," + + " compressed length: %s%n", deflater.getTotalOut(), + deflater.getTotalOut(), compressedDataLength); + deflater.finished(); + + // Decompress the bytes + inflater.setInput(output, 0, compressedDataLength); + byte[] result = new byte[RESULT_SIZE]; + int resultLength = inflater.inflate(result); + if (inflater.needsDictionary()) { + System.out.println("Specifying Dictionary"); + inflater.setDictionary(dictInf); + resultLength = inflater.inflate(result); + } else { + System.out.println("Did not need to use a Dictionary"); + } + inflater.finished(); + System.out.printf("Inflater::getAdler:%s, length: %s%n", + inflater.getAdler(), resultLength); + + Assert.assertEquals(SRC_DATA.length(), resultLength); + Assert.assertEquals(input, Arrays.copyOf(result, resultLength)); + } finally { + // Release Resources + deflater.end(); + inflater.end(); + } + } + + /** + * Validate that ByteBuffer::allocateDirect can be used with Deflater::setDictionary + * + * @throws Exception if an error occurs + */ + @Test + public void testByteBufferDirect() throws Exception { + byte[] input = SRC_DATA.getBytes(UTF_8); + byte[] output = new byte[RESULT_SIZE]; + ByteBuffer dictDef = ByteBuffer.allocateDirect(DICTIONARY.length()); + ByteBuffer dictInf = ByteBuffer.allocateDirect(DICTIONARY.length()); + dictDef.put(DICTIONARY.getBytes(UTF_8)); + dictInf.put(DICTIONARY.getBytes(UTF_8)); + dictDef.position(DICTIONARY_OFFSET); + dictDef.limit(DICTIONARY_LENGTH); + dictInf.position(DICTIONARY_OFFSET); + dictInf.limit(DICTIONARY_LENGTH); + Deflater deflater = new Deflater(); + Inflater inflater = new Inflater(); + try { + // Compress the bytes + deflater.setDictionary(dictDef.slice()); + deflater.setInput(input); + deflater.finish(); + int compressedDataLength = deflater.deflate(output, 0, output.length, Deflater.NO_FLUSH); + System.out.printf("Deflater::getTotalOut:%s, Deflater::getAdler: %s," + + " compressed length: %s%n", deflater.getTotalOut(), + deflater.getTotalOut(), compressedDataLength); + deflater.finished(); + + // Decompress the bytes + inflater.setInput(output, 0, compressedDataLength); + byte[] result = new byte[RESULT_SIZE]; + int resultLength = inflater.inflate(result); + if (inflater.needsDictionary()) { + System.out.println("Specifying Dictionary"); + inflater.setDictionary(dictInf.slice()); + resultLength = inflater.inflate(result); + } else { + System.out.println("Did not need to use a Dictionary"); + } + inflater.finished(); + System.out.printf("Inflater::getAdler:%s, length: %s%n", + inflater.getAdler(), resultLength); + + Assert.assertEquals(SRC_DATA.length(), resultLength); + Assert.assertEquals(input, Arrays.copyOf(result, resultLength)); + } finally { + // Release Resources + deflater.end(); + inflater.end(); + } + } + + /** + * Validate that an invalid offset used with setDictionary will + * throw an Exception + * + * @param dictionary_offset offset value to be used + */ + @Test(dataProvider = "invalidDictionaryOffsets") + public void testInvalidOffsets(int dictionary_offset) { + byte[] dictionary = DICTIONARY.getBytes(UTF_8); + + Deflater deflater = new Deflater(); + Inflater inflater = new Inflater(); + try { + assertThrows(ArrayIndexOutOfBoundsException.class, () -> + deflater.setDictionary(dictionary, dictionary_offset, DICTIONARY_LENGTH)); + assertThrows(ArrayIndexOutOfBoundsException.class, () -> + inflater.setDictionary(dictionary, dictionary_offset, DICTIONARY_LENGTH)); + } finally { + // Release Resources + deflater.end(); + inflater.end(); + } + } +}
\ No newline at end of file diff --git a/ojluni/src/test/java/util/zip/ZipFile/TestCleaner.java b/ojluni/src/test/java/util/zip/ZipFile/TestCleaner.java new file mode 100644 index 00000000000..a940f57f63f --- /dev/null +++ b/ojluni/src/test/java/util/zip/ZipFile/TestCleaner.java @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2018, 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. + */ + +/* @test + * @bug 8185582 8197989 + * @modules java.base/java.util.zip:open java.base/jdk.internal.vm.annotation + * @summary Check the resources of Inflater, Deflater and ZipFile are always + * cleaned/released when the instance is not unreachable + */ + +package test.java.util.zip.ZipFile; + +import java.io.*; +import java.lang.reflect.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.zip.*; +import static java.nio.charset.StandardCharsets.US_ASCII; + +public class TestCleaner { + + public static void main(String[] args) throws Throwable { + testDeInflater(); + testZipFile(); + } + + private static long addrOf(Object obj) { + try { + Field addr = obj.getClass().getDeclaredField("address"); + // Android-changed: trySetAccessible is not yet available. + // if (!addr.trySetAccessible()) { + // return -1; + // } + addr.setAccessible(true); + return addr.getLong(obj); + } catch (Exception x) { + return -1; + } + } + + // verify the "native resource" of In/Deflater has been cleaned + private static void testDeInflater() throws Throwable { + Field zsRefDef = Deflater.class.getDeclaredField("zsRef"); + Field zsRefInf = Inflater.class.getDeclaredField("zsRef"); + // Android-changed: trySetAccessible is not yet available. + // if (!zsRefDef.trySetAccessible() || !zsRefInf.trySetAccessible()) { + // throw new RuntimeException("'zsRef' is not accesible"); + // } + zsRefDef.setAccessible(true); + zsRefInf.setAccessible(true); + + if (addrOf(zsRefDef.get(new Deflater())) == -1 || + addrOf(zsRefInf.get(new Inflater())) == -1) { + throw new RuntimeException("'addr' is not accesible"); + } + List<Object> list = new ArrayList<>(); + byte[] buf1 = new byte[1024]; + byte[] buf2 = new byte[1024]; + for (int i = 0; i < 10; i++) { + var def = new Deflater(); + list.add(zsRefDef.get(def)); + def.setInput("hello".getBytes()); + def.finish(); + int n = def.deflate(buf1); + + var inf = new Inflater(); + list.add(zsRefInf.get(inf)); + inf.setInput(buf1, 0, n); + n = inf.inflate(buf2); + if (!"hello".equals(new String(buf2, 0, n))) { + throw new RuntimeException("compression/decompression failed"); + } + } + + int n = 10; + long cnt = list.size(); + while (n-- > 0 && cnt != 0) { + Thread.sleep(100); + // Android-changed: System.gc() does not always trigger GC. + // System.gc(); + Runtime.getRuntime().gc(); + cnt = list.stream().filter(o -> addrOf(o) != 0).count(); + } + if (cnt != 0) + throw new RuntimeException("cleaner failed to clean : " + cnt); + + } + + // Android-removed: @DontInline is not available on Android. + // @DontInline + private static Object openAndCloseZipFile(File zip) throws Throwable { + try { + try (var fos = new FileOutputStream(zip); + var zos = new ZipOutputStream(fos)) { + zos.putNextEntry(new ZipEntry("hello")); + zos.write("hello".getBytes(US_ASCII)); + zos.closeEntry(); + } + + var zf = new ZipFile(zip); + var es = zf.entries(); + while (es.hasMoreElements()) { + zf.getInputStream(es.nextElement()).read(); + } + + Field fieldRes = ZipFile.class.getDeclaredField("res"); + // Android-changed: trySetAccessible is not yet available. + // if (!fieldRes.trySetAccessible()) { + // throw new RuntimeException("'ZipFile.res' is not accesible"); + // } + fieldRes.setAccessible(true); + Object zfRes = fieldRes.get(zf); + if (zfRes == null) { + throw new RuntimeException("'ZipFile.res' is null"); + } + Field fieldZsrc = zfRes.getClass().getDeclaredField("zsrc"); + // Android-changed: trySetAccessible is not yet available. + // if (!fieldZsrc.trySetAccessible()) { + // throw new RuntimeException("'ZipFile.zsrc' is not accesible"); + // } + fieldZsrc.setAccessible(true); + return fieldZsrc.get(zfRes); + } finally { + zip.delete(); + } + } + + + private static void testZipFile() throws Throwable { + // Android-changed: property is not available, create temp dir explicitly. + // File dir = new File(System.getProperty("test.dir", ".")); + Path dir = Files.createTempDirectory("zip-test-dir"); + File zip = File.createTempFile("testzf", "zip", dir.toFile()); + + Object zsrc = openAndCloseZipFile(zip); + if (zsrc != null) { + Field zfileField = zsrc.getClass().getDeclaredField("zfile"); + // Android-changed: trySetAccessible is not yet available. + // if (!zfileField.trySetAccessible()) { + // throw new RuntimeException("'ZipFile.Source.zfile' is not accesible"); + // } + zfileField.setAccessible(true); + //System.out.println("zffile: " + zfileField.get(zsrc)); + int n = 10; + while (n-- > 0 && zfileField.get(zsrc) != null) { + System.out.println("waiting gc ... " + n); + // Android-changed: System.gc() does not always trigger GC. + // System.gc(); + Runtime.getRuntime().gc(); + Thread.sleep(100); + } + if (zfileField.get(zsrc) != null) { + throw new RuntimeException("cleaner failed to clean zipfile."); + } + } + } +} |