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 e26387a Prevent NullPointerException in ReversedLinesFileReader constructors #117. e26387a is described below commit e26387a5322eb4f68963573ae4febd76c975def0 Author: Gary Gregory <garydgreg...@gmail.com> AuthorDate: Sat Aug 29 13:57:39 2020 -0400 Prevent NullPointerException in ReversedLinesFileReader constructors #117. --- src/changes/changes.xml | 3 + .../commons/io/input/ReversedLinesFileReader.java | 157 +++++++++++---------- .../org/apache/commons/io/IOUtilsTestCase.java | 1 - .../commons/io/{input => }/TestResources.java | 2 +- .../commons/io/input/CharSequenceReaderTest.java | 1 + .../ReversedLinesFileReaderTestParamBlockSize.java | 1 + .../ReversedLinesFileReaderTestParamFile.java | 74 ++++++---- .../input/ReversedLinesFileReaderTestSimple.java | 1 + .../org/apache/commons/io/input/TailerTest.java | 1 + 9 files changed, 132 insertions(+), 109 deletions(-) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index c15c2f2..1f99178 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -127,6 +127,9 @@ The <action> type attribute can be add,update,fix,remove. <action issue="IO-676" dev="ggregory" type="add" due-to="Isira Seneviratne, Gary Gregory"> Add isFileNewer() and isFileOlder() methods that support the Java 8 Date/Time API. #124. </action> + <action issue="IO-676" dev="ggregory" type="fix" due-to="Michael Ernst, Gary Gregory"> + Prevent NullPointerException in ReversedLinesFileReader constructors #117. + </action> <!-- UPDATES --> <action dev="ggregory" type="add" due-to="Gary Gregory"> Deprecate IOUtils.LINE_SEPARATOR in favor of Java 7's System.lineSeparator(). diff --git a/src/main/java/org/apache/commons/io/input/ReversedLinesFileReader.java b/src/main/java/org/apache/commons/io/input/ReversedLinesFileReader.java index fe3db71..6f3e3d8 100644 --- a/src/main/java/org/apache/commons/io/input/ReversedLinesFileReader.java +++ b/src/main/java/org/apache/commons/io/input/ReversedLinesFileReader.java @@ -54,8 +54,9 @@ public class ReversedLinesFileReader implements Closeable { /** * ctor - * @param no the part number - * @param length its length + * + * @param no the part number + * @param length its length * @param leftOverOfLastFilePart remainder * @throws IOException if there is a problem reading the file */ @@ -100,7 +101,7 @@ public class ReversedLinesFileReader implements Closeable { * Finds the new-line sequence and return its length. * * @param data buffer to scan - * @param i start offset in buffer + * @param i start offset in buffer * @return length of newline sequence or 0 if none found */ private int getNewLineMatchByteCount(final byte[] data, final int i) { @@ -146,7 +147,7 @@ public class ReversedLinesFileReader implements Closeable { final int lineLengthBytes = currentLastBytePos - lineStart + 1; if (lineLengthBytes < 0) { - throw new IllegalStateException("Unexpected negative line length="+lineLengthBytes); + throw new IllegalStateException("Unexpected negative line length=" + lineLengthBytes); } final byte[] lineData = new byte[lineLengthBytes]; System.arraycopy(data, lineStart, lineData, 0, lineLengthBytes); @@ -220,9 +221,8 @@ public class ReversedLinesFileReader implements Closeable { * Creates a ReversedLinesFileReader with default block size of 4KB and the * platform's default encoding. * - * @param file - * the file to be read - * @throws IOException if an I/O error occurs + * @param file the file to be read + * @throws IOException if an I/O error occurs * @deprecated 2.5 use {@link #ReversedLinesFileReader(File, Charset)} instead */ @Deprecated @@ -234,10 +234,9 @@ public class ReversedLinesFileReader implements Closeable { * Creates a ReversedLinesFileReader with default block size of 4KB and the * specified encoding. * - * @param file - * the file to be read - * @param charset the charset to use - * @throws IOException if an I/O error occurs + * @param file the file to be read + * @param charset the charset to use, null uses the default Charset. + * @throws IOException if an I/O error occurs * @since 2.5 */ public ReversedLinesFileReader(final File file, final Charset charset) throws IOException { @@ -247,46 +246,44 @@ public class ReversedLinesFileReader implements Closeable { /** * Creates a ReversedLinesFileReader with the given block size and encoding. * - * @param file - * the file to be read - * @param blockSize - * size of the internal buffer (for ideal performance this should - * match with the block size of the underlying file system). - * @param encoding - * the encoding of the file - * @throws IOException if an I/O error occurs + * @param file the file to be read + * @param blockSize size of the internal buffer (for ideal performance this + * should match with the block size of the underlying file + * system). + * @param charset the encoding of the file, null uses the default Charset. + * @throws IOException if an I/O error occurs * @since 2.3 */ - public ReversedLinesFileReader(final File file, final int blockSize, final Charset encoding) throws IOException { - this(file.toPath(), blockSize, encoding); + public ReversedLinesFileReader(final File file, final int blockSize, final Charset charset) throws IOException { + this(file.toPath(), blockSize, charset); } /** * Creates a ReversedLinesFileReader with the given block size and encoding. * - * @param file - * the file to be read - * @param blockSize - * size of the internal buffer (for ideal performance this should - * match with the block size of the underlying file system). - * @param encoding - * the encoding of the file - * @throws IOException if an I/O error occurs - * @throws java.nio.charset.UnsupportedCharsetException thrown instead of {@link UnsupportedEncodingException} in - * version 2.2 if the encoding is not supported. + * @param file the file to be read + * @param blockSize size of the internal buffer (for ideal performance this + * should match with the block size of the underlying file + * system). + * @param charsetName the encoding of the file, null uses the default Charset. + * @throws IOException if an I/O error occurs + * @throws java.nio.charset.UnsupportedCharsetException thrown instead of + * {@link UnsupportedEncodingException} + * in version 2.2 if the + * encoding is not + * supported. */ - public ReversedLinesFileReader(final File file, final int blockSize, final String encoding) throws IOException { - this(file.toPath(), blockSize, encoding); + public ReversedLinesFileReader(final File file, final int blockSize, final String charsetName) throws IOException { + this(file.toPath(), blockSize, charsetName); } /** * Creates a ReversedLinesFileReader with default block size of 4KB and the * specified encoding. * - * @param file - * the file to be read - * @param charset the charset to use - * @throws IOException if an I/O error occurs + * @param file the file to be read + * @param charset the charset to use, null uses the default Charset. + * @throws IOException if an I/O error occurs * @since 2.7 */ public ReversedLinesFileReader(final Path file, final Charset charset) throws IOException { @@ -296,52 +293,53 @@ public class ReversedLinesFileReader implements Closeable { /** * Creates a ReversedLinesFileReader with the given block size and encoding. * - * @param file - * the file to be read - * @param blockSize - * size of the internal buffer (for ideal performance this should - * match with the block size of the underlying file system). - * @param encoding - * the encoding of the file - * @throws IOException if an I/O error occurs + * @param file the file to be read + * @param blockSize size of the internal buffer (for ideal performance this + * should match with the block size of the underlying file + * system). + * @param charset the encoding of the file, null uses the default Charset. + * @throws IOException if an I/O error occurs * @since 2.7 */ - public ReversedLinesFileReader(final Path file, final int blockSize, final Charset encoding) throws IOException { + public ReversedLinesFileReader(final Path file, final int blockSize, final Charset charset) throws IOException { this.blockSize = blockSize; - this.encoding = encoding; + this.encoding = Charsets.toCharset(charset); // --- check & prepare encoding --- - final Charset charset = Charsets.toCharset(encoding); - final CharsetEncoder charsetEncoder = charset.newEncoder(); + final CharsetEncoder charsetEncoder = this.encoding.newEncoder(); final float maxBytesPerChar = charsetEncoder.maxBytesPerChar(); if (maxBytesPerChar == 1f) { // all one byte encodings are no problem byteDecrement = 1; - } else if (charset == StandardCharsets.UTF_8) { - // UTF-8 works fine out of the box, for multibyte sequences a second UTF-8 byte can never be a newline byte + } else if (this.encoding == StandardCharsets.UTF_8) { + // UTF-8 works fine out of the box, for multibyte sequences a second UTF-8 byte + // can never be a newline byte // http://en.wikipedia.org/wiki/UTF-8 byteDecrement = 1; - } else if (charset == Charset.forName("Shift_JIS") || // Same as for UTF-8 + } else if (this.encoding == Charset.forName("Shift_JIS") || // Same as for UTF-8 // http://www.herongyang.com/Unicode/JIS-Shift-JIS-Encoding.html - charset == Charset.forName("windows-31j") || // Windows code page 932 (Japanese) - charset == Charset.forName("x-windows-949") || // Windows code page 949 (Korean) - charset == Charset.forName("gbk") || // Windows code page 936 (Simplified Chinese) - charset == Charset.forName("x-windows-950")) { // Windows code page 950 (Traditional Chinese) + this.encoding == Charset.forName("windows-31j") || // Windows code page 932 (Japanese) + this.encoding == Charset.forName("x-windows-949") || // Windows code page 949 (Korean) + this.encoding == Charset.forName("gbk") || // Windows code page 936 (Simplified Chinese) + this.encoding == Charset.forName("x-windows-950")) { // Windows code page 950 (Traditional Chinese) byteDecrement = 1; - } else if (charset == StandardCharsets.UTF_16BE || charset == StandardCharsets.UTF_16LE) { - // UTF-16 new line sequences are not allowed as second tuple of four byte sequences, + } else if (this.encoding == StandardCharsets.UTF_16BE || this.encoding == StandardCharsets.UTF_16LE) { + // UTF-16 new line sequences are not allowed as second tuple of four byte + // sequences, // however byte order has to be specified byteDecrement = 2; - } else if (charset == StandardCharsets.UTF_16) { + } else if (this.encoding == StandardCharsets.UTF_16) { throw new UnsupportedEncodingException( - "For UTF-16, you need to specify the byte order (use UTF-16BE or " + "UTF-16LE)"); + "For UTF-16, you need to specify the byte order (use UTF-16BE or " + "UTF-16LE)"); } else { throw new UnsupportedEncodingException( - "Encoding " + encoding + " is not supported yet (feel free to " + "submit a patch)"); + "Encoding " + charset + " is not supported yet (feel free to " + "submit a patch)"); } - // NOTE: The new line sequences are matched in the order given, so it is important that \r\n is BEFORE \n - this.newLineSequences = new byte[][] {"\r\n".getBytes(encoding), "\n".getBytes(encoding), "\r".getBytes(encoding)}; + // NOTE: The new line sequences are matched in the order given, so it is + // important that \r\n is BEFORE \n + this.newLineSequences = new byte[][] { "\r\n".getBytes(this.encoding), "\n".getBytes(this.encoding), + "\r".getBytes(this.encoding) }; this.avoidNewlineSplitBufferSize = newLineSequences[0].length; @@ -364,16 +362,17 @@ public class ReversedLinesFileReader implements Closeable { /** * Creates a ReversedLinesFileReader with the given block size and encoding. * - * @param file - * the file to be read - * @param blockSize - * size of the internal buffer (for ideal performance this should - * match with the block size of the underlying file system). - * @param charsetName - * the encoding of the file - * @throws IOException if an I/O error occurs - * @throws java.nio.charset.UnsupportedCharsetException thrown instead of {@link UnsupportedEncodingException} in - * version 2.2 if the encoding is not supported. + * @param file the file to be read + * @param blockSize size of the internal buffer (for ideal performance this + * should match with the block size of the underlying file + * system). + * @param charsetName the encoding of the file, null uses the default Charset. + * @throws IOException if an I/O error occurs + * @throws java.nio.charset.UnsupportedCharsetException thrown instead of + * {@link UnsupportedEncodingException} + * in version 2.2 if the + * encoding is not + * supported. * @since 2.7 */ public ReversedLinesFileReader(final Path file, final int blockSize, final String charsetName) throws IOException { @@ -383,7 +382,7 @@ public class ReversedLinesFileReader implements Closeable { /** * Closes underlying resources. * - * @throws IOException if an I/O error occurs + * @throws IOException if an I/O error occurs */ @Override public void close() throws IOException { @@ -394,7 +393,7 @@ public class ReversedLinesFileReader implements Closeable { * Returns the lines of the file from bottom to top. * * @return the next line or null if the start of the file is reached - * @throws IOException if an I/O error occurs + * @throws IOException if an I/O error occurs */ public String readLine() throws IOException { @@ -410,7 +409,7 @@ public class ReversedLinesFileReader implements Closeable { } // aligned behavior with BufferedReader that doesn't return a last, empty line - if(EMPTY_STRING.equals(line) && !trailingNewlineOfFileSkipped) { + if (EMPTY_STRING.equals(line) && !trailingNewlineOfFileSkipped) { trailingNewlineOfFileSkipped = true; line = readLine(); } @@ -421,7 +420,8 @@ public class ReversedLinesFileReader implements Closeable { /** * Returns {@code lineCount} lines of the file from bottom to top. * <p> - * If there are less than {@code lineCount} lines in the file, then that's what you get. + * If there are less than {@code lineCount} lines in the file, then that's what + * you get. * </p> * <p> * Note: You can easily flip the result with {@link Collections#reverse(List)}. @@ -450,7 +450,8 @@ public class ReversedLinesFileReader implements Closeable { /** * Returns the last {@code lineCount} lines of the file. * <p> - * If there are less than {@code lineCount} lines in the file, then that's what you get. + * If there are less than {@code lineCount} lines in the file, then that's what + * you get. * </p> * * @param lineCount How many lines to read. diff --git a/src/test/java/org/apache/commons/io/IOUtilsTestCase.java b/src/test/java/org/apache/commons/io/IOUtilsTestCase.java index 38f2d65..635fa41 100644 --- a/src/test/java/org/apache/commons/io/IOUtilsTestCase.java +++ b/src/test/java/org/apache/commons/io/IOUtilsTestCase.java @@ -63,7 +63,6 @@ import java.util.List; import org.apache.commons.io.function.IOConsumer; import org.apache.commons.io.input.NullInputStream; -import org.apache.commons.io.input.TestResources; import org.apache.commons.io.output.AppendableWriter; import org.apache.commons.io.output.NullOutputStream; import org.apache.commons.io.output.StringBuilderWriter; diff --git a/src/test/java/org/apache/commons/io/input/TestResources.java b/src/test/java/org/apache/commons/io/TestResources.java similarity index 98% rename from src/test/java/org/apache/commons/io/input/TestResources.java rename to src/test/java/org/apache/commons/io/TestResources.java index 1b7de8e..44d634a 100644 --- a/src/test/java/org/apache/commons/io/input/TestResources.java +++ b/src/test/java/org/apache/commons/io/TestResources.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.commons.io.input; +package org.apache.commons.io; import java.io.File; import java.io.InputStream; diff --git a/src/test/java/org/apache/commons/io/input/CharSequenceReaderTest.java b/src/test/java/org/apache/commons/io/input/CharSequenceReaderTest.java index 864b2e0..d81f99d 100644 --- a/src/test/java/org/apache/commons/io/input/CharSequenceReaderTest.java +++ b/src/test/java/org/apache/commons/io/input/CharSequenceReaderTest.java @@ -30,6 +30,7 @@ import java.io.Reader; import java.nio.CharBuffer; import java.util.Arrays; +import org.apache.commons.io.TestResources; import org.junit.jupiter.api.Test; /** diff --git a/src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderTestParamBlockSize.java b/src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderTestParamBlockSize.java index 401004d..fcd9dce 100644 --- a/src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderTestParamBlockSize.java +++ b/src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderTestParamBlockSize.java @@ -27,6 +27,7 @@ import java.io.UnsupportedEncodingException; import java.net.URISyntaxException; import java.util.stream.IntStream; +import org.apache.commons.io.TestResources; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; diff --git a/src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderTestParamFile.java b/src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderTestParamFile.java index c131e21..51e0cff 100644 --- a/src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderTestParamFile.java +++ b/src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderTestParamFile.java @@ -20,14 +20,21 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.BufferedReader; import java.io.IOException; +import java.io.Reader; +import java.io.Writer; import java.net.URISyntaxException; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Stack; import java.util.stream.Stream; +import org.apache.commons.io.Charsets; +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.TestResources; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -40,50 +47,59 @@ import com.google.common.jimfs.Jimfs; */ public class ReversedLinesFileReaderTestParamFile { - public static Stream<Arguments> testDataIntegrityWithBufferedReader() { - return Stream.of( - Arguments.of("test-file-20byteslength.bin", "ISO_8859_1", null, false), - Arguments.of("test-file-iso8859-1-shortlines-win-linebr.bin", "ISO_8859_1", null, false), - Arguments.of("test-file-iso8859-1.bin", "ISO_8859_1", null, false), - Arguments.of("test-file-shiftjis.bin", "Shift_JIS", null, false), - Arguments.of("test-file-utf16be.bin", "UTF-16BE", null, false), - Arguments.of("test-file-utf16le.bin", "UTF-16LE", null, false), - Arguments.of("test-file-utf8-cr-only.bin", "UTF-8", null, false), - Arguments.of("test-file-utf8-win-linebr.bin", "UTF-8", null, false), - Arguments.of("test-file-utf8-win-linebr.bin", "UTF-8", 1, false), - Arguments.of("test-file-utf8-win-linebr.bin", "UTF-8", 2, false), - Arguments.of("test-file-utf8-win-linebr.bin", "UTF-8", 3, false), - Arguments.of("test-file-utf8-win-linebr.bin", "UTF-8", 4, false), - Arguments.of("test-file-utf8.bin", "UTF-8", null, false), - Arguments.of("test-file-utf8.bin", "UTF-8", null, true), - Arguments.of("test-file-windows-31j.bin", "windows-31j", null, false), - Arguments.of("test-file-gbk.bin", "gbk", null, false), - Arguments.of("test-file-x-windows-949.bin", "x-windows-949", null, false), - Arguments.of("test-file-x-windows-950.bin", "x-windows-950", null, false) - ); + public static Stream<Arguments> testDataIntegrityWithBufferedReader() throws IOException, URISyntaxException { + // Make a file using the default encoding. + Path sourcePath = TestResources.getPath("test-file-utf8-win-linebr.bin"); + Path targetPath = Files.createTempFile("ReversedLinesFileReaderTestParamFile", ".bin"); + try (Reader input = Files.newBufferedReader(sourcePath, StandardCharsets.UTF_8); + Writer output = Files.newBufferedWriter(targetPath, Charset.defaultCharset())) { + IOUtils.copyLarge(input, output); + } + // All tests + return Stream.of(Arguments.of(targetPath.toAbsolutePath().toString(), null, null, false, false), + Arguments.of("test-file-20byteslength.bin", "ISO_8859_1", null, false, true), + Arguments.of("test-file-iso8859-1-shortlines-win-linebr.bin", "ISO_8859_1", null, false, true), + Arguments.of("test-file-iso8859-1.bin", "ISO_8859_1", null, false, true), + Arguments.of("test-file-shiftjis.bin", "Shift_JIS", null, false, true), + Arguments.of("test-file-utf16be.bin", "UTF-16BE", null, false, true), + Arguments.of("test-file-utf16le.bin", "UTF-16LE", null, false, true), + Arguments.of("test-file-utf8-cr-only.bin", "UTF-8", null, false, true), + Arguments.of("test-file-utf8-win-linebr.bin", "UTF-8", null, false, true, + Arguments.of("test-file-utf8-win-linebr.bin", "UTF-8", 1, false, true), + Arguments.of("test-file-utf8-win-linebr.bin", "UTF-8", 2, false, true), + Arguments.of("test-file-utf8-win-linebr.bin", "UTF-8", 3, false, true), + Arguments.of("test-file-utf8-win-linebr.bin", "UTF-8", 4, false, true), + Arguments.of("test-file-utf8.bin", "UTF-8", null, false, true), + Arguments.of("test-file-utf8.bin", "UTF-8", null, true, true), + Arguments.of("test-file-windows-31j.bin", "windows-31j", null, false, true), + Arguments.of("test-file-gbk.bin", "gbk", null, false, true), + Arguments.of("test-file-x-windows-949.bin", "x-windows-949", null, false, true), + Arguments.of("test-file-x-windows-950.bin", "x-windows-950", null, false, true))); } - @ParameterizedTest(name = "{0}, encoding={1}, blockSize={2}, useNonDefaultFileSystem={3}") + @ParameterizedTest(name = "{0}, encoding={1}, blockSize={2}, useNonDefaultFileSystem={3}, isResource={4}") @MethodSource - public void testDataIntegrityWithBufferedReader(final String fileName, final String encodingName, - final Integer blockSize, final boolean useNonDefaultFileSystem) throws IOException, URISyntaxException { + public void testDataIntegrityWithBufferedReader(final String fileName, final String charsetName, + final Integer blockSize, final boolean useNonDefaultFileSystem, final boolean isResource) + throws IOException, URISyntaxException { - Path filePath = TestResources.getPath(fileName); + Path filePath = isResource ? TestResources.getPath(fileName) : Paths.get(fileName); FileSystem fileSystem = null; if (useNonDefaultFileSystem) { fileSystem = Jimfs.newFileSystem(Configuration.unix()); filePath = Files.copy(filePath, fileSystem.getPath("/" + fileName)); } - final Charset charset = Charset.forName(encodingName); + // We want to test null Charset in the ReversedLinesFileReader ctor. + final Charset charset = charsetName != null ? Charset.forName(charsetName) : null; try (ReversedLinesFileReader reversedLinesFileReader = blockSize == null - ? new ReversedLinesFileReader(filePath, charset) - : new ReversedLinesFileReader(filePath, blockSize, charset)) { + ? new ReversedLinesFileReader(filePath, charset) + : new ReversedLinesFileReader(filePath, blockSize, charset)) { final Stack<String> lineStack = new Stack<>(); String line; - try (BufferedReader bufferedReader = Files.newBufferedReader(filePath, charset)) { + try (BufferedReader bufferedReader = Files.newBufferedReader(filePath, Charsets.toCharset(charset))) { // read all lines in normal order while ((line = bufferedReader.readLine()) != null) { lineStack.push(line); diff --git a/src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderTestSimple.java b/src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderTestSimple.java index 8d889d8..c662b5a 100644 --- a/src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderTestSimple.java +++ b/src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderTestSimple.java @@ -28,6 +28,7 @@ import java.net.URISyntaxException; import java.util.List; import org.apache.commons.io.IOUtils; +import org.apache.commons.io.TestResources; import org.junit.jupiter.api.Test; public class ReversedLinesFileReaderTestSimple { diff --git a/src/test/java/org/apache/commons/io/input/TailerTest.java b/src/test/java/org/apache/commons/io/input/TailerTest.java index e73f1dc..0069799 100644 --- a/src/test/java/org/apache/commons/io/input/TailerTest.java +++ b/src/test/java/org/apache/commons/io/input/TailerTest.java @@ -45,6 +45,7 @@ import java.util.concurrent.ScheduledThreadPoolExecutor; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; +import org.apache.commons.io.TestResources; import org.apache.commons.io.test.TestUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test;