706d1b7508
Reviewed-by: alanb, bpb
357 lines
13 KiB
Java
357 lines
13 KiB
Java
/*
|
|
* 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);
|
|
}
|
|
);
|
|
}
|
|
}
|