aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDominik Stadler <centic@apache.org>2021-03-19 09:40:05 +0000
committerDominik Stadler <centic@apache.org>2021-03-19 09:40:05 +0000
commit82bf04669fa90728ccae201e145dc941188e1b92 (patch)
tree225c2767bffd7a2d8d752ff3d15bc20344138040 /src
parent2ceb5ff55346ee97c3541c9e7bcd8bc5847be9b4 (diff)
downloadpoi-82bf04669fa90728ccae201e145dc941188e1b92.tar.gz
poi-82bf04669fa90728ccae201e145dc941188e1b92.zip
Enhance javadoc and coverage for IOUtils
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1887801 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src')
-rw-r--r--src/java/org/apache/poi/util/IOUtils.java18
-rw-r--r--src/testcases/org/apache/poi/util/TestIOUtils.java224
2 files changed, 223 insertions, 19 deletions
diff --git a/src/java/org/apache/poi/util/IOUtils.java b/src/java/org/apache/poi/util/IOUtils.java
index 8e69427553..974efdf64b 100644
--- a/src/java/org/apache/poi/util/IOUtils.java
+++ b/src/java/org/apache/poi/util/IOUtils.java
@@ -232,6 +232,13 @@ public final class IOUtils {
/**
* Helper method, just calls <tt>readFully(in, b, 0, b.length)</tt>
+ *
+ * @param in the stream from which the data is read.
+ * @param b the buffer into which the data is read.
+ *
+ * @return the number of bytes read or -1 if no bytes were read
+ *
+ * @throws IOException if reading from the stream fails
*/
public static int readFully(InputStream in, byte[] b) throws IOException {
return readFully(in, b, 0, b.length);
@@ -250,6 +257,10 @@ public final class IOUtils {
* @param b the buffer into which the data is read.
* @param off the start offset in array <tt>b</tt> at which the data is written.
* @param len the maximum number of bytes to read.
+ *
+ * @return the number of bytes read or -1 if no bytes were read
+ *
+ * @throws IOException if reading from the stream fails
*/
public static int readFully(InputStream in, byte[] b, int off, int len) throws IOException {
int total = 0;
@@ -275,6 +286,13 @@ public final class IOUtils {
* number of bytes read. If the end of the file isn't reached before the
* buffer has no more remaining capacity, will return the number of bytes
* that were read.
+ *
+ * @param channel The byte-channel to read data from
+ * @param b the buffer into which the data is read.
+ *
+ * @return the number of bytes read or -1 if no bytes were read
+ *
+ * @throws IOException if reading from the stream fails
*/
public static int readFully(ReadableByteChannel channel, ByteBuffer b) throws IOException {
int total = 0;
diff --git a/src/testcases/org/apache/poi/util/TestIOUtils.java b/src/testcases/org/apache/poi/util/TestIOUtils.java
index c39efacfe2..4c9f6a1fb7 100644
--- a/src/testcases/org/apache/poi/util/TestIOUtils.java
+++ b/src/testcases/org/apache/poi/util/TestIOUtils.java
@@ -26,6 +26,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@@ -34,6 +35,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.io.PushbackInputStream;
import java.nio.ByteBuffer;
+import java.nio.channels.ReadableByteChannel;
import java.nio.charset.StandardCharsets;
import java.util.Random;
@@ -42,29 +44,25 @@ import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
-/**
- * Class to test IOUtils
- */
final class TestIOUtils {
-
private static File TMP;
- private static final long LENGTH = 300+new Random().nextInt(9000);
+ private static final long LENGTH = 300 + new Random().nextInt(9000);
@BeforeAll
public static void setUp() throws IOException {
TMP = File.createTempFile("poi-ioutils-", "");
- OutputStream os = new FileOutputStream(TMP);
- for (int i = 0; i < LENGTH; i++) {
- os.write(0x01);
+ try (OutputStream os = new FileOutputStream(TMP)) {
+ for (int i = 0; i < LENGTH; i++) {
+ os.write(0x01);
+ }
}
- os.flush();
- os.close();
-
}
@AfterAll
public static void tearDown() {
- if (TMP != null) assertTrue(TMP.delete());
+ if (TMP != null) {
+ assertTrue(TMP.delete());
+ }
}
private static InputStream data123() {
@@ -163,7 +161,7 @@ final class TestIOUtils {
void testSkipFullyByteArray() throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try (InputStream is = new FileInputStream(TMP)) {
- IOUtils.copy(is, bos);
+ assertEquals(LENGTH, IOUtils.copy(is, bos));
long skipped = IOUtils.skipFully(new ByteArrayInputStream(bos.toByteArray()), 20000L);
assertEquals(LENGTH, skipped);
}
@@ -173,13 +171,42 @@ final class TestIOUtils {
void testSkipFullyByteArrayGtIntMax() throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try (InputStream is = new FileInputStream(TMP)) {
- IOUtils.copy(is, bos);
+ assertEquals(LENGTH, IOUtils.copy(is, bos));
long skipped = IOUtils.skipFully(new ByteArrayInputStream(bos.toByteArray()), Integer.MAX_VALUE + 20000L);
assertEquals(LENGTH, skipped);
}
}
@Test
+ void testCopyToFile() throws IOException {
+ File dest = File.createTempFile("poi-ioutils-", "");
+ try {
+ try (InputStream is = new FileInputStream(TMP)) {
+ assertEquals(LENGTH, IOUtils.copy(is, dest));
+ }
+
+ try (FileInputStream strOrig = new FileInputStream(TMP);
+ FileInputStream strDest = new FileInputStream(dest)) {
+ byte[] bytesOrig = new byte[(int)LENGTH];
+ byte[] bytesDest = new byte[(int)LENGTH];
+ IOUtils.readFully(strOrig, bytesOrig);
+ IOUtils.readFully(strDest, bytesDest);
+ assertArrayEquals(bytesOrig, bytesDest);
+ }
+ } finally {
+ assertTrue(dest.delete());
+ }
+ }
+
+ @Test
+ void testCopyToInvalidFile() throws IOException {
+ try (InputStream is = new FileInputStream(TMP)) {
+ assertThrows(RuntimeException.class,
+ () -> IOUtils.copy(is, new File("/notexisting/directory/structure")));
+ }
+ }
+
+ @Test
void testSkipFullyBug61294() throws IOException {
long skipped = IOUtils.skipFully(new ByteArrayInputStream(new byte[0]), 1);
assertEquals(-1L, skipped);
@@ -292,7 +319,7 @@ final class TestIOUtils {
}
@Test
- void testSetMaxOverrideOverLimitWithLength() throws IOException {
+ void testSetMaxOverrideOverLimitWithLength() {
IOUtils.setByteArrayMaxOverride(2);
try {
ByteArrayInputStream stream = new ByteArrayInputStream("abc".getBytes(StandardCharsets.UTF_8));
@@ -324,31 +351,92 @@ final class TestIOUtils {
@Test
void testReadFully() throws IOException {
byte[] bytes = new byte[2];
- IOUtils.readFully(new ByteArrayInputStream(new byte[] {1, 2, 3}), bytes, 0, 2);
+ assertEquals(2, IOUtils.readFully(new ByteArrayInputStream(new byte[] {1, 2, 3}), bytes, 0, 2));
assertArrayEquals(new byte[] {1,2}, bytes);
}
@Test
+ void testReadFullyEOF() throws IOException {
+ byte[] bytes = new byte[2];
+ assertEquals(2, IOUtils.readFully(new NullInputStream(2), bytes, 0, 4));
+ assertArrayEquals(new byte[] {0,0}, bytes);
+ }
+
+ @Test
+ void testReadFullyEOFZero() throws IOException {
+ byte[] bytes = new byte[2];
+ assertEquals(-1, IOUtils.readFully(new NullInputStream(0), bytes, 0, 4));
+ assertArrayEquals(new byte[] {0,0}, bytes);
+ }
+
+ @Test
void testReadFullySimple() throws IOException {
byte[] bytes = new byte[2];
- IOUtils.readFully(new ByteArrayInputStream(new byte[] {1, 2, 3}), bytes);
+ assertEquals(2, IOUtils.readFully(new ByteArrayInputStream(new byte[] {1, 2, 3}), bytes));
assertArrayEquals(new byte[] {1,2}, bytes);
}
@Test
void testReadFullyOffset() throws IOException {
byte[] bytes = new byte[3];
- IOUtils.readFully(new ByteArrayInputStream(new byte[] {1, 2, 3}), bytes, 1, 2);
+ assertEquals(2, IOUtils.readFully(new ByteArrayInputStream(new byte[] {1, 2, 3}), bytes, 1, 2));
assertArrayEquals(new byte[] {0, 1,2}, bytes);
}
@Test
void testReadFullyAtLength() throws IOException {
byte[] bytes = new byte[3];
- IOUtils.readFully(new ByteArrayInputStream(new byte[] {1, 2, 3}), bytes, 0, 3);
+ assertEquals(3, IOUtils.readFully(new ByteArrayInputStream(new byte[] {1, 2, 3}), bytes, 0, 3));
assertArrayEquals(new byte[] {1,2, 3}, bytes);
}
+
+ @Test
+ void testReadFullyChannel() throws IOException {
+ ByteBuffer bytes = ByteBuffer.allocate(2);
+ assertEquals(2, IOUtils.readFully(new SimpleByteChannel(new byte[]{1, 2, 3}), bytes));
+ assertArrayEquals(new byte[] {1,2}, bytes.array());
+ assertEquals(2, bytes.position());
+ }
+
+ @Test
+ void testReadFullyChannelEOF() throws IOException {
+ ByteBuffer bytes = ByteBuffer.allocate(2);
+ assertEquals(-1, IOUtils.readFully(new EOFByteChannel(false), bytes));
+ assertArrayEquals(new byte[] {0,0}, bytes.array());
+ assertEquals(0, bytes.position());
+ }
+
+ @Test
+ void testReadFullyChannelEOFException() {
+ ByteBuffer bytes = ByteBuffer.allocate(2);
+ assertThrows(IOException.class,
+ () -> IOUtils.readFully(new EOFByteChannel(true), bytes));
+ }
+
+ @Test
+ void testReadFullyChannelSimple() throws IOException {
+ ByteBuffer bytes = ByteBuffer.allocate(2);
+ assertEquals(2, IOUtils.readFully(new SimpleByteChannel(new byte[] {1, 2, 3}), bytes));
+ assertArrayEquals(new byte[] {1,2}, bytes.array());
+ assertEquals(2, bytes.position());
+ }
+
+ @Test
+ public void testChecksum() {
+ assertEquals(0L, IOUtils.calculateChecksum(new byte[0]));
+ assertEquals(3057449933L, IOUtils.calculateChecksum(new byte[] { 1, 2, 3, 4}));
+ }
+
+ @Test
+ public void testChecksumStream() throws IOException {
+ assertEquals(0L, IOUtils.calculateChecksum(new NullInputStream(0)));
+ assertEquals(0L, IOUtils.calculateChecksum(new NullInputStream(1)));
+ assertEquals(3057449933L, IOUtils.calculateChecksum(new ByteArrayInputStream(new byte[] { 1, 2, 3, 4})));
+ assertThrows(EOFException.class,
+ () -> IOUtils.calculateChecksum(new NullInputStream(1, true)));
+ }
+
/**
* This returns 0 for the first call to skip and then reads
* as requested. This tests that the fallback to read() works.
@@ -386,4 +474,102 @@ final class TestIOUtils {
return 100000;
}
}
+
+ private static class EOFByteChannel implements ReadableByteChannel {
+ private final boolean throwException;
+
+ public EOFByteChannel(boolean throwException) {
+ this.throwException = throwException;
+ }
+
+ @Override
+ public int read(ByteBuffer dst) throws IOException {
+ if (throwException) {
+ throw new IOException("EOF");
+ }
+
+ return -1;
+ }
+
+ @Override
+ public boolean isOpen() {
+ return false;
+ }
+
+ @Override
+ public void close() throws IOException {
+
+ }
+ }
+
+ private static class SimpleByteChannel extends InputStream implements ReadableByteChannel {
+ private final byte[] bytes;
+
+ public SimpleByteChannel(byte[] bytes) {
+ this.bytes = bytes;
+ }
+
+ @Override
+ public int read() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int read(ByteBuffer dst) throws IOException {
+ int toRead = Math.min(bytes.length, dst.capacity());
+ dst.put(bytes, 0, toRead);
+ return toRead;
+ }
+
+ @Override
+ public boolean isOpen() {
+ return false;
+ }
+ }
+
+ public class NullInputStream extends InputStream {
+ private final int bytes;
+ private final boolean exception;
+
+ private int position;
+
+ public NullInputStream(int bytes) {
+ this(bytes, false);
+ }
+
+ public NullInputStream(int bytes, boolean exception) {
+ this.bytes = bytes;
+ this.exception = exception;
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (position >= bytes) {
+ return handleReturn();
+ }
+
+ position++;
+ return 0;
+ }
+
+ private int handleReturn() throws EOFException {
+ if (exception) {
+ throw new EOFException();
+ } else {
+ return -1;
+ }
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ int toRead = Math.min(b.length, len);
+ if (toRead > (bytes - position)) {
+ return handleReturn();
+ }
+ toRead = Math.min(toRead, (bytes - position));
+
+ position += toRead;
+ return toRead;
+ }
+ }
}