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
commit 6ca84c08bef64a754021613c64fc2b95e606cc3f Author: Gary D. Gregory <garydgreg...@gmail.com> AuthorDate: Sun Mar 2 08:52:54 2025 -0500 Better tests for FileChannels.contentEquals() --- .../commons/io/channels/FileChannelsTest.java | 125 ++++++++++++++++----- 1 file changed, 95 insertions(+), 30 deletions(-) diff --git a/src/test/java/org/apache/commons/io/channels/FileChannelsTest.java b/src/test/java/org/apache/commons/io/channels/FileChannelsTest.java index 7a3b260ab..a8aef2ef3 100644 --- a/src/test/java/org/apache/commons/io/channels/FileChannelsTest.java +++ b/src/test/java/org/apache/commons/io/channels/FileChannelsTest.java @@ -25,88 +25,153 @@ import java.io.FileInputStream; import java.io.IOException; import java.nio.channels.FileChannel; +import java.nio.file.Files; +import java.nio.file.Path; import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; import org.apache.commons.io.file.AbstractTempDirTest; +import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; /** * Tests {@link FileChannels}. */ public class FileChannelsTest extends AbstractTempDirTest { - private static final int BUFFER_SIZE = 1024; - private static final String CONTENT = StringUtils.repeat("x", BUFFER_SIZE); + private static final int SMALL_BUFFER_SIZE = 1024; + private static final String CONTENT = StringUtils.repeat("x", SMALL_BUFFER_SIZE); + + public static int[] getBufferSizes() { + // 1 and 2 are unusual and slow edge cases, but edge cases nonetheless. + return new int[] { 1, 2, IOUtils.DEFAULT_BUFFER_SIZE / 10, IOUtils.DEFAULT_BUFFER_SIZE, IOUtils.DEFAULT_BUFFER_SIZE * 10 }; + } + + private static byte reverse(final byte b) { + return (byte) (~b & 0xff); + } private boolean isEmpty(final File empty) { return empty.length() == 0; } - private void testContentEquals(final String content1, final String content2) throws IOException { - assertTrue(FileChannels.contentEquals(null, null, BUFFER_SIZE)); + private FileChannel reset(final FileChannel fc) throws IOException { + return fc.position(0); + } + + @ParameterizedTest() + @MethodSource("getBufferSizes") + public void testContentEquals(final int bufferSize) throws IOException { + final Path bigFile1 = Files.createTempFile(getClass().getSimpleName(), "-1.bin"); + final Path bigFile2 = Files.createTempFile(getClass().getSimpleName(), "-2.bin"); + final Path bigFile3 = Files.createTempFile(getClass().getSimpleName(), "-3.bin"); + try { + // This length must match any restriction from the Surefire configuration. + final int newLength = 5_000_000; + final byte[] bytes1 = new byte[newLength]; + final byte[] bytes2 = new byte[newLength]; + // Make sure bytes1 and bytes2 are different despite the shuffle + ArrayUtils.shuffle(bytes1); + bytes1[0] = 1; + ArrayUtils.shuffle(bytes2); + bytes2[0] = 2; + Files.write(bigFile1, bytes1); + Files.write(bigFile2, bytes2); + try (FileChannel fc1 = FileChannel.open(bigFile1); FileChannel fc2 = FileChannel.open(bigFile2)) { + assertFalse(FileChannels.contentEquals(fc1, fc2, bufferSize)); + assertFalse(FileChannels.contentEquals(reset(fc2), reset(fc1), bufferSize)); + assertTrue(FileChannels.contentEquals(reset(fc1), reset(fc1), bufferSize)); + } + // Make the LAST byte different. + byte[] bytes3 = bytes1.clone(); + final int last = bytes3.length - 1; + bytes3[last] = reverse(bytes3[last]); + Files.write(bigFile3, bytes3); + try (FileChannel fc1 = FileChannel.open(bigFile1); FileChannel fc3 = FileChannel.open(bigFile3)) { + assertFalse(FileChannels.contentEquals(fc1, fc3, bufferSize)); + assertFalse(FileChannels.contentEquals(reset(fc3), reset(fc1), bufferSize)); + } + // Make a byte in the middle different + bytes3 = bytes1.clone(); + final int middle = bytes3.length / 2; + bytes3[middle] = reverse(bytes3[middle]); + Files.write(bigFile3, bytes3); + try (FileChannel fc1 = FileChannel.open(bigFile1); FileChannel fc3 = FileChannel.open(bigFile3)) { + assertFalse(FileChannels.contentEquals(fc1, fc3, bufferSize)); + assertFalse(FileChannels.contentEquals(reset(fc3), reset(fc1), bufferSize)); + } + } finally { + // Delete ASAP + Files.deleteIfExists(bigFile1); + Files.deleteIfExists(bigFile2); + Files.deleteIfExists(bigFile3); + } + } + + private void testContentEquals(final String content1, final String content2, final int bufferSize) throws IOException { + assertTrue(FileChannels.contentEquals(null, null, bufferSize)); // Prepare test files with same size but different content // (first 3 bytes are different, followed by a large amount of equal content) final File file1 = new File(tempDirFile, "test1.txt"); final File file2 = new File(tempDirFile, "test2.txt"); FileUtils.writeStringToFile(file1, content1, US_ASCII); FileUtils.writeStringToFile(file2, content2, US_ASCII); - // File checksums are different assertNotEquals(FileUtils.checksumCRC32(file1), FileUtils.checksumCRC32(file2)); - try (FileInputStream stream1 = new FileInputStream(file1); FileInputStream stream2 = new FileInputStream(file2); FileChannel channel1 = stream1.getChannel(); FileChannel channel2 = stream2.getChannel()) { - assertFalse(FileChannels.contentEquals(channel1, channel2, BUFFER_SIZE)); + assertFalse(FileChannels.contentEquals(channel1, channel2, bufferSize)); } try (FileInputStream stream1 = new FileInputStream(file1); FileInputStream stream2 = new FileInputStream(file2); FileChannel channel1 = stream1.getChannel(); FileChannel channel2 = stream2.getChannel()) { - assertTrue(FileChannels.contentEquals(channel1, channel1, BUFFER_SIZE)); - assertTrue(FileChannels.contentEquals(channel2, channel2, BUFFER_SIZE)); + assertTrue(FileChannels.contentEquals(channel1, channel1, bufferSize)); + assertTrue(FileChannels.contentEquals(channel2, channel2, bufferSize)); } } - @Test - public void testContentEqualsDifferentPostfix() throws IOException { - testContentEquals(CONTENT + "ABC", CONTENT + "XYZ"); + @ParameterizedTest() + @MethodSource("getBufferSizes") + public void testContentEqualsDifferentPostfix(final int bufferSize) throws IOException { + testContentEquals(CONTENT + "ABC", CONTENT + "XYZ", bufferSize); } - @Test - public void testContentEqualsDifferentPrefix() throws IOException { - testContentEquals("ABC" + CONTENT, "XYZ" + CONTENT); + @ParameterizedTest() + @MethodSource("getBufferSizes") + public void testContentEqualsDifferentPrefix(final int bufferSize) throws IOException { + testContentEquals("ABC" + CONTENT, "XYZ" + CONTENT, bufferSize); } - @Test - public void testContentEqualsEmpty() throws IOException { - assertTrue(FileChannels.contentEquals(null, null, BUFFER_SIZE)); - + @ParameterizedTest() + @MethodSource("getBufferSizes") + public void testContentEqualsEmpty(final int bufferSize) throws IOException { + assertTrue(FileChannels.contentEquals(null, null, bufferSize)); + // setup fixtures final File empty = new File(tempDirFile, "empty.txt"); final File notEmpty = new File(tempDirFile, "not-empty.txt"); FileUtils.writeStringToFile(empty, StringUtils.EMPTY, US_ASCII); FileUtils.writeStringToFile(notEmpty, "X", US_ASCII); assertTrue(isEmpty(empty)); assertFalse(isEmpty(notEmpty)); - // File checksums are different assertNotEquals(FileUtils.checksumCRC32(empty), FileUtils.checksumCRC32(notEmpty)); - try (FileInputStream streamEmpty = new FileInputStream(empty); FileInputStream streamNotEmpty = new FileInputStream(notEmpty); FileChannel channelEmpty = streamEmpty.getChannel(); FileChannel channelNotEmpty = streamNotEmpty.getChannel()) { - assertFalse(FileChannels.contentEquals(channelEmpty, channelNotEmpty, BUFFER_SIZE)); - assertFalse(FileChannels.contentEquals(null, channelNotEmpty, BUFFER_SIZE)); - assertFalse(FileChannels.contentEquals(channelNotEmpty, null, BUFFER_SIZE)); - assertTrue(FileChannels.contentEquals(channelEmpty, channelEmpty, BUFFER_SIZE)); - assertTrue(FileChannels.contentEquals(null, channelEmpty, BUFFER_SIZE)); - assertTrue(FileChannels.contentEquals(channelEmpty, null, BUFFER_SIZE)); - assertTrue(FileChannels.contentEquals(channelNotEmpty, channelNotEmpty, BUFFER_SIZE)); + assertFalse(FileChannels.contentEquals(channelEmpty, channelNotEmpty, bufferSize)); + assertFalse(FileChannels.contentEquals(null, channelNotEmpty, bufferSize)); + assertFalse(FileChannels.contentEquals(channelNotEmpty, null, bufferSize)); + assertTrue(FileChannels.contentEquals(channelEmpty, channelEmpty, bufferSize)); + assertTrue(FileChannels.contentEquals(null, channelEmpty, bufferSize)); + assertTrue(FileChannels.contentEquals(channelEmpty, null, bufferSize)); + assertTrue(FileChannels.contentEquals(channelNotEmpty, channelNotEmpty, bufferSize)); } } - }