From 7a2733d48bb69431f4e48657dfdbbdce9dac7792 Mon Sep 17 00:00:00 2001 From: Ryan Schmitt Date: Sat, 20 Dec 2025 21:39:51 -0500 Subject: [PATCH] Parallelize tests (except on CI) This change enables JUnit 5's parallel mode. On my machine, this change causes `./mvnw` to run in a third of the time (from 90 seconds to 30 seconds). Integration test time specifically has been reduced 80% (from 75 seconds to 15 seconds). In order to prevent the introduction of reliability issues with CI builds, parallel execution is disabled in our `maven.yml`. --- .github/workflows/maven.yml | 2 +- httpcore5-testing/pom.xml | 7 +++- .../LoggingInitializationListener.java | 42 +++++++++++++++++++ .../hc/core5/testing/nio/AlpnTests.java | 4 ++ ....platform.launcher.LauncherSessionListener | 19 +++++++++ .../hc/core5/ssl/TestSSLContextBuilder.java | 2 + .../test/resources/junit-platform.properties | 22 ++++++++++ 7 files changed, 96 insertions(+), 2 deletions(-) create mode 100644 httpcore5-testing/src/test/java/org/apache/hc/core5/testing/LoggingInitializationListener.java create mode 100644 httpcore5-testing/src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener create mode 100644 httpcore5/src/test/resources/junit-platform.properties diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index c0342d7478..3219d051d2 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -49,4 +49,4 @@ jobs: distribution: 'temurin' java-version: ${{ matrix.java }} - name: Build with Maven - run: ./mvnw -V --file pom.xml --no-transfer-progress -DtrimStackTrace=false -P-use-toolchains,docker + run: ./mvnw -V --file pom.xml --no-transfer-progress -DtrimStackTrace=false -Djunit.jupiter.execution.parallel.enabled=false -P-use-toolchains,docker diff --git a/httpcore5-testing/pom.xml b/httpcore5-testing/pom.xml index 92fc1a6b20..2aad7462e5 100644 --- a/httpcore5-testing/pom.xml +++ b/httpcore5-testing/pom.xml @@ -90,6 +90,11 @@ junit-jupiter test + + org.junit.platform + junit-platform-launcher + test + org.hamcrest hamcrest @@ -205,4 +210,4 @@ - \ No newline at end of file + diff --git a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/LoggingInitializationListener.java b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/LoggingInitializationListener.java new file mode 100644 index 0000000000..0152385ad3 --- /dev/null +++ b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/LoggingInitializationListener.java @@ -0,0 +1,42 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package org.apache.hc.core5.testing; + +import org.junit.platform.launcher.LauncherSession; +import org.junit.platform.launcher.LauncherSessionListener; +import org.slf4j.LoggerFactory; + +/** + * Initialize Slf4j centrally before any tests run. This prevents Slf4j from + * spamming warnings during parallelized test runs. + */ +public class LoggingInitializationListener implements LauncherSessionListener { + @Override + public void launcherSessionOpened(final LauncherSession session) { + LoggerFactory.getILoggerFactory(); + } +} diff --git a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/AlpnTests.java b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/AlpnTests.java index 4bcae56d9b..5005a99aa4 100644 --- a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/AlpnTests.java +++ b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/AlpnTests.java @@ -37,10 +37,12 @@ import org.junit.jupiter.api.condition.EnabledForJreRange; import org.junit.jupiter.api.condition.JRE; import org.junit.jupiter.api.condition.OS; +import org.junit.jupiter.api.parallel.Isolated; class AlpnTests { @Nested + @Isolated @DisplayName("ALPN Oracle") class OracleJSSEAlpnTest extends AlpnTest { @@ -51,6 +53,7 @@ public OracleJSSEAlpnTest() throws Exception { } @Nested + @Isolated @DisplayName("ALPN Conscrypt (Java 9 or newer)") @DisabledOnOs(OS.MAC) @EnabledForJreRange(min = JRE.JAVA_9) @@ -63,6 +66,7 @@ public ConscryptJSSEAlpnTest() throws Exception { } @Nested + @Isolated @DisplayName("ALPN Conscrypt (Conscrypt specific TLS strategies)") @DisabledOnOs(OS.MAC) class ConscryptJSSEAndStrategiesAlpnTest extends AlpnTest { diff --git a/httpcore5-testing/src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener b/httpcore5-testing/src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener new file mode 100644 index 0000000000..0cd9f6efc3 --- /dev/null +++ b/httpcore5-testing/src/test/resources/META-INF/services/org.junit.platform.launcher.LauncherSessionListener @@ -0,0 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +org.apache.hc.core5.testing.LoggingInitializationListener diff --git a/httpcore5/src/test/java/org/apache/hc/core5/ssl/TestSSLContextBuilder.java b/httpcore5/src/test/java/org/apache/hc/core5/ssl/TestSSLContextBuilder.java index 81ac48c282..16539124dc 100644 --- a/httpcore5/src/test/java/org/apache/hc/core5/ssl/TestSSLContextBuilder.java +++ b/httpcore5/src/test/java/org/apache/hc/core5/ssl/TestSSLContextBuilder.java @@ -66,10 +66,12 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Isolated; /** * Unit tests for {@link SSLContextBuilder}. */ +@Isolated class TestSSLContextBuilder { static final String PROVIDER_SUN_JSSE = "SunJSSE"; diff --git a/httpcore5/src/test/resources/junit-platform.properties b/httpcore5/src/test/resources/junit-platform.properties new file mode 100644 index 0000000000..25e8e686d6 --- /dev/null +++ b/httpcore5/src/test/resources/junit-platform.properties @@ -0,0 +1,22 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +junit.jupiter.execution.parallel.enabled = true +junit.jupiter.execution.parallel.mode.default = concurrent +junit.jupiter.execution.parallel.mode.classes.default = concurrent +junit.jupiter.execution.parallel.config.strategy = dynamic