/* * 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 8268435 8274780 * @summary Verify ChannelInputStream methods readAllBytes and readNBytes * @requires (sun.arch.data.model == "64" & os.maxMemory >= 16g) * @library .. * @library /test/lib * @build jdk.test.lib.RandomFactory * @modules java.base/jdk.internal.util * @run testng/othervm/timeout=900 -Xmx12G ReadXBytes * @key randomness */ import java.io.File; import java.io.FileInputStream; import java.io.FilterInputStream; import java.io.InputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.Channel; import java.nio.channels.Channels; import java.nio.channels.FileChannel; import java.nio.channels.ReadableByteChannel; import java.nio.channels.SeekableByteChannel; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import java.util.Random; import jdk.internal.util.ArraysSupport; import static java.nio.file.StandardOpenOption.*; import jdk.test.lib.RandomFactory; import org.testng.Assert; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertThrows; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; public class ReadXBytes { private static final Random RAND = RandomFactory.getRandom(); // The largest source from which to read all bytes private static final int BIG_LENGTH = ArraysSupport.SOFT_MAX_ARRAY_LENGTH; // A length greater than a 32-bit integer can accommodate private static final long HUGE_LENGTH = Integer.MAX_VALUE + 27L; // Current directory private static final Path DIR = Path.of(System.getProperty("test.dir", ".")); // --- Framework --- // Create a temporary file path static Path createFilePath() { String name = String.format("ReadXBytes%d.tmp", System.nanoTime()); return DIR.resolve(name); } // Creates a temporary file of a specified length with undefined content static Path createFile(long length) throws IOException { Path path = createFilePath(); path.toFile().deleteOnExit(); try (FileChannel fc = FileChannel.open(path, CREATE_NEW, SPARSE, WRITE)) { if (length > 0) { fc.position(length - 1); fc.write(ByteBuffer.wrap(new byte[] {27})); } } return path; } // Creates a temporary file of a specified length with random content static Path createFileWithRandomContent(long length) throws IOException { Path file = createFile(length); try (FileChannel fc = FileChannel.open(file, WRITE)) { if (length < 65536) { // if the length is less than 64k, write the entire file byte[] buf = new byte[(int)length]; RAND.nextBytes(buf); ByteBuffer bb = ByteBuffer.wrap(buf); while (bb.hasRemaining()) { fc.write(bb); } } else { // write the first and the last 32k only byte[] buf = new byte[32768]; RAND.nextBytes(buf); ByteBuffer bb = ByteBuffer.wrap(buf); while (bb.hasRemaining()) { fc.write(bb); } bb.clear(); fc.position(length - buf.length); RAND.nextBytes(buf); while (bb.hasRemaining()) { fc.write(bb); } } } return file; } // Creates a file of a specified length @FunctionalInterface interface FileCreator { Path create(long length) throws IOException; } // Performs a test for checking edge cases @FunctionalInterface interface EdgeTest { void test(long length, InputStream source) throws IOException; } // Performs a test for evaluating correctness of content @FunctionalInterface interface DataTest { void test(long length, InputStream source, InputStream reference) throws IOException; } // Construct for testing zero length, EOF, and IAE public void edgeTest(long length, FileCreator c, EdgeTest f) throws IOException { Path file = c.create(length); try (FileChannel fc = FileChannel.open(file, READ); InputStream cis = Channels.newInputStream(fc)) { f.test(length, cis); } finally { Files.delete(file); } } // Construct for testing correctness of content public void dataTest(long length, FileCreator c, DataTest f) throws IOException { Path file = c.create(length); try { for (boolean seekable : List.of(false, true)) { try (FileInputStream fis = new FileInputStream(file.toFile())) { ReadableByteChannel ch; if (seekable) { ch = FileChannel.open(file, READ); } else { InputStream fis2 = new FileInputStream(file.toFile()); ch = Channels.newChannel(new FilterInputStream(fis2) {}); assertFalse(ch instanceof SeekableByteChannel); } try (InputStream cis = Channels.newInputStream(ch)) { f.test(length, cis, fis); } } } } finally { Files.delete(file); } } // --- readAllBytes tests --- // Verifies readAllBytes() behavior for an empty file @Test public void readAllBytesFromEmptyFile() throws IOException { edgeTest(0L, (length) -> createFile(length), (length, cis) -> { byte[] bytes = cis.readAllBytes(); assertNotNull(bytes); assertEquals(bytes.length, 0L); } ); } // Verifies readAllBytes() behavior at EOF @Test public void readAllBytesAtEOF() throws IOException { edgeTest(RAND.nextInt(Short.MAX_VALUE), (length) -> createFile(length), (length, cis) -> { cis.skipNBytes(length); byte[] bytes = cis.readAllBytes(); assertNotNull(bytes); assertEquals(bytes.length, 0); } ); } // Verifies readAllBytes() behavior for a maximal length source @Test public void readAllBytesFromMaxLengthFile() throws IOException { dataTest(BIG_LENGTH, (length) -> createFileWithRandomContent(length), (length, cis, fis) -> { byte[] cisBytes = cis.readAllBytes(); assertNotNull(cisBytes); assertEquals(cisBytes.length, (long)length); byte[] fisBytes = fis.readAllBytes(); assertEquals(cisBytes, fisBytes); } ); } // Verifies readAllBytes() throws OOME if the source is too large @Test public void readAllBytesFromBeyondMaxLengthFile() throws IOException { dataTest(HUGE_LENGTH, (length) -> createFile(length), (length, cis, fis) -> { assertThrows(OutOfMemoryError.class, () -> {cis.readAllBytes();}); } ); } // Provides an array of lengths @DataProvider public Object[][] lengthProvider() throws IOException { return new Object[][] { {1 + RAND.nextInt(1)}, {1 + RAND.nextInt(Byte.MAX_VALUE)}, {1 + RAND.nextInt(Short.MAX_VALUE)}, {1 + RAND.nextInt(1_000_000)}, {1 + RAND.nextInt(BIG_LENGTH)} }; } // Verifies readAllBytes() accuracy for random lengths and initial positions @Test(dataProvider = "lengthProvider") public void readAllBytes(int len) throws IOException { dataTest(len, (length) -> createFileWithRandomContent(length), (length, cis, fis) -> { long position = RAND.nextInt(Math.toIntExact(length)); cis.skipNBytes(position); byte[] cisBytes = cis.readAllBytes(); assertNotNull(cisBytes); assertEquals(cisBytes.length, length - position); fis.skipNBytes(position); byte[] fisBytes = fis.readAllBytes(); assertEquals(cisBytes, fisBytes); } ); } // --- readNBytes tests --- // Verifies readNBytes() behavior for a negative length @Test public void readNBytesWithNegativeLength() throws IOException { edgeTest(0L, (length) -> createFile(length), (length, cis) -> { assertThrows(IllegalArgumentException.class, () -> {cis.readNBytes(-1);}); } ); } // Verifies readNBytes() for an empty file @Test public void readNBytesFromEmptyFile() throws IOException { edgeTest(0L, (length) -> createFile(length), (length, cis) -> { byte[] bytes = cis.readNBytes(1); assertNotNull(bytes); assertEquals(bytes.length, 0); } ); } // Verifies readNBytes() behavior at EOF @Test public void readNBytesAtEOF() throws IOException { edgeTest(RAND.nextInt(Short.MAX_VALUE), (length) -> createFile(length), (length, cis) -> { cis.skipNBytes(length); byte[] bytes = cis.readNBytes(1); assertNotNull(bytes); assertEquals(bytes.length, 0); } ); } // Verifies readNBytes() behavior for a maximal length source @Test public void readNBytesFromMaxLengthFile() throws IOException { dataTest(BIG_LENGTH, (length) -> createFileWithRandomContent(length), (length, cis, fis) -> { byte[] cisBytes = cis.readNBytes(BIG_LENGTH); assertNotNull(cisBytes); assertEquals(cisBytes.length, (long)length); byte[] fisBytes = fis.readNBytes(BIG_LENGTH); assertEquals(cisBytes, fisBytes); } ); } // Verifies readNBytes() beyond the maximum length source @Test public void readNBytesFromBeyondMaxLengthFile() throws IOException { dataTest(HUGE_LENGTH, (length) -> createFileWithRandomContent(length), (length, cis, fis) -> { assertTrue(BIG_LENGTH < length, length + " >= " + HUGE_LENGTH); int n = Math.toIntExact(length - BIG_LENGTH); cis.skipNBytes(BIG_LENGTH); byte[] cisBytes = cis.readNBytes(n); assertNotNull(cisBytes); assertEquals(cisBytes.length, n); fis.skipNBytes(BIG_LENGTH); byte[] fisBytes = fis.readNBytes(n); assertEquals(cisBytes, fisBytes); } ); } // Verifies readNBytes() accuracy for random lengths and initial positions @Test(dataProvider = "lengthProvider") public void readNBytes(int len) throws IOException { dataTest(len, (length) -> createFileWithRandomContent(length), (length, cis, fis) -> { int ilen = Math.toIntExact(len); int position = RAND.nextInt(ilen); int n = RAND.nextInt(ilen - position); cis.skipNBytes(position); byte[] cisBytes = cis.readNBytes(n); assertNotNull(cisBytes); assertEquals(cisBytes.length, n); fis.skipNBytes(position); byte[] fisBytes = fis.readNBytes(n); assertEquals(cisBytes, fisBytes); } ); } }