From 11a33cbb59bdc826d63595f225200d963c409871 Mon Sep 17 00:00:00 2001 From: LossyDragon Date: Mon, 30 Dec 2024 18:01:38 -0600 Subject: [PATCH] Add tests for compat functions, ensuring parity with the methods they replace. --- .../ByteArrayOutputStreamCompatTest.java | 93 +++++++++++ .../util/compat/InputStreamCompatTest.kt | 154 ++++++++++++++++++ 2 files changed, 247 insertions(+) create mode 100644 src/test/java/in/dragonbra/javasteam/util/compat/ByteArrayOutputStreamCompatTest.java create mode 100644 src/test/java/in/dragonbra/javasteam/util/compat/InputStreamCompatTest.kt diff --git a/src/test/java/in/dragonbra/javasteam/util/compat/ByteArrayOutputStreamCompatTest.java b/src/test/java/in/dragonbra/javasteam/util/compat/ByteArrayOutputStreamCompatTest.java new file mode 100644 index 00000000..73e83a7d --- /dev/null +++ b/src/test/java/in/dragonbra/javasteam/util/compat/ByteArrayOutputStreamCompatTest.java @@ -0,0 +1,93 @@ +package in.dragonbra.javasteam.util.compat; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; + +public class ByteArrayOutputStreamCompatTest { + + @Test + public void testEmptyStream() { + var baos = new ByteArrayOutputStream(); + + var compatResult = ByteArrayOutputStreamCompat.toString(baos); + var standardResult = baos.toString(); + + Assertions.assertEquals("", compatResult); + Assertions.assertEquals("", standardResult); + Assertions.assertEquals(standardResult, compatResult); + } + + @Test + public void testAsciiContent() { + var baos = new ByteArrayOutputStream(); + var testString = "Hello, World!"; + + baos.write(testString.getBytes(StandardCharsets.UTF_8), 0, testString.length()); + + var compatResult = ByteArrayOutputStreamCompat.toString(baos); + var standardResult = baos.toString(); + + Assertions.assertEquals(testString, compatResult); + Assertions.assertEquals(testString, standardResult); + Assertions.assertEquals(standardResult, compatResult); + } + + @Test + public void testUnicodeContent() { + var baos = new ByteArrayOutputStream(); + var testString = "Hello, δΈ–η•Œ! πŸ‘‹"; + var bytes = testString.getBytes(StandardCharsets.UTF_8); + + baos.write(bytes, 0, bytes.length); + + var compatResult = ByteArrayOutputStreamCompat.toString(baos); + var standardResult = baos.toString(); + + Assertions.assertEquals(testString, compatResult); + Assertions.assertEquals(testString, standardResult); + Assertions.assertEquals(standardResult, compatResult); + } + + @Test + public void testLargeContent() { + var baos = new ByteArrayOutputStream(); + var largeString = new StringBuilder(); + for (int i = 0; i < 1000; i++) { + largeString.append("Line ").append(i).append("\n"); + } + var testString = largeString.toString(); + var bytes = testString.getBytes(StandardCharsets.UTF_8); + baos.write(bytes, 0, bytes.length); + + var compatResult = ByteArrayOutputStreamCompat.toString(baos); + var standardResult = baos.toString(); + + Assertions.assertEquals(testString, compatResult); + Assertions.assertEquals(testString, standardResult); + Assertions.assertEquals(standardResult, compatResult); + } + + @Test + public void testPartialWrites() { + var baos = new ByteArrayOutputStream(); + var part1 = "Hello"; + var part2 = ", "; + var part3 = "World!"; + + baos.write(part1.getBytes(StandardCharsets.UTF_8), 0, part1.length()); + baos.write(part2.getBytes(StandardCharsets.UTF_8), 0, part2.length()); + baos.write(part3.getBytes(StandardCharsets.UTF_8), 0, part3.length()); + + var expected = part1 + part2 + part3; + var compatResult = ByteArrayOutputStreamCompat.toString(baos); + var standardResult = baos.toString(); + + Assertions.assertEquals(expected, compatResult); + Assertions.assertEquals(expected, standardResult); + Assertions.assertEquals(standardResult, compatResult); + } + +} diff --git a/src/test/java/in/dragonbra/javasteam/util/compat/InputStreamCompatTest.kt b/src/test/java/in/dragonbra/javasteam/util/compat/InputStreamCompatTest.kt new file mode 100644 index 00000000..60852a0d --- /dev/null +++ b/src/test/java/in/dragonbra/javasteam/util/compat/InputStreamCompatTest.kt @@ -0,0 +1,154 @@ +package `in`.dragonbra.javasteam.util.compat + +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource +import java.io.ByteArrayInputStream +import java.io.IOException +import java.io.InputStream +import kotlin.random.Random + +/** + * [readNBytesCompat] methods are kotlin only extension functions for [InputStream]. + * This test is in Kotlin to ensure functionality for the extension functions + */ +class InputStreamCompatTest { + + private lateinit var testData: ByteArray + private lateinit var emptyStream: InputStream + private lateinit var normalStream: InputStream + private lateinit var largeStream: InputStream + + @BeforeEach + fun setup() { + testData = Random.nextBytes(16384) + emptyStream = ByteArrayInputStream(ByteArray(0)) + normalStream = ByteArrayInputStream(testData) + largeStream = ByteArrayInputStream(Random.nextBytes(1024 * 1024)) + } + + @Nested + inner class ReadNBytesCompatArrayTest { + + @Test + fun `empty stream returns zero bytes`() { + val buffer = ByteArray(100) + val bytesRead = emptyStream.readNBytesCompat(buffer, 0, buffer.size) + Assertions.assertEquals(0, bytesRead) + } + + @Test + fun `reading zero bytes returns zero`() { + val buffer = ByteArray(100) + val bytesRead = normalStream.readNBytesCompat(buffer, 0, 0) + Assertions.assertEquals(0, bytesRead) + } + + @ParameterizedTest + @ValueSource(ints = [1, 100, 1000, 8192]) + fun `reading n bytes matches JDK implementation`(n: Int) { + val compatBuffer = ByteArray(n) + val jdkBuffer = ByteArray(n) + + val stream1 = ByteArrayInputStream(testData) + val stream2 = ByteArrayInputStream(testData) + + val compatBytesRead = stream1.readNBytesCompat(compatBuffer, 0, n) + val jdkBytesRead = stream2.readNBytes(jdkBuffer, 0, n) + + Assertions.assertArrayEquals(jdkBuffer, compatBuffer) + Assertions.assertEquals(jdkBytesRead, compatBytesRead) + } + + @Test + fun `throws exception on negative length`() { + val buffer = ByteArray(100) + Assertions.assertThrows(IndexOutOfBoundsException::class.java) { + normalStream.readNBytesCompat(buffer, 0, -1) + } + } + + @Test + fun `throws exception on invalid offset`() { + val buffer = ByteArray(100) + Assertions.assertThrows(IndexOutOfBoundsException::class.java) { + normalStream.readNBytesCompat(buffer, -1, 50) + } + Assertions.assertThrows(IndexOutOfBoundsException::class.java) { + normalStream.readNBytesCompat(buffer, 101, 50) + } + } + } + + @Nested + inner class ReadNBytesCompatLengthTest { + + @Test + fun `empty stream returns empty array`() { + val result = emptyStream.readNBytesCompat(100) + Assertions.assertEquals(0, result.size) + } + + @Test + fun `reading zero bytes returns empty array`() { + val result = normalStream.readNBytesCompat(0) + Assertions.assertEquals(0, result.size) + } + + @ParameterizedTest + @ValueSource(ints = [1, 100, 1000, 8192, 16384]) + fun `reading n bytes matches JDK implementation`(n: Int) { + val stream1 = ByteArrayInputStream(testData) + val stream2 = ByteArrayInputStream(testData) + + val compatResult = stream1.readNBytesCompat(n) + val jdkResult = stream2.readNBytes(n) + + Assertions.assertArrayEquals(jdkResult, compatResult) + } + + @Test + fun `throws exception on negative length`() { + Assertions.assertThrows(IllegalArgumentException::class.java) { + normalStream.readNBytesCompat(-1) + } + } + + @Test + fun `handles large reads correctly`() { + val lengthToRead = 1024 * 1024 + val result = largeStream.readNBytesCompat(lengthToRead) + Assertions.assertEquals(lengthToRead, result.size) + } + + @Test + fun `partial read when EOF reached`() { + val result = normalStream.readNBytesCompat(testData.size * 2) + Assertions.assertEquals(testData.size, result.size) + Assertions.assertArrayEquals(testData, result) + } + } + + @Nested + inner class ErrorConditionsTest { + + @Test + fun `handles IOException from underlying stream`() { + val failingStream = object : InputStream() { + override fun read(): Int = throw IOException("Simulated failure") + override fun read(b: ByteArray, off: Int, len: Int): Int = throw IOException("Simulated failure") + } + + Assertions.assertThrows(IOException::class.java) { + failingStream.readNBytesCompat(100) + } + + Assertions.assertThrows(IOException::class.java) { + failingStream.readNBytesCompat(ByteArray(100), 0, 100) + } + } + } +}