From aa692dbc87c4af1b4ddf03e8a26478a3877c9b3b Mon Sep 17 00:00:00 2001 From: dragonfsky Date: Mon, 29 Jun 2026 04:45:02 +0800 Subject: [PATCH] test: parameterize closed stream operation tests --- .../io/github/dfa1/zstd/ZstdStreamTest.java | 72 +++++++++---------- 1 file changed, 35 insertions(+), 37 deletions(-) diff --git a/zstd/src/test/java/io/github/dfa1/zstd/ZstdStreamTest.java b/zstd/src/test/java/io/github/dfa1/zstd/ZstdStreamTest.java index 4ba9cca..d9700b9 100644 --- a/zstd/src/test/java/io/github/dfa1/zstd/ZstdStreamTest.java +++ b/zstd/src/test/java/io/github/dfa1/zstd/ZstdStreamTest.java @@ -4,6 +4,8 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; import java.io.ByteArrayInputStream; @@ -14,6 +16,7 @@ import java.lang.foreign.MemorySegment; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; +import java.util.stream.Stream; import static io.github.dfa1.zstd.ZstdTestSupport.randomBytes; import static io.github.dfa1.zstd.ZstdTestSupport.sample; @@ -290,35 +293,20 @@ void closeFlushesAndClosesTheUnderlyingStreamExactlyOnce() throws IOException { assertThat(streamDecompress(sink.toByteArray())).isEqualTo(original); } - @Test - void writeAfterCloseThrows() throws IOException { + @ParameterizedTest(name = "{0}") + @MethodSource("io.github.dfa1.zstd.ZstdStreamTest#closedStreamOperations") + void operationAfterCloseThrows(String name, ClosedStreamOperation operation) throws IOException { // Given a closed stream - ZstdOutputStream zout = new ZstdOutputStream(new ByteArrayOutputStream()); - zout.close(); + ThrowingCallable closedStreamOperation = operation.closeThenOperate(); - // When written to - ThrowingCallable result = () -> zout.write(1); + // When operating on it + ThrowingCallable result = closedStreamOperation; // Then it refuses with an IOException rather than touching freed native state assertThatThrownBy(result) .isInstanceOf(IOException.class) .hasMessageContaining("closed"); } - - @Test - void flushAfterCloseThrows() throws IOException { - // Given a closed stream - ZstdOutputStream zout = new ZstdOutputStream(new ByteArrayOutputStream()); - zout.close(); - - // When flushed - ThrowingCallable result = zout::flush; - - // Then it refuses with an IOException - assertThatThrownBy(result) - .isInstanceOf(IOException.class) - .hasMessageContaining("closed"); - } } @Nested @@ -357,22 +345,6 @@ void readPastEndOfStreamStaysMinusOne() throws IOException { } } - @Test - void readAfterCloseThrows() throws IOException { - // Given a closed input stream - byte[] frame = streamCompress("payload".getBytes(StandardCharsets.UTF_8), 3); - ZstdInputStream zin = new ZstdInputStream(new ByteArrayInputStream(frame)); - zin.close(); - - // When read - ThrowingCallable result = zin::read; - - // Then it refuses with an IOException rather than touching freed native state - assertThatThrownBy(result) - .isInstanceOf(IOException.class) - .hasMessageContaining("closed"); - } - @Test void closeClosesTheUnderlyingStreamExactlyOnce() throws IOException { // Given an input stream over a source that tracks close() @@ -422,6 +394,32 @@ void readsCorrectlyWhenInputArrivesOneByteAtATime(int size) throws IOException { } } + private static Stream closedStreamOperations() { + return Stream.of( + Arguments.of("write after close", (ClosedStreamOperation) () -> { + ZstdOutputStream stream = new ZstdOutputStream(new ByteArrayOutputStream()); + stream.close(); + return () -> stream.write(1); + }), + Arguments.of("flush after close", (ClosedStreamOperation) () -> { + ZstdOutputStream stream = new ZstdOutputStream(new ByteArrayOutputStream()); + stream.close(); + return stream::flush; + }), + Arguments.of("read after close", (ClosedStreamOperation) () -> { + byte[] frame = streamCompress("payload".getBytes(StandardCharsets.UTF_8), 3); + ZstdInputStream stream = new ZstdInputStream(new ByteArrayInputStream(frame)); + stream.close(); + return stream::read; + }) + ); + } + + @FunctionalInterface + private interface ClosedStreamOperation { + ThrowingCallable closeThenOperate() throws IOException; + } + /// A sink that records flush/close calls while retaining the bytes written to it /// (a [ByteArrayOutputStream] whose close is a no-op, so bytes stay readable). private static final class CountingOutputStream extends ByteArrayOutputStream {