aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2024-03-04 22:22:40 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2024-03-04 22:22:40 +0000
commit70c4ff91686496e73b21a71e2fc71ee31aa6c2c4 (patch)
tree91ad70aefe694afce0a7f578a99acc1d1c34058f
parent0636518df5c17e7770416e1e4cd1984940115012 (diff)
parentb8c26973adafbf5875ca59affab5ecdb59c119e1 (diff)
downloadlibcore-simpleperf-release.tar.gz
Merge "Snap for 11526323 from eab0a18d6574cd660fd1e968213513f7c24a452b to simpleperf-release" into simpleperf-releasesimpleperf-release
-rw-r--r--EXPECTED_UPSTREAM4
-rw-r--r--luni/src/test/java/libcore/java/util/ArbitrarilyJumpableGeneratorTest.java121
-rw-r--r--luni/src/test/java/libcore/java/util/SplittableRandomTest.java96
-rw-r--r--luni/src/test/java/libcore/java/util/random/SplittableGeneratorTest.java66
-rw-r--r--ojluni/src/test/java/util/zip/CloseInflaterDeflaterTest.java211
-rw-r--r--ojluni/src/test/java/util/zip/DeInflate.java335
-rw-r--r--ojluni/src/test/java/util/zip/DeflaterDictionaryTests.java254
-rw-r--r--ojluni/src/test/java/util/zip/ZipFile/TestCleaner.java178
8 files changed, 1259 insertions, 6 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/luni/src/test/java/libcore/java/util/ArbitrarilyJumpableGeneratorTest.java b/luni/src/test/java/libcore/java/util/ArbitrarilyJumpableGeneratorTest.java
new file mode 100644
index 00000000000..bdd48e8cbe4
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/ArbitrarilyJumpableGeneratorTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.util;
+
+import static org.junit.Assert.assertEquals;
+
+import static java.util.stream.Collectors.toSet;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Set;
+import java.util.random.RandomGenerator;
+import java.util.stream.Collectors;
+
+@RunWith(JUnit4.class)
+public class ArbitrarilyJumpableGeneratorTest {
+
+ private static final class StubGenerator
+ implements RandomGenerator.ArbitrarilyJumpableGenerator {
+
+ private double state;
+
+ StubGenerator(double state) {
+ this.state = state;
+ }
+
+ @Override
+ public long nextLong() {
+ return 0;
+ }
+
+ @Override
+ public double jumpDistance() {
+ return 1.0d;
+ }
+
+ @Override
+ public double leapDistance() {
+ return 2.0d;
+ }
+
+ @Override
+ public ArbitrarilyJumpableGenerator copy() {
+ return new StubGenerator(state);
+ }
+
+ @Override
+ public void jumpPowerOfTwo(int logDistance) {
+ state += Math.pow(2, logDistance);
+ }
+
+ @Override
+ public void jump(double distance) {
+ state += distance;
+ }
+ }
+
+ @Test
+ public void jump_jumpsToJumpDistance() {
+ double initState = 10.0d;
+ StubGenerator rng = new StubGenerator(initState);
+
+ rng.jump();
+
+ assertEquals(initState + rng.jumpDistance(), rng.state, 0.001d);
+ }
+
+ @Test
+ public void leap_jumpsToLeapDistance() {
+ double initState = 10.0d;
+ StubGenerator rng = new StubGenerator(initState);
+
+ rng.leap();
+
+ assertEquals(initState + rng.leapDistance(), rng.state, 0.001d);
+ }
+
+ @Test
+ public void jump_advancesEachElementInStream() {
+ double initState = 1d;
+ StubGenerator rng = new StubGenerator(initState);
+
+ double jumpDistance = 10;
+ int streamSize = 5;
+
+ var generators = rng.jumps(streamSize, jumpDistance).toList();
+
+ assertEquals(initState + jumpDistance * streamSize, rng.state, 0.001d);
+ assertEquals(streamSize, generators.size());
+
+ Set<Double> actualStates = generators.stream()
+ .map(generator -> (StubGenerator) generator)
+ .map(stubGenerator -> stubGenerator.state)
+ .collect(toSet());
+
+ Set<Double> expectedStates = Set.of(
+ initState,
+ initState + jumpDistance,
+ initState + 2 * jumpDistance,
+ initState + 3 * jumpDistance,
+ initState + 4 * jumpDistance);
+
+ assertEquals(expectedStates, actualStates);
+ }
+}
diff --git a/luni/src/test/java/libcore/java/util/SplittableRandomTest.java b/luni/src/test/java/libcore/java/util/SplittableRandomTest.java
index e1bb9309736..85f83c77732 100644
--- a/luni/src/test/java/libcore/java/util/SplittableRandomTest.java
+++ b/luni/src/test/java/libcore/java/util/SplittableRandomTest.java
@@ -16,9 +16,11 @@
package libcore.java.util;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
import java.util.SplittableRandom;
+import java.util.random.RandomGenerator.SplittableGenerator;
import org.junit.Assert;
import org.junit.Test;
@@ -34,18 +36,36 @@ public class SplittableRandomTest {
SplittableRandom random1 = new SplittableRandom(seed);
SplittableRandom random2 = new SplittableRandom(seed);
- assertEquals(random1, random2);
+ assertGeneratorsAreEquals(random1, random2);
}
@Test
- public void split_throwsNullWhenSourceIsNull() {
+ public void split_throwsNPE_whenSourceIsNull() {
SplittableRandom random = new SplittableRandom(42);
assertThrows(NullPointerException.class, () -> random.split(/* source= */ null));
+ assertThrows(NullPointerException.class, () -> random.splits(1, /* source= */ null));
+ assertThrows(NullPointerException.class, () -> random.splits(/* source= */ null));
}
@Test
- public void splitInstances_areTheSameWhenTheyAreSplitWithIdenticalSource() {
+ public void split_throwsIAE_whenSizeIsNonPositive() {
+ SplittableRandom random = new SplittableRandom(42);
+
+ assertThrows(IllegalArgumentException.class, () -> random.splits(-1, random));
+ assertThrows(IllegalArgumentException.class, () -> random.splits(-1));
+ }
+
+ @Test
+ public void splits_streamOfSizeZero_areEmpty() {
+ var random = new SplittableRandom();
+
+ assertEquals(0L, random.splits(0).count());
+ assertEquals(0L, random.splits(0, random).count());
+ }
+
+ @Test
+ public void splitInstances_areTheSame_whenTheyAreSplitWithIdenticalSource() {
var seed = 1001;
var random1 = new SplittableRandom(seed);
var random2 = new SplittableRandom(seed);
@@ -55,12 +75,76 @@ public class SplittableRandomTest {
var splitRandom1 = random1.split(new SplittableRandom(sourceSeed));
var splitRandom2 = random2.split(new SplittableRandom(sourceSeed));
- assertEquals(splitRandom1, splitRandom2);
+ assertGeneratorsAreEquals(splitRandom1, splitRandom2);
+ }
+
+ @Test
+ public void splitsInstances_areTheSame_whenTheyAreSplitWithIdenticalSource_boundedStream() {
+ var seed = 1001;
+
+ var random1 = new SplittableRandom(seed);
+ var random2 = new SplittableRandom(seed);
+
+ var sourceSeed = 9999;
+ var size = 10;
+
+ var splitRandom1 = random1.splits(size, new SplittableRandom(sourceSeed)).toList();
+ var splitRandom2 = random2.splits(size, new SplittableRandom(sourceSeed)).toList();
+
+ assertEquals(size, splitRandom1.size());
+ assertEquals(size, splitRandom2.size());
+
+ for (int i = 0; i < size; ++i) {
+ assertGeneratorsAreEquals(splitRandom1.get(i), splitRandom2.get(i));
+ }
+ }
+
+
+ @Test
+ public void splitsInstances_areTheSame_whenTheyAreSplitWithIdenticalSource_unboundedStream() {
+ var seed = 1001;
+
+ var random1 = new SplittableRandom(seed);
+ var random2 = new SplittableRandom(seed);
+
+ var sourceSeed = 9999;
+ var size = 10;
+
+ var splitRandom1 = random1.splits(new SplittableRandom(sourceSeed)).limit(size).toList();
+ var splitRandom2 = random2.splits(new SplittableRandom(sourceSeed)).limit(size).toList();
+
+ assertEquals(size, splitRandom1.size());
+ assertEquals(size, splitRandom2.size());
+
+ for (int i = 0; i < size; ++i) {
+ assertGeneratorsAreEquals(splitRandom1.get(i), splitRandom2.get(i));
+ }
+ }
+
+ @Test
+ public void splitsInstances_areTheSame_whenSourceIsIdentical() {
+ var seed = 1001;
+
+ var random1 = new SplittableRandom(seed);
+ var random2 = new SplittableRandom(seed);
+
+ var size = 10;
+
+ var splitRandom1 = random1.splits(size).toList();
+ var splitRandom2 = random2.splits(size).toList();
+
+ assertEquals(size, splitRandom1.size());
+ assertEquals(size, splitRandom2.size());
+
+ for (int i = 0; i < size; ++i) {
+ assertGeneratorsAreEquals(splitRandom1.get(i), splitRandom2.get(i));
+ }
}
- private static void assertEquals(SplittableRandom random1, SplittableRandom random2) {
+ private static void assertGeneratorsAreEquals(SplittableGenerator random1,
+ SplittableGenerator random2) {
for (int i = 0; i < 1_000; ++i) {
- Assert.assertEquals(random1.nextLong(), random2.nextLong());
+ assertEquals(random1.nextLong(), random2.nextLong());
}
}
diff --git a/luni/src/test/java/libcore/java/util/random/SplittableGeneratorTest.java b/luni/src/test/java/libcore/java/util/random/SplittableGeneratorTest.java
new file mode 100644
index 00000000000..9f0f99cc2fc
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/random/SplittableGeneratorTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.util.random;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.SplittableRandom;
+import java.util.random.RandomGenerator;
+import java.util.random.RandomGenerator.SplittableGenerator;
+import java.util.random.RandomGeneratorFactory;
+
+@RunWith(JUnit4.class)
+public class SplittableGeneratorTest {
+
+ @Test
+ public void splits_produceTheSameResult_whenSourceIsIdentical() {
+ var sourceSeed = 12345;
+ var size = 10;
+
+ SplittableGenerator random1 = new SplittableRandom(sourceSeed);
+ SplittableGenerator random2 = new SplittableRandom(sourceSeed);
+
+ var splitRandom1 = random1.splits().limit(size).toList();
+ var splitRandom2 = random2.splits().limit(size).toList();
+
+ assertEquals(size, splitRandom1.size());
+ assertEquals(size, splitRandom2.size());
+
+ for (int i = 0; i < size; ++i) {
+ assertGeneratorsAreEquals(splitRandom1.get(i), splitRandom2.get(i));
+ }
+ }
+
+ @Test
+ public void of_acceptsAllSplittablesFromRandomGeneratorFactory() {
+ RandomGeneratorFactory.all()
+ .filter(RandomGeneratorFactory::isSplittable)
+ .forEach(factory -> SplittableGenerator.of(factory.name()));
+ }
+
+ private static void assertGeneratorsAreEquals(SplittableGenerator random1,
+ SplittableGenerator random2) {
+ for (int i = 0; i < 1_000; ++i) {
+ assertEquals(random1.nextLong(), random2.nextLong());
+ }
+ }
+
+}
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.");
+ }
+ }
+ }
+}