This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-io.git
The following commit(s) were added to refs/heads/master by this push: new 87da775 [IO-651] Add DeferredFileOutputStream.toInputStream() #206. 87da775 is described below commit 87da775d2f0aa9d3382ac30b9dd7e793c3c6f9e5 Author: Gary Gregory <gardgreg...@gmail.com> AuthorDate: Mon Feb 22 14:58:24 2021 -0500 [IO-651] Add DeferredFileOutputStream.toInputStream() #206. - Update @since tag. - Sort members. --- src/changes/changes.xml | 7 +- .../io/output/DeferredFileOutputStream.java | 190 ++++++------ .../io/output/DeferredFileOutputStreamTest.java | 340 ++++++++++----------- 3 files changed, 270 insertions(+), 267 deletions(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 3cfaf6a..69dc2a2 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -68,7 +68,7 @@ The <action> type attribute can be add,update,fix,remove. </action> <action issue="IO-597" dev="ggregory" type="fix" due-to="Gary Gregory, Arvind, Rob Spoor"> FileUtils.iterateFiles runs out of memory when executed for a directory with large number of files. - Re-implement FileUtils' iterateFiles(), iterateFilesAndDirs(), listFiles(), listFilesAndDirs() to use NIO + Re-implement FileUtils' iterateFiles(), iterateFilesAndDirs(), listFiles(), listFilesAndDirs() to use NIO file tree walking instead of IO file listings to avoid memory consumption issues on large file trees. </action> <action dev="ggregory" type="fix" due-to="Gary Gregory"> @@ -193,6 +193,9 @@ The <action> type attribute can be add,update,fix,remove. <action dev="ggregory" type="add" due-to="Gary Gregory"> Add copy(URL, OutputStream). </action> + <action issue="IO-651" dev="ggregory" type="add" due-to="jmark109, Gary Gregory"> + Add DeferredFileOutputStream.toInputStream() #206. + </action> <!-- UPDATES --> <action dev="ggregory" type="update" due-to="Dependabot"> Update junit-jupiter from 5.6.2 to 5.7.0 #153. @@ -385,7 +388,7 @@ The <action> type attribute can be add,update,fix,remove. </action> <action type="update" dev="ggregory" due-to="Gary Gregory"> Update com.github.siom79.japicmp:japicmp-maven-plugin 0.14.3 -> 0.14.4. - </action> + </action> </release> <!-- The release date is the date RC is cut --> <release version="2.7" date="2020-05-24" description="Java 8 required."> diff --git a/src/main/java/org/apache/commons/io/output/DeferredFileOutputStream.java b/src/main/java/org/apache/commons/io/output/DeferredFileOutputStream.java index d6dac39..a99c01a 100644 --- a/src/main/java/org/apache/commons/io/output/DeferredFileOutputStream.java +++ b/src/main/java/org/apache/commons/io/output/DeferredFileOutputStream.java @@ -87,6 +87,29 @@ public class DeferredFileOutputStream extends ThresholdingOutputStream { } /** + * Constructs an instance of this class which will trigger an event at the specified threshold, and save data either + * to a file beyond that point. + * + * @param threshold The number of bytes at which to trigger an event. + * @param outputFile The file to which data is saved beyond the threshold. + * @param prefix Prefix to use for the temporary file. + * @param suffix Suffix to use for the temporary file. + * @param directory Temporary file directory. + * @param initialBufferSize The initial size of the in memory buffer. + */ + private DeferredFileOutputStream(final int threshold, final File outputFile, final String prefix, + final String suffix, final File directory, final int initialBufferSize) { + super(threshold); + this.outputFile = outputFile; + this.prefix = prefix; + this.suffix = suffix; + this.directory = directory; + + memoryOutputStream = new ByteArrayOutputStream(initialBufferSize); + currentOutputStream = memoryOutputStream; + } + + /** * Constructs an instance of this class which will trigger an event at the specified threshold, and save data to a * file beyond that point. * @@ -105,26 +128,6 @@ public class DeferredFileOutputStream extends ThresholdingOutputStream { /** * Constructs an instance of this class which will trigger an event at the specified threshold, and save data to a - * temporary file beyond that point. The initial buffer size will default to 32 bytes which is - * ByteArrayOutputStream's default buffer size. - * - * @param threshold The number of bytes at which to trigger an event. - * @param prefix Prefix to use for the temporary file. - * @param suffix Suffix to use for the temporary file. - * @param directory Temporary file directory. - * - * @since 1.4 - */ - public DeferredFileOutputStream(final int threshold, final String prefix, final String suffix, - final File directory) { - this(threshold, null, prefix, suffix, directory, AbstractByteArrayOutputStream.DEFAULT_SIZE); - if (prefix == null) { - throw new IllegalArgumentException("Temporary file prefix is missing"); - } - } - - /** - * Constructs an instance of this class which will trigger an event at the specified threshold, and save data to a * temporary file beyond that point. * * @param threshold The number of bytes at which to trigger an event. @@ -147,72 +150,34 @@ public class DeferredFileOutputStream extends ThresholdingOutputStream { } /** - * Constructs an instance of this class which will trigger an event at the specified threshold, and save data either - * to a file beyond that point. + * Constructs an instance of this class which will trigger an event at the specified threshold, and save data to a + * temporary file beyond that point. The initial buffer size will default to 32 bytes which is + * ByteArrayOutputStream's default buffer size. * * @param threshold The number of bytes at which to trigger an event. - * @param outputFile The file to which data is saved beyond the threshold. * @param prefix Prefix to use for the temporary file. * @param suffix Suffix to use for the temporary file. * @param directory Temporary file directory. - * @param initialBufferSize The initial size of the in memory buffer. - */ - private DeferredFileOutputStream(final int threshold, final File outputFile, final String prefix, - final String suffix, final File directory, final int initialBufferSize) { - super(threshold); - this.outputFile = outputFile; - this.prefix = prefix; - this.suffix = suffix; - this.directory = directory; - - memoryOutputStream = new ByteArrayOutputStream(initialBufferSize); - currentOutputStream = memoryOutputStream; - } - - /** - * Returns the current output stream. This may be memory based or disk based, depending on the current state with - * respect to the threshold. - * - * @return The underlying output stream. * - * @throws IOException if an error occurs. + * @since 1.4 */ - @Override - protected OutputStream getStream() throws IOException { - return currentOutputStream; + public DeferredFileOutputStream(final int threshold, final String prefix, final String suffix, + final File directory) { + this(threshold, null, prefix, suffix, directory, AbstractByteArrayOutputStream.DEFAULT_SIZE); + if (prefix == null) { + throw new IllegalArgumentException("Temporary file prefix is missing"); + } } /** - * Switches the underlying output stream from a memory based stream to one that is backed by disk. This is the point - * at which we realize that too much data is being written to keep in memory, so we elect to switch to disk-based - * storage. + * Closes underlying output stream, and mark this as closed * * @throws IOException if an error occurs. */ @Override - protected void thresholdReached() throws IOException { - if (prefix != null) { - outputFile = File.createTempFile(prefix, suffix, directory); - } - FileUtils.forceMkdirParent(outputFile); - final FileOutputStream fos = new FileOutputStream(outputFile); - try { - memoryOutputStream.writeTo(fos); - } catch (final IOException e) { - fos.close(); - throw e; - } - currentOutputStream = fos; - memoryOutputStream = null; - } - - /** - * Determines whether or not the data for this output stream has been retained in memory. - * - * @return {@code true} if the data is available in memory; {@code false} otherwise. - */ - public boolean isInMemory() { - return !isThresholdExceeded(); + public void close() throws IOException { + super.close(); + closed = true; } /** @@ -241,38 +206,49 @@ public class DeferredFileOutputStream extends ThresholdingOutputStream { } /** - * Closes underlying output stream, and mark this as closed + * Returns the current output stream. This may be memory based or disk based, depending on the current state with + * respect to the threshold. + * + * @return The underlying output stream. * * @throws IOException if an error occurs. */ @Override - public void close() throws IOException { - super.close(); - closed = true; + protected OutputStream getStream() throws IOException { + return currentOutputStream; } /** - * Writes the data from this output stream to the specified output stream, after it has been closed. + * Determines whether or not the data for this output stream has been retained in memory. * - * @param outputStream output stream to write to. - * @throws NullPointerException if the OutputStream is {@code null}. - * @throws IOException if this stream is not yet closed or an error occurs. + * @return {@code true} if the data is available in memory; {@code false} otherwise. */ - public void writeTo(final OutputStream outputStream) throws IOException { - // we may only need to check if this is closed if we are working with a file - // but we should force the habit of closing whether we are working with - // a file or memory. - if (!closed) { - throw new IOException("Stream not closed"); - } + public boolean isInMemory() { + return !isThresholdExceeded(); + } - if (isInMemory()) { - memoryOutputStream.writeTo(outputStream); - } else { - try (FileInputStream fis = new FileInputStream(outputFile)) { - IOUtils.copy(fis, outputStream); - } + /** + * Switches the underlying output stream from a memory based stream to one that is backed by disk. This is the point + * at which we realize that too much data is being written to keep in memory, so we elect to switch to disk-based + * storage. + * + * @throws IOException if an error occurs. + */ + @Override + protected void thresholdReached() throws IOException { + if (prefix != null) { + outputFile = File.createTempFile(prefix, suffix, directory); } + FileUtils.forceMkdirParent(outputFile); + final FileOutputStream fos = new FileOutputStream(outputFile); + try { + memoryOutputStream.writeTo(fos); + } catch (final IOException e) { + fos.close(); + throw e; + } + currentOutputStream = fos; + memoryOutputStream = null; } /** @@ -286,8 +262,8 @@ public class DeferredFileOutputStream extends ThresholdingOutputStream { * @return the current contents of this output stream. * @throws IOException if this stream is not yet closed or an error occurs. * @see org.apache.commons.io.output.ByteArrayOutputStream#toInputStream() - * - * @since 2.9 + * + * @since 2.9.0 */ public InputStream toInputStream() throws IOException { // we may only need to check if this is closed if we are working with a file @@ -303,4 +279,28 @@ public class DeferredFileOutputStream extends ThresholdingOutputStream { return Files.newInputStream(outputFile.toPath()); } } + + /** + * Writes the data from this output stream to the specified output stream, after it has been closed. + * + * @param outputStream output stream to write to. + * @throws NullPointerException if the OutputStream is {@code null}. + * @throws IOException if this stream is not yet closed or an error occurs. + */ + public void writeTo(final OutputStream outputStream) throws IOException { + // we may only need to check if this is closed if we are working with a file + // but we should force the habit of closing whether we are working with + // a file or memory. + if (!closed) { + throw new IOException("Stream not closed"); + } + + if (isInMemory()) { + memoryOutputStream.writeTo(outputStream); + } else { + try (FileInputStream fis = new FileInputStream(outputFile)) { + IOUtils.copy(fis, outputStream); + } + } + } } diff --git a/src/test/java/org/apache/commons/io/output/DeferredFileOutputStreamTest.java b/src/test/java/org/apache/commons/io/output/DeferredFileOutputStreamTest.java index 23a1c35..8f0d509 100644 --- a/src/test/java/org/apache/commons/io/output/DeferredFileOutputStreamTest.java +++ b/src/test/java/org/apache/commons/io/output/DeferredFileOutputStreamTest.java @@ -60,48 +60,6 @@ public class DeferredFileOutputStreamTest { private final byte[] testBytes = testString.getBytes(); /** - * Tests the case where the amount of data falls below the threshold, and is therefore confined to memory. - */ - @ParameterizedTest(name = "initialBufferSize = {0}") - @MethodSource("data") - public void testBelowThreshold(final int initialBufferSize) { - final DeferredFileOutputStream dfos = new DeferredFileOutputStream(testBytes.length + 42, initialBufferSize, - null); - try { - dfos.write(testBytes, 0, testBytes.length); - dfos.close(); - } catch (final IOException e) { - fail("Unexpected IOException"); - } - assertTrue(dfos.isInMemory()); - - final byte[] resultBytes = dfos.getData(); - assertEquals(testBytes.length, resultBytes.length); - assertTrue(Arrays.equals(resultBytes, testBytes)); - } - - /** - * Tests the case where the amount of data is exactly the same as the threshold. The behavior should be the same as - * that for the amount of data being below (i.e. not exceeding) the threshold. - */ - @ParameterizedTest(name = "initialBufferSize = {0}") - @MethodSource("data") - public void testAtThreshold(final int initialBufferSize) { - final DeferredFileOutputStream dfos = new DeferredFileOutputStream(testBytes.length, initialBufferSize, null); - try { - dfos.write(testBytes, 0, testBytes.length); - dfos.close(); - } catch (final IOException e) { - fail("Unexpected IOException"); - } - assertTrue(dfos.isInMemory()); - - final byte[] resultBytes = dfos.getData(); - assertEquals(testBytes.length, resultBytes.length); - assertTrue(Arrays.equals(resultBytes, testBytes)); - } - - /** * Tests the case where the amount of data exceeds the threshold, and is therefore written to disk. The actual data * written to disk is verified, as is the file itself. */ @@ -131,132 +89,86 @@ public class DeferredFileOutputStreamTest { } /** - * Tests the case where there are multiple writes beyond the threshold, to ensure that the - * {@code thresholdReached()} method is only called once, as the threshold is crossed for the first time. + * Tests the case where the amount of data exceeds the threshold, and is therefore written to disk. The actual data + * written to disk is verified, as is the file itself. + * Testing the getInputStream() method. */ @ParameterizedTest(name = "initialBufferSize = {0}") @MethodSource("data") - public void testThresholdReached(final int initialBufferSize) { - final File testFile = new File("testThresholdReached.dat"); - - // Ensure that the test starts from a clean base. - testFile.delete(); + public void testAboveThresholdGetInputStream(final int initialBufferSize, final @TempDir Path tempDir) throws IOException { + final File testFile = tempDir.resolve("testAboveThreshold.dat").toFile(); - final DeferredFileOutputStream dfos = new DeferredFileOutputStream(testBytes.length / 2, initialBufferSize, + final DeferredFileOutputStream dfos = new DeferredFileOutputStream(testBytes.length - 5, initialBufferSize, testFile); - final int chunkSize = testBytes.length / 3; + dfos.write(testBytes, 0, testBytes.length); + dfos.close(); + assertFalse(dfos.isInMemory()); - try { - dfos.write(testBytes, 0, chunkSize); - dfos.write(testBytes, chunkSize, chunkSize); - dfos.write(testBytes, chunkSize * 2, testBytes.length - chunkSize * 2); - dfos.close(); - } catch (final IOException e) { - fail("Unexpected IOException"); + try (InputStream is = dfos.toInputStream()) { + assertArrayEquals(testBytes, IOUtils.toByteArray(is)); } - assertFalse(dfos.isInMemory()); - assertNull(dfos.getData()); verifyResultFile(testFile); - - // Ensure that the test starts from a clean base. - testFile.delete(); } /** - * Test whether writeTo() properly writes small content. + * Tests the case where the amount of data is exactly the same as the threshold. The behavior should be the same as + * that for the amount of data being below (i.e. not exceeding) the threshold. */ @ParameterizedTest(name = "initialBufferSize = {0}") @MethodSource("data") - public void testWriteToSmall(final int initialBufferSize) { - final File testFile = new File("testWriteToMem.dat"); - final ByteArrayOutputStream baos = new ByteArrayOutputStream(initialBufferSize); - // Ensure that the test starts from a clean base. - testFile.delete(); - - final DeferredFileOutputStream dfos = new DeferredFileOutputStream(testBytes.length * 2, initialBufferSize, - testFile); + public void testAtThreshold(final int initialBufferSize) { + final DeferredFileOutputStream dfos = new DeferredFileOutputStream(testBytes.length, initialBufferSize, null); try { - dfos.write(testBytes); - - assertFalse(testFile.exists()); - assertTrue(dfos.isInMemory()); - - try { - dfos.writeTo(baos); - fail("Should not have been able to write before closing"); - } catch (final IOException ioe) { - // ok, as expected - } - + dfos.write(testBytes, 0, testBytes.length); dfos.close(); - dfos.writeTo(baos); - } catch (final IOException ioe) { + } catch (final IOException e) { fail("Unexpected IOException"); } - final byte[] copiedBytes = baos.toByteArray(); - assertTrue(Arrays.equals(testBytes, copiedBytes)); + assertTrue(dfos.isInMemory()); - testFile.delete(); + final byte[] resultBytes = dfos.getData(); + assertEquals(testBytes.length, resultBytes.length); + assertTrue(Arrays.equals(resultBytes, testBytes)); } /** - * Test whether writeTo() properly writes large content. + * Tests the case where the amount of data falls below the threshold, and is therefore confined to memory. */ @ParameterizedTest(name = "initialBufferSize = {0}") @MethodSource("data") - public void testWriteToLarge(final int initialBufferSize) { - final File testFile = new File("testWriteToFile.dat"); - final ByteArrayOutputStream baos = new ByteArrayOutputStream(initialBufferSize); - // Ensure that the test starts from a clean base. - testFile.delete(); - - final DeferredFileOutputStream dfos = new DeferredFileOutputStream(testBytes.length / 2, testFile); + public void testBelowThreshold(final int initialBufferSize) { + final DeferredFileOutputStream dfos = new DeferredFileOutputStream(testBytes.length + 42, initialBufferSize, + null); try { - dfos.write(testBytes); - - assertTrue(testFile.exists()); - assertFalse(dfos.isInMemory()); - - try { - dfos.writeTo(baos); - fail("Should not have been able to write before closing"); - } catch (final IOException ioe) { - // ok, as expected - } - + dfos.write(testBytes, 0, testBytes.length); dfos.close(); - dfos.writeTo(baos); - } catch (final IOException ioe) { + } catch (final IOException e) { fail("Unexpected IOException"); } - final byte[] copiedBytes = baos.toByteArray(); - assertTrue(Arrays.equals(testBytes, copiedBytes)); - verifyResultFile(testFile); - testFile.delete(); + assertTrue(dfos.isInMemory()); + + final byte[] resultBytes = dfos.getData(); + assertEquals(testBytes.length, resultBytes.length); + assertTrue(Arrays.equals(resultBytes, testBytes)); } /** - * Test specifying a temporary file and the threshold not reached. + * Tests the case where the amount of data falls below the threshold, and is therefore confined to memory. + * Testing the getInputStream() method. */ @ParameterizedTest(name = "initialBufferSize = {0}") @MethodSource("data") - public void testTempFileBelowThreshold(final int initialBufferSize) { - - final String prefix = "commons-io-test"; - final String suffix = ".out"; - final File tempDir = new File("."); + public void testBelowThresholdGetInputStream(final int initialBufferSize) throws IOException { final DeferredFileOutputStream dfos = new DeferredFileOutputStream(testBytes.length + 42, initialBufferSize, - prefix, suffix, tempDir); - assertNull(dfos.getFile(), "Check file is null-A"); - try { - dfos.write(testBytes, 0, testBytes.length); - dfos.close(); - } catch (final IOException e) { - fail("Unexpected IOException"); - } + null); + dfos.write(testBytes, 0, testBytes.length); + dfos.close(); assertTrue(dfos.isInMemory()); - assertNull(dfos.getFile(), "Check file is null-B"); + + try (InputStream is = dfos.toInputStream()) { + assertArrayEquals(testBytes, IOUtils.toByteArray(is)); + } } /** @@ -325,6 +237,29 @@ public class DeferredFileOutputStreamTest { } /** + * Test specifying a temporary file and the threshold not reached. + */ + @ParameterizedTest(name = "initialBufferSize = {0}") + @MethodSource("data") + public void testTempFileBelowThreshold(final int initialBufferSize) { + + final String prefix = "commons-io-test"; + final String suffix = ".out"; + final File tempDir = new File("."); + final DeferredFileOutputStream dfos = new DeferredFileOutputStream(testBytes.length + 42, initialBufferSize, + prefix, suffix, tempDir); + assertNull(dfos.getFile(), "Check file is null-A"); + try { + dfos.write(testBytes, 0, testBytes.length); + dfos.close(); + } catch (final IOException e) { + fail("Unexpected IOException"); + } + assertTrue(dfos.isInMemory()); + assertNull(dfos.getFile(), "Check file is null-B"); + } + + /** * Test specifying a temporary file and the threshold is reached. * * @throws Exception @@ -344,71 +279,136 @@ public class DeferredFileOutputStreamTest { } /** - * Verifies that the specified file contains the same data as the original test data. - * - * @param testFile The file containing the test output. + * Tests the case where there are multiple writes beyond the threshold, to ensure that the + * {@code thresholdReached()} method is only called once, as the threshold is crossed for the first time. */ - private void verifyResultFile(final File testFile) { - try { - final FileInputStream fis = new FileInputStream(testFile); - assertEquals(testBytes.length, fis.available()); + @ParameterizedTest(name = "initialBufferSize = {0}") + @MethodSource("data") + public void testThresholdReached(final int initialBufferSize) { + final File testFile = new File("testThresholdReached.dat"); - final byte[] resultBytes = new byte[testBytes.length]; - assertEquals(testBytes.length, fis.read(resultBytes)); + // Ensure that the test starts from a clean base. + testFile.delete(); - assertTrue(Arrays.equals(resultBytes, testBytes)); - assertEquals(-1, fis.read(resultBytes)); + final DeferredFileOutputStream dfos = new DeferredFileOutputStream(testBytes.length / 2, initialBufferSize, + testFile); + final int chunkSize = testBytes.length / 3; - try { - fis.close(); - } catch (final IOException e) { - // Ignore an exception on close - } - } catch (final FileNotFoundException e) { - fail("Unexpected FileNotFoundException"); + try { + dfos.write(testBytes, 0, chunkSize); + dfos.write(testBytes, chunkSize, chunkSize); + dfos.write(testBytes, chunkSize * 2, testBytes.length - chunkSize * 2); + dfos.close(); } catch (final IOException e) { fail("Unexpected IOException"); } + assertFalse(dfos.isInMemory()); + assertNull(dfos.getData()); + + verifyResultFile(testFile); + + // Ensure that the test starts from a clean base. + testFile.delete(); } /** - * Tests the case where the amount of data falls below the threshold, and is therefore confined to memory. - * Testing the getInputStream() method. + * Test whether writeTo() properly writes large content. */ @ParameterizedTest(name = "initialBufferSize = {0}") @MethodSource("data") - public void testBelowThresholdGetInputStream(final int initialBufferSize) throws IOException { - final DeferredFileOutputStream dfos = new DeferredFileOutputStream(testBytes.length + 42, initialBufferSize, - null); - dfos.write(testBytes, 0, testBytes.length); - dfos.close(); - assertTrue(dfos.isInMemory()); + public void testWriteToLarge(final int initialBufferSize) { + final File testFile = new File("testWriteToFile.dat"); + final ByteArrayOutputStream baos = new ByteArrayOutputStream(initialBufferSize); + // Ensure that the test starts from a clean base. + testFile.delete(); - try (InputStream is = dfos.toInputStream()) { - assertArrayEquals(testBytes, IOUtils.toByteArray(is)); + final DeferredFileOutputStream dfos = new DeferredFileOutputStream(testBytes.length / 2, testFile); + try { + dfos.write(testBytes); + + assertTrue(testFile.exists()); + assertFalse(dfos.isInMemory()); + + try { + dfos.writeTo(baos); + fail("Should not have been able to write before closing"); + } catch (final IOException ioe) { + // ok, as expected + } + + dfos.close(); + dfos.writeTo(baos); + } catch (final IOException ioe) { + fail("Unexpected IOException"); } + final byte[] copiedBytes = baos.toByteArray(); + assertTrue(Arrays.equals(testBytes, copiedBytes)); + verifyResultFile(testFile); + testFile.delete(); } /** - * Tests the case where the amount of data exceeds the threshold, and is therefore written to disk. The actual data - * written to disk is verified, as is the file itself. - * Testing the getInputStream() method. + * Test whether writeTo() properly writes small content. */ @ParameterizedTest(name = "initialBufferSize = {0}") @MethodSource("data") - public void testAboveThresholdGetInputStream(final int initialBufferSize, final @TempDir Path tempDir) throws IOException { - final File testFile = tempDir.resolve("testAboveThreshold.dat").toFile(); + public void testWriteToSmall(final int initialBufferSize) { + final File testFile = new File("testWriteToMem.dat"); + final ByteArrayOutputStream baos = new ByteArrayOutputStream(initialBufferSize); + // Ensure that the test starts from a clean base. + testFile.delete(); - final DeferredFileOutputStream dfos = new DeferredFileOutputStream(testBytes.length - 5, initialBufferSize, + final DeferredFileOutputStream dfos = new DeferredFileOutputStream(testBytes.length * 2, initialBufferSize, testFile); - dfos.write(testBytes, 0, testBytes.length); - dfos.close(); - assertFalse(dfos.isInMemory()); + try { + dfos.write(testBytes); - try (InputStream is = dfos.toInputStream()) { - assertArrayEquals(testBytes, IOUtils.toByteArray(is)); + assertFalse(testFile.exists()); + assertTrue(dfos.isInMemory()); + + try { + dfos.writeTo(baos); + fail("Should not have been able to write before closing"); + } catch (final IOException ioe) { + // ok, as expected + } + + dfos.close(); + dfos.writeTo(baos); + } catch (final IOException ioe) { + fail("Unexpected IOException"); } + final byte[] copiedBytes = baos.toByteArray(); + assertTrue(Arrays.equals(testBytes, copiedBytes)); - verifyResultFile(testFile); + testFile.delete(); + } + + /** + * Verifies that the specified file contains the same data as the original test data. + * + * @param testFile The file containing the test output. + */ + private void verifyResultFile(final File testFile) { + try { + final FileInputStream fis = new FileInputStream(testFile); + assertEquals(testBytes.length, fis.available()); + + final byte[] resultBytes = new byte[testBytes.length]; + assertEquals(testBytes.length, fis.read(resultBytes)); + + assertTrue(Arrays.equals(resultBytes, testBytes)); + assertEquals(-1, fis.read(resultBytes)); + + try { + fis.close(); + } catch (final IOException e) { + // Ignore an exception on close + } + } catch (final FileNotFoundException e) { + fail("Unexpected FileNotFoundException"); + } catch (final IOException e) { + fail("Unexpected IOException"); + } } }