diff --git a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/ByteBufferUtils.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/ByteBufferUtils.java index 29276dc7b60..0f9d6a373d4 100644 --- a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/ByteBufferUtils.java +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/ByteBufferUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -61,7 +61,7 @@ public final class ByteBufferUtils { private static byte[] bytes(ByteBuffer buffer) { byte[] bytes = new byte[buffer.limit()]; - buffer.get(bytes); + buffer.get(buffer.position(), bytes); return bytes; } diff --git a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/FromPublisherTest.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/FromPublisherTest.java index f05eab0a2e5..7d3e78e0b29 100644 --- a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/FromPublisherTest.java +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/FromPublisherTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,10 +25,13 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; +import java.nio.ByteBuffer; +import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; +import static java.net.http.HttpRequest.BodyPublishers.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -37,16 +40,18 @@ import static org.junit.jupiter.api.Assertions.assertTrue; * @test * @bug 8364733 * @summary Verify all specified `HttpRequest.BodyPublishers::fromPublisher` behavior - * @build RecordingSubscriber - * @run junit FromPublisherTest + * @build ByteBufferUtils + * RecordingSubscriber + * ReplayTestSupport + * @run junit ${test.main.class} */ -class FromPublisherTest { +class FromPublisherTest extends ReplayTestSupport { @Test void testNullPublisher() { - assertThrows(NullPointerException.class, () -> HttpRequest.BodyPublishers.fromPublisher(null)); - assertThrows(NullPointerException.class, () -> HttpRequest.BodyPublishers.fromPublisher(null, 1)); + assertThrows(NullPointerException.class, () -> fromPublisher(null)); + assertThrows(NullPointerException.class, () -> fromPublisher(null, 1)); } @ParameterizedTest @@ -54,7 +59,7 @@ class FromPublisherTest { void testInvalidContentLength(long contentLength) { IllegalArgumentException exception = assertThrows( IllegalArgumentException.class, - () -> HttpRequest.BodyPublishers.fromPublisher(null, contentLength)); + () -> fromPublisher(null, contentLength)); String exceptionMessage = exception.getMessage(); assertTrue( exceptionMessage.contains("non-positive contentLength"), @@ -64,29 +69,26 @@ class FromPublisherTest { @ParameterizedTest @ValueSource(longs = {1, 2, 3, 4}) void testValidContentLength(long contentLength) { - HttpRequest.BodyPublisher publisher = - HttpRequest.BodyPublishers.fromPublisher(HttpRequest.BodyPublishers.noBody(), contentLength); + BodyPublisher publisher = fromPublisher(noBody(), contentLength); assertEquals(contentLength, publisher.contentLength()); } @Test void testNoContentLength() { - HttpRequest.BodyPublisher publisher = - HttpRequest.BodyPublishers.fromPublisher(HttpRequest.BodyPublishers.noBody()); + BodyPublisher publisher = fromPublisher(noBody()); assertEquals(-1, publisher.contentLength()); } @Test void testNullSubscriber() { - HttpRequest.BodyPublisher publisher = - HttpRequest.BodyPublishers.fromPublisher(HttpRequest.BodyPublishers.noBody()); + BodyPublisher publisher = fromPublisher(noBody()); assertThrows(NullPointerException.class, () -> publisher.subscribe(null)); } @Test void testDelegation() throws InterruptedException { BlockingQueue publisherInvocations = new LinkedBlockingQueue<>(); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.fromPublisher(subscriber -> { + BodyPublisher publisher = fromPublisher(subscriber -> { publisherInvocations.add("subscribe"); publisherInvocations.add(subscriber); }); @@ -97,4 +99,13 @@ class FromPublisherTest { assertTrue(subscriber.invocations.isEmpty()); } + @Override + Iterable createReplayTargets() { + byte[] content = ByteBufferUtils.byteArrayOfLength(10); + ByteBuffer expectedBuffer = ByteBuffer.wrap(content); + BodyPublisher delegatePublisher = ofByteArray(content); + BodyPublisher publisher = fromPublisher(delegatePublisher, delegatePublisher.contentLength()); + return List.of(new ReplayTarget(expectedBuffer, publisher)); + } + } diff --git a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/NoBodyTest.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/NoBodyTest.java index f58e9505c9a..d45482700d6 100644 --- a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/NoBodyTest.java +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/NoBodyTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,10 @@ import org.junit.jupiter.api.Test; -import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; +import java.net.http.HttpRequest.BodyPublishers; +import java.nio.ByteBuffer; +import java.util.List; import java.util.concurrent.Flow; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -32,17 +35,19 @@ import static org.junit.jupiter.api.Assertions.assertEquals; * @test * @bug 8364733 * @summary Verify all specified `HttpRequest.BodyPublishers::noBody` behavior - * @build RecordingSubscriber - * @run junit NoBodyTest + * @build ByteBufferUtils + * RecordingSubscriber + * ReplayTestSupport + * @run junit ${test.main.class} */ -class NoBodyTest { +class NoBodyTest extends ReplayTestSupport { @Test void test() throws InterruptedException { // Create the publisher - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.noBody(); + BodyPublisher publisher = BodyPublishers.noBody(); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -54,4 +59,11 @@ class NoBodyTest { } + @Override + Iterable createReplayTargets() { + ByteBuffer expectedBuffer = ByteBuffer.wrap(new byte[0]); + BodyPublisher publisher = BodyPublishers.noBody(); + return List.of(new ReplayTarget(expectedBuffer, publisher)); + } + } diff --git a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArrayTest.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArrayTest.java index 19f7369125d..25233c9dd99 100644 --- a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArrayTest.java +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArrayTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,7 +25,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; -import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; +import java.net.http.HttpRequest.BodyPublishers; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; @@ -39,30 +40,34 @@ import static org.junit.jupiter.api.Assertions.assertThrows; * @test * @bug 8364733 * @summary Verify all specified `HttpRequest.BodyPublishers::ofByteArray` behavior - * @build RecordingSubscriber - * @run junit OfByteArrayTest + * + * @build ByteBufferUtils + * RecordingSubscriber + * ReplayTestSupport + * + * @run junit ${test.main.class} * * @comment Using `main/othervm` to initiate tests that depend on a custom-configured JVM - * @run main/othervm -Djdk.httpclient.bufsize=3 OfByteArrayTest testChunking "" 0 0 "" - * @run main/othervm -Djdk.httpclient.bufsize=3 OfByteArrayTest testChunking a 0 0 "" - * @run main/othervm -Djdk.httpclient.bufsize=3 OfByteArrayTest testChunking a 1 0 "" - * @run main/othervm -Djdk.httpclient.bufsize=3 OfByteArrayTest testChunking a 0 1 a - * @run main/othervm -Djdk.httpclient.bufsize=3 OfByteArrayTest testChunking ab 0 1 a - * @run main/othervm -Djdk.httpclient.bufsize=3 OfByteArrayTest testChunking ab 1 1 b - * @run main/othervm -Djdk.httpclient.bufsize=3 OfByteArrayTest testChunking ab 0 2 ab - * @run main/othervm -Djdk.httpclient.bufsize=1 OfByteArrayTest testChunking abc 0 3 a:b:c - * @run main/othervm -Djdk.httpclient.bufsize=2 OfByteArrayTest testChunking abc 0 3 ab:c - * @run main/othervm -Djdk.httpclient.bufsize=2 OfByteArrayTest testChunking abcdef 2 4 cd:ef + * @run main/othervm -Djdk.httpclient.bufsize=3 ${test.main.class} testChunking "" 0 0 "" + * @run main/othervm -Djdk.httpclient.bufsize=3 ${test.main.class} testChunking a 0 0 "" + * @run main/othervm -Djdk.httpclient.bufsize=3 ${test.main.class} testChunking a 1 0 "" + * @run main/othervm -Djdk.httpclient.bufsize=3 ${test.main.class} testChunking a 0 1 a + * @run main/othervm -Djdk.httpclient.bufsize=3 ${test.main.class} testChunking ab 0 1 a + * @run main/othervm -Djdk.httpclient.bufsize=3 ${test.main.class} testChunking ab 1 1 b + * @run main/othervm -Djdk.httpclient.bufsize=3 ${test.main.class} testChunking ab 0 2 ab + * @run main/othervm -Djdk.httpclient.bufsize=1 ${test.main.class} testChunking abc 0 3 a:b:c + * @run main/othervm -Djdk.httpclient.bufsize=2 ${test.main.class} testChunking abc 0 3 ab:c + * @run main/othervm -Djdk.httpclient.bufsize=2 ${test.main.class} testChunking abcdef 2 4 cd:ef */ -public class OfByteArrayTest { +public class OfByteArrayTest extends ReplayTestSupport { private static final Charset CHARSET = StandardCharsets.US_ASCII; @Test void testNullContent() { - assertThrows(NullPointerException.class, () -> HttpRequest.BodyPublishers.ofByteArray(null)); - assertThrows(NullPointerException.class, () -> HttpRequest.BodyPublishers.ofByteArray(null, 1, 2)); + assertThrows(NullPointerException.class, () -> BodyPublishers.ofByteArray(null)); + assertThrows(NullPointerException.class, () -> BodyPublishers.ofByteArray(null, 1, 2)); } @ParameterizedTest @@ -78,7 +83,15 @@ public class OfByteArrayTest { byte[] content = contentText.getBytes(CHARSET); assertThrows( IndexOutOfBoundsException.class, - () -> HttpRequest.BodyPublishers.ofByteArray(content, offset, length)); + () -> BodyPublishers.ofByteArray(content, offset, length)); + } + + @Override + Iterable createReplayTargets() { + byte[] content = "this content needs to be replayed again and again".getBytes(CHARSET); + ByteBuffer expectedBuffer = ByteBuffer.wrap(content); + BodyPublisher publisher = BodyPublishers.ofByteArray(content); + return List.of(new ReplayTarget(expectedBuffer, publisher)); } /** @@ -105,7 +118,7 @@ public class OfByteArrayTest { // Create the publisher byte[] content = contentText.getBytes(CHARSET); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofByteArray(content, offset, length); + BodyPublisher publisher = BodyPublishers.ofByteArray(content, offset, length); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); diff --git a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArraysTest.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArraysTest.java index ab051d2020f..10784f7dbee 100644 --- a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArraysTest.java +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfByteArraysTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,8 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; -import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; +import java.net.http.HttpRequest.BodyPublishers; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Iterator; @@ -46,15 +47,18 @@ import static org.junit.jupiter.api.Assertions.assertThrows; * @test * @bug 8226303 8364733 * @summary Verify all specified `HttpRequest.BodyPublishers::ofByteArrays` behavior + * * @build ByteBufferUtils * RecordingSubscriber + * ReplayTestSupport + * * @run junit OfByteArraysTest * * @comment Using `main/othervm` to initiate tests that depend on a custom-configured JVM * @run main/othervm -Xmx64m OfByteArraysTest testOOM */ -public class OfByteArraysTest { +public class OfByteArraysTest extends ReplayTestSupport { @ParameterizedTest @ValueSource(ints = {0, 1, 2, 3}) @@ -65,7 +69,7 @@ public class OfByteArraysTest { .range(0, length) .mapToObj(i -> new byte[]{(byte) i}) .toList(); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofByteArrays(buffers::iterator); + BodyPublisher publisher = BodyPublishers.ofByteArrays(buffers::iterator); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -95,7 +99,7 @@ public class OfByteArraysTest { case 2 -> List.of(buffer2).iterator(); default -> throw new AssertionError(); }; - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofByteArrays(iterable); + BodyPublisher publisher = BodyPublishers.ofByteArrays(iterable); // Subscribe twice (to force two `Iterable::iterator` invocations) RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -112,14 +116,14 @@ public class OfByteArraysTest { @Test void testNullIterable() { - assertThrows(NullPointerException.class, () -> HttpRequest.BodyPublishers.ofByteArrays(null)); + assertThrows(NullPointerException.class, () -> BodyPublishers.ofByteArrays(null)); } @Test void testNullIterator() throws InterruptedException { // Create the publisher - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofByteArrays(() -> null); + BodyPublisher publisher = BodyPublishers.ofByteArrays(() -> null); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -138,7 +142,7 @@ public class OfByteArraysTest { // Create the publisher List iterable = new ArrayList<>(); iterable.add(null); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofByteArrays(iterable); + BodyPublisher publisher = BodyPublishers.ofByteArrays(iterable); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -156,7 +160,7 @@ public class OfByteArraysTest { // Create the publisher RuntimeException exception = new RuntimeException("failure for `testIteratorCreationException`"); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofByteArrays(() -> { + BodyPublisher publisher = BodyPublishers.ofByteArrays(() -> { throw exception; }); @@ -192,7 +196,7 @@ public class OfByteArraysTest { // Create the publisher IteratorThrowingAtEnd iterator = new IteratorThrowingAtEnd(exceptionIndex, hasNextException, nextException); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofByteArrays(() -> iterator); + BodyPublisher publisher = BodyPublishers.ofByteArrays(() -> iterator); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -257,6 +261,14 @@ public class OfByteArraysTest { } + @Override + Iterable createReplayTargets() { + byte[] byteArray = ByteBufferUtils.byteArrayOfLength(9); + ByteBuffer expectedBuffer = ByteBuffer.wrap(byteArray); + BodyPublisher publisher = BodyPublishers.ofByteArrays(List.of(byteArray)); + return List.of(new ReplayTarget(expectedBuffer, -1, publisher, null)); + } + /** * Initiates tests that depend on a custom-configured JVM. */ @@ -273,7 +285,7 @@ public class OfByteArraysTest { // Create the publisher int length = ByteBufferUtils.findLengthExceedingMaxMemory(); Iterable iterable = createIterableOfLength(length); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofByteArrays(iterable); + BodyPublisher publisher = BodyPublishers.ofByteArrays(iterable); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); diff --git a/test/jdk/java/net/httpclient/FileChannelPublisherTest.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfFileChannelTest.java similarity index 96% rename from test/jdk/java/net/httpclient/FileChannelPublisherTest.java rename to test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfFileChannelTest.java index c9c78791da3..47596f40a5f 100644 --- a/test/jdk/java/net/httpclient/FileChannelPublisherTest.java +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfFileChannelTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,9 +26,12 @@ * @summary Verifies `HttpRequest.BodyPublishers::ofFileChannel` * @library /test/lib * /test/jdk/java/net/httpclient/lib - * @build jdk.httpclient.test.lib.common.HttpServerAdapters + * @build ByteBufferUtils + * RecordingSubscriber + * ReplayTestSupport + * jdk.httpclient.test.lib.common.HttpServerAdapters * jdk.test.lib.net.SimpleSSLContext - * @run junit FileChannelPublisherTest + * @run junit ${test.main.class} */ import jdk.httpclient.test.lib.common.HttpServerAdapters.HttpTestHandler; @@ -52,8 +55,10 @@ import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpClient.Version; import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; import java.net.http.HttpRequest.BodyPublishers; import java.net.http.HttpResponse; +import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; import java.nio.channels.FileChannel; import java.nio.file.Files; @@ -82,9 +87,9 @@ import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -class FileChannelPublisherTest { +class OfFileChannelTest extends ReplayTestSupport { - private static final String CLASS_NAME = FileChannelPublisherTest.class.getSimpleName(); + private static final String CLASS_NAME = OfFileChannelTest.class.getSimpleName(); private static final Logger LOGGER = Utils.getDebugLogger(CLASS_NAME::toString, Utils.DEBUG); @@ -650,6 +655,22 @@ class FileChannelPublisherTest { } + @Override + Iterable createReplayTargets() { + int fileLength = 42; + byte[] fileBytes = generateFileBytes(fileLength); + Path filePath = Path.of("replayTarget.txt"); + try { + Files.write(filePath, fileBytes, StandardOpenOption.CREATE); + FileChannel fileChannel = FileChannel.open(filePath); + BodyPublisher publisher = BodyPublishers.ofFileChannel(fileChannel, 0, fileLength); + ByteBuffer expectedBuffer = ByteBuffer.wrap(fileBytes); + return List.of(new ReplayTarget(expectedBuffer, fileLength, publisher, fileChannel)); + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + } + /** * Performs the initial {@code HEAD} request to the specified server. This * effectively admits a connection to the client's pool, where all protocol diff --git a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfFileTest.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfFileTest.java index 94b5a596fc6..7287468d600 100644 --- a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfFileTest.java +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfFileTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,7 +31,8 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.OutputStream; import java.io.UncheckedIOException; -import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; +import java.net.http.HttpRequest.BodyPublishers; import java.nio.ByteBuffer; import java.nio.file.FileSystem; import java.nio.file.FileSystems; @@ -51,15 +52,18 @@ import static org.junit.jupiter.api.Assertions.assertTrue; * @test * @bug 8226303 8235459 8358688 8364733 * @summary Verify all specified `HttpRequest.BodyPublishers::ofFile` behavior + * * @build ByteBufferUtils * RecordingSubscriber - * @run junit OfFileTest + * ReplayTestSupport + * + * @run junit ${test.main.class} * * @comment Using `main/othervm` to initiate tests that depend on a custom-configured JVM - * @run main/othervm -Xmx64m OfFileTest testOOM + * @run main/othervm -Xmx64m ${test.main.class} testOOM */ -public class OfFileTest { +public class OfFileTest extends ReplayTestSupport { private static final Path DEFAULT_FS_DIR = Path.of(System.getProperty("user.dir", ".")); @@ -89,14 +93,14 @@ public class OfFileTest { @Test void testNullPath() { - assertThrows(NullPointerException.class, () -> HttpRequest.BodyPublishers.ofFile(null)); + assertThrows(NullPointerException.class, () -> BodyPublishers.ofFile(null)); } @ParameterizedTest @MethodSource("parentDirs") void testNonExistentPath(Path parentDir) { Path nonExistentPath = createFilePath(parentDir, "testNonExistentPath"); - assertThrows(FileNotFoundException.class, () -> HttpRequest.BodyPublishers.ofFile(nonExistentPath)); + assertThrows(FileNotFoundException.class, () -> BodyPublishers.ofFile(nonExistentPath)); } @ParameterizedTest @@ -106,7 +110,7 @@ public class OfFileTest { // Create the publisher byte[] fileBytes = ByteBufferUtils.byteArrayOfLength(3); Path filePath = createFile(parentDir, "testNonExistentPathAtSubscribe", fileBytes); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofFile(filePath); + BodyPublisher publisher = BodyPublishers.ofFile(filePath); // Delete the file Files.delete(filePath); @@ -131,7 +135,7 @@ public class OfFileTest { void testIrregularFile(Path parentDir) throws Exception { // Create the publisher - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofFile(parentDir); + BodyPublisher publisher = BodyPublishers.ofFile(parentDir); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -167,7 +171,7 @@ public class OfFileTest { // Create the publisher byte[] fileBytes = ByteBufferUtils.byteArrayOfLength(BIG_FILE_LENGTH); Path filePath = createFile(parentDir, "testFileModificationWhileReading", fileBytes); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofFile(filePath); + BodyPublisher publisher = BodyPublishers.ofFile(filePath); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -210,7 +214,7 @@ public class OfFileTest { // Create the publisher byte[] fileBytes = ByteBufferUtils.byteArrayOfLength(fileLength); Path filePath = createFile(parentDir, "testFileOfLength", fileBytes); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofFile(filePath); + BodyPublisher publisher = BodyPublishers.ofFile(filePath); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -222,6 +226,24 @@ public class OfFileTest { } + @Override + Iterable createReplayTargets() { + byte[] fileBytes = ByteBufferUtils.byteArrayOfLength(3); + ByteBuffer expectedBuffer = ByteBuffer.wrap(fileBytes); + return parentDirs() + .stream() + .map(parentDir -> { + try { + Path filePath = createFile(parentDir, "replayTest", fileBytes); + BodyPublisher publisher = BodyPublishers.ofFile(filePath); + return new ReplayTarget(expectedBuffer, publisher); + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + }) + .toList(); + } + /** * Initiates tests that depend on a custom-configured JVM. */ @@ -248,7 +270,7 @@ public class OfFileTest { // Create the publisher int fileLength = ByteBufferUtils.findLengthExceedingMaxMemory(); Path filePath = createFileOfLength(parentDir, "testOOM", fileLength); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofFile(filePath); + BodyPublisher publisher = BodyPublishers.ofFile(filePath); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); diff --git a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfInputStreamTest.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfInputStreamTest.java index 7688c1674ee..06ed13333ae 100644 --- a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfInputStreamTest.java +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfInputStreamTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -28,7 +28,9 @@ import org.junit.jupiter.params.provider.ValueSource; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; +import java.net.http.HttpRequest.BodyPublishers; +import java.nio.ByteBuffer; import java.util.List; import java.util.concurrent.Flow; import java.util.function.Supplier; @@ -41,19 +43,22 @@ import static org.junit.jupiter.api.Assertions.assertThrows; * @test * @bug 8364733 * @summary Verify all specified `HttpRequest.BodyPublishers::ofInputStream` behavior + * * @build ByteBufferUtils * RecordingSubscriber - * @run junit OfInputStreamTest + * ReplayTestSupport + * + * @run junit ${test.main.class} * * @comment Using `main/othervm` to initiate tests that depend on a custom-configured JVM - * @run main/othervm -Xmx64m OfInputStreamTest testOOM + * @run main/othervm -Xmx64m ${test.main.class} testOOM */ -public class OfInputStreamTest { +public class OfInputStreamTest extends ReplayTestSupport { @Test void testNullInputStreamSupplier() { - assertThrows(NullPointerException.class, () -> HttpRequest.BodyPublishers.ofInputStream(null)); + assertThrows(NullPointerException.class, () -> BodyPublishers.ofInputStream(null)); } @Test @@ -61,7 +66,7 @@ public class OfInputStreamTest { // Create the publisher RuntimeException exception = new RuntimeException(); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofInputStream(() -> { throw exception; }); + BodyPublisher publisher = BodyPublishers.ofInputStream(() -> { throw exception; }); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -80,7 +85,7 @@ public class OfInputStreamTest { void testNullInputStream() throws InterruptedException { // Create the publisher - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofInputStream(() -> null); + BodyPublisher publisher = BodyPublishers.ofInputStream(() -> null); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -107,7 +112,7 @@ public class OfInputStreamTest { case 2 -> new ByteArrayInputStream(buffer2); default -> throw new AssertionError(); }; - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofInputStream(inputStreamSupplier); + BodyPublisher publisher = BodyPublishers.ofInputStream(inputStreamSupplier); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -129,7 +134,7 @@ public class OfInputStreamTest { // Create the publisher byte[] content = ByteBufferUtils.byteArrayOfLength(length); InputStream inputStream = new ByteArrayInputStream(content); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofInputStream(() -> inputStream); + BodyPublisher publisher = BodyPublishers.ofInputStream(() -> inputStream); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -148,7 +153,7 @@ public class OfInputStreamTest { // Create the publisher RuntimeException exception = new RuntimeException("failure for `read`"); InputStream inputStream = new InputStreamThrowingOnCompletion(exceptionIndex, exception); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofInputStream(() -> inputStream); + BodyPublisher publisher = BodyPublishers.ofInputStream(() -> inputStream); // Subscribe RecordingSubscriber subscriber = new RecordingSubscriber(); @@ -185,6 +190,14 @@ public class OfInputStreamTest { } + @Override + Iterable createReplayTargets() { + byte[] content = ByteBufferUtils.byteArrayOfLength(10); + ByteBuffer expectedBuffer = ByteBuffer.wrap(content); + BodyPublisher publisher = BodyPublishers.ofInputStream(() -> new ByteArrayInputStream(content)); + return List.of(new ReplayTarget(expectedBuffer, -1, publisher, null)); + } + /** * Initiates tests that depend on a custom-configured JVM. */ @@ -200,8 +213,8 @@ public class OfInputStreamTest { // Create the publisher using an `InputStream` that emits content exceeding the maximum memory int length = ByteBufferUtils.findLengthExceedingMaxMemory(); - HttpRequest.BodyPublisher publisher = - HttpRequest.BodyPublishers.ofInputStream(() -> new InputStream() { + BodyPublisher publisher = + BodyPublishers.ofInputStream(() -> new InputStream() { private int position; diff --git a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfStringTest.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfStringTest.java index 143b5ce17da..2e1fcb11f99 100644 --- a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfStringTest.java +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/OfStringTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,10 +26,12 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.ValueSource; -import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublisher; +import java.net.http.HttpRequest.BodyPublishers; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.util.List; import java.util.concurrent.Flow; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -41,10 +43,11 @@ import static org.junit.jupiter.api.Assertions.assertThrows; * @summary Verify all specified `HttpRequest.BodyPublishers::ofString` behavior * @build ByteBufferUtils * RecordingSubscriber - * @run junit OfStringTest + * ReplayTestSupport + * @run junit ${test.main.class} */ -class OfStringTest { +class OfStringTest extends ReplayTestSupport { private static final Charset CHARSET = StandardCharsets.US_ASCII; @@ -58,7 +61,7 @@ class OfStringTest { contentChars[i] = (char) ('a' + i); } String content = new String(contentChars); - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofString(content, CHARSET); + BodyPublisher publisher = BodyPublishers.ofString(content, CHARSET); // Subscribe assertEquals(length, publisher.contentLength()); @@ -87,7 +90,7 @@ class OfStringTest { void testCharset(String content, Charset charset) throws InterruptedException { // Create the publisher - HttpRequest.BodyPublisher publisher = HttpRequest.BodyPublishers.ofString(content, charset); + BodyPublisher publisher = BodyPublishers.ofString(content, charset); // Subscribe ByteBuffer expectedBuffer = charset.encode(content); @@ -108,12 +111,20 @@ class OfStringTest { @Test void testNullContent() { - assertThrows(NullPointerException.class, () -> HttpRequest.BodyPublishers.ofString(null, CHARSET)); + assertThrows(NullPointerException.class, () -> BodyPublishers.ofString(null, CHARSET)); } @Test void testNullCharset() { - assertThrows(NullPointerException.class, () -> HttpRequest.BodyPublishers.ofString("foo", null)); + assertThrows(NullPointerException.class, () -> BodyPublishers.ofString("foo", null)); + } + + @Override + Iterable createReplayTargets() { + String content = "this content needs to be replayed again and again"; + ByteBuffer expectedBuffer = CHARSET.encode(content); + BodyPublisher publisher = BodyPublishers.ofString(content, CHARSET); + return List.of(new ReplayTarget(expectedBuffer, publisher)); } } diff --git a/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/ReplayTestSupport.java b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/ReplayTestSupport.java new file mode 100644 index 00000000000..788b59e96b7 --- /dev/null +++ b/test/jdk/java/net/httpclient/HttpRequestBodyPublishers/ReplayTestSupport.java @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.net.http.HttpRequest.BodyPublisher; +import java.nio.ByteBuffer; +import java.util.concurrent.Flow; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Tests for verifying that a request body publisher supports multiple subscriptions, aka. replayable. + */ +public abstract class ReplayTestSupport { + + @ParameterizedTest + @ValueSource(strings = { + // 2 subscriptions + "subscribe-cancel-subscribe-cancel", + "subscribe-cancel-subscribe-request", + "subscribe-request-subscribe-cancel", + "subscribe-request-subscribe-request", + // 3 subscriptions + "subscribe-cancel-subscribe-cancel-subscribe-request", + "subscribe-cancel-subscribe-request-subscribe-cancel", + "subscribe-cancel-subscribe-request-subscribe-request", + "subscribe-request-subscribe-cancel-subscribe-cancel", + "subscribe-request-subscribe-cancel-subscribe-request", + "subscribe-request-subscribe-request-subscribe-cancel", + "subscribe-request-subscribe-request-subscribe-request", + }) + void testReplay(String opSequence) throws Exception { + for (ReplayTarget replayTarget : createReplayTargets()) { + try (replayTarget) { + System.err.printf("Executing test for replay target: %s%n", replayTarget); + testReplay(opSequence, replayTarget); + } + } + } + + private static void testReplay(String opSequence, ReplayTarget replayTarget) throws InterruptedException { + + // Create the publisher + ByteBuffer expectedBuffer = replayTarget.expectedBuffer; + BodyPublisher publisher = replayTarget.publisher; + assertEquals(replayTarget.expectedContentLength, publisher.contentLength()); + + // Execute the specified operations + RecordingSubscriber subscriber = null; + Flow.Subscription subscription = null; + String[] ops = opSequence.split("-"); + for (int opIndex = 0; opIndex < ops.length; opIndex++) { + String op = ops[opIndex]; + System.err.printf("Executing operation at index %s: %s%n", opIndex, op); + switch (op) { + + case "subscribe": { + subscriber = new RecordingSubscriber(); + publisher.subscribe(subscriber); + assertEquals("onSubscribe", subscriber.invocations.take()); + subscription = (Flow.Subscription) subscriber.invocations.take(); + break; + } + + case "request": { + assert subscription != null; + subscription.request(Long.MAX_VALUE); + if (expectedBuffer.hasRemaining()) { + assertEquals("onNext", subscriber.invocations.take()); + ByteBuffer actualBuffer = (ByteBuffer) subscriber.invocations.take(); + ByteBufferUtils.assertEquals(expectedBuffer, actualBuffer, null); + } + assertEquals("onComplete", subscriber.invocations.take()); + break; + } + + case "cancel": { + assert subscription != null; + subscription.cancel(); + break; + } + + default: throw new IllegalArgumentException("Unknown operation: " + op); + + } + } + + } + + abstract Iterable createReplayTargets(); + + public record ReplayTarget( + ByteBuffer expectedBuffer, + int expectedContentLength, + BodyPublisher publisher, + AutoCloseable resource) + implements AutoCloseable { + + public ReplayTarget(ByteBuffer expectedBuffer, BodyPublisher publisher) { + this(expectedBuffer, expectedBuffer.limit(), publisher, null); + } + + @Override + public void close() throws Exception { + if (resource != null) { + resource.close(); + } + } + + } + +}