This is an automated email from the ASF dual-hosted git repository.

tballison pushed a commit to branch TIKA-4809-stage-1
in repository https://gitbox.apache.org/repos/asf/tika.git

commit 0df56b8e26310ec5dd0e8955a0a9992980414075
Author: tallison <[email protected]>
AuthorDate: Fri Aug 7 10:19:28 2026 -0400

    TIKA-4809: Fix IntegrationTestBase shared static port and TempDir lifecycle
---
 .../tika/server/core/IntegrationTestBase.java      | 29 +++++++------
 .../core/TikaServerPipesIntegrationTest.java       | 16 +++++--
 .../src/test/resources/junit-platform.properties   | 50 ++++++++++++++++++++++
 3 files changed, 78 insertions(+), 17 deletions(-)

diff --git 
a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/IntegrationTestBase.java
 
b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/IntegrationTestBase.java
index 19ee324ef0..e5f644188f 100644
--- 
a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/IntegrationTestBase.java
+++ 
b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/IntegrationTestBase.java
@@ -17,9 +17,7 @@
 package org.apache.tika.server.core;
 
 import java.io.IOException;
-import java.nio.file.Files;
 import java.nio.file.Path;
-import java.nio.file.StandardCopyOption;
 import java.time.Duration;
 import java.time.Instant;
 import java.util.ArrayList;
@@ -33,12 +31,17 @@ import org.apache.cxf.common.logging.LogUtils;
 import org.apache.cxf.jaxrs.client.WebClient;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.TestInstance;
 import org.junit.jupiter.api.io.TempDir;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import org.apache.tika.TikaTest;
 
+// PER_CLASS so subclasses' state (notably TEMP_WORKING_DIR below) is isolated
+// per test class instead of shared via one static field on this common base --
+// a prerequisite for running subclasses' test classes concurrently.
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
 public class IntegrationTestBase extends TikaTest {
 
     static final String TEST_HELLO_WORLD = 
"test-documents/mock/hello_world.xml";
@@ -52,24 +55,24 @@ public class IntegrationTestBase extends TikaTest {
     static final String STATUS_PATH = "/status";
 
     static final long MAX_WAIT_MS = 60000;
-    static final int integrationTestPort = TestPortAllocator.findFreePort();
-    static final String INTEGRATION_TEST_PORT = 
String.valueOf(integrationTestPort);
-    protected static final String endPoint = "http://localhost:"; + 
INTEGRATION_TEST_PORT;
     private static final Logger LOG = 
LoggerFactory.getLogger(IntegrationTestBase.class);
 
+    // Instance (not static) so each test method -- JUnit5 creates a fresh test
+    // instance per @Test method by default -- gets its own port. These used 
to be
+    // `static final`, computed once for the whole JVM fork and shared by every
+    // subclass; harmless serially, but a guaranteed bind collision if two
+    // subclasses' tests ever ran concurrently in the same fork.
+    final int integrationTestPort = TestPortAllocator.findFreePort();
+    final String INTEGRATION_TEST_PORT = String.valueOf(integrationTestPort);
+    protected final String endPoint = "http://localhost:"; + 
INTEGRATION_TEST_PORT;
+
     @TempDir
-    static Path TEMP_WORKING_DIR;
-    static Path LOG_FILE;
-    static Path STREAMS_DIR;
+    Path TEMP_WORKING_DIR;
     protected Process process = null;
 
     @BeforeAll
-    public static void staticSetup() throws Exception {
+    public void setUp() throws Exception {
         LogUtils.setLoggerClass(NullWebClientLogger.class);
-
-        LOG_FILE = Files.createTempFile(TEMP_WORKING_DIR, 
"tika-server-integration", ".xml");
-        
Files.copy(TikaServerIntegrationTest.class.getResourceAsStream("/logging/log4j2_forked.xml"),
 LOG_FILE, StandardCopyOption.REPLACE_EXISTING);
-        STREAMS_DIR = Files.createTempDirectory(TEMP_WORKING_DIR, 
"tika-server-integration");
     }
 
     @AfterEach
diff --git 
a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaServerPipesIntegrationTest.java
 
b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaServerPipesIntegrationTest.java
index 4827641de2..01637dd308 100644
--- 
a/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaServerPipesIntegrationTest.java
+++ 
b/tika-server/tika-server-core/src/test/java/org/apache/tika/server/core/TikaServerPipesIntegrationTest.java
@@ -38,6 +38,7 @@ import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
 
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.parser.ParseContext;
@@ -52,6 +53,13 @@ import org.apache.tika.utils.ProcessUtils;
 
 public class TikaServerPipesIntegrationTest extends IntegrationTestBase {
 
+    // Own dedicated static @TempDir, not the inherited (instance, 
per-test-method)
+    // TEMP_WORKING_DIR from IntegrationTestBase -- @BeforeAll needs a 
directory
+    // available before any test method runs, and instance-field @TempDir isn't
+    // populated until right before each @Test method regardless of 
test-instance
+    // lifecycle.
+    @TempDir
+    private static Path SETUP_DIR;
     private static Path TEMP_OUTPUT_DIR;
     private static Path TIKA_CONFIG;
     private static Path TIKA_CONFIG_TIMEOUT;
@@ -59,16 +67,16 @@ public class TikaServerPipesIntegrationTest extends 
IntegrationTestBase {
 
     @BeforeAll
     public static void setUpBeforeClass() throws Exception {
-        Path inputDir = TEMP_WORKING_DIR.resolve("input");
-        TEMP_OUTPUT_DIR = TEMP_WORKING_DIR.resolve("output");
+        Path inputDir = SETUP_DIR.resolve("input");
+        TEMP_OUTPUT_DIR = SETUP_DIR.resolve("output");
         Files.createDirectories(inputDir);
         Files.createDirectories(TEMP_OUTPUT_DIR);
 
         for (String mockFile : FILES) {
             
Files.copy(TikaPipesTest.class.getResourceAsStream("/test-documents/mock/" + 
mockFile), inputDir.resolve(mockFile));
         }
-        TIKA_CONFIG = TEMP_WORKING_DIR.resolve("tika-config.json");
-        TIKA_CONFIG_TIMEOUT = 
TEMP_WORKING_DIR.resolve("tika-config-timeout.json");
+        TIKA_CONFIG = SETUP_DIR.resolve("tika-config.json");
+        TIKA_CONFIG_TIMEOUT = SETUP_DIR.resolve("tika-config-timeout.json");
         CXFTestBase.createPluginsConfig(TIKA_CONFIG, inputDir, 
TEMP_OUTPUT_DIR, null, 5000L);
         CXFTestBase.createPluginsConfig(TIKA_CONFIG_TIMEOUT, inputDir, 
TEMP_OUTPUT_DIR, null, 500L);
 
diff --git 
a/tika-server/tika-server-core/src/test/resources/junit-platform.properties 
b/tika-server/tika-server-core/src/test/resources/junit-platform.properties
new file mode 100644
index 0000000000..d4dd0c5f28
--- /dev/null
+++ b/tika-server/tika-server-core/src/test/resources/junit-platform.properties
@@ -0,0 +1,50 @@
+#
+# 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.
+#
+
+# Tried and disabled -- three attempts now, each finding a real blocker:
+#
+# 1. Most classes here extend CXFTestBase, which used to route through
+#    TikaResource's static (process-wide singleton) config fields --
+#    concurrently-running CXFTestBase-derived classes stomped on each other's
+#    TikaResource state (500s, null responses, DirectoryNotEmpty races).
+#    FIXED: TikaResource no longer holds any static state; it's
+#    constructor-injected per instance now.
+#
+# 2. TikaServerIntegrationTest and TikaServerPipesIntegrationTest (each forks
+#    its own separate tika-server OS process) failed when run concurrently
+#    with each other, even after fix #1: PipesConfig's CPU auto-sizing is
+#    computed independently per forked process, with no awareness of sibling
+#    OS processes also running on the same machine.
+#
+#    Attempted fix: pin -XX:ActiveProcessorCount explicitly in both classes'
+#    pipes configs instead of leaving it to auto-sizing (pins the forked
+#    *child* workers' CPU view). This was NOT sufficient -- running both
+#    classes concurrently still produced real failures: a
+#    java.nio.file.NoSuchFileException creating a forked pipes-server's temp
+#    dir (a race, not CPU-sizing), and two ~60s server-startup timeouts
+#    (test1WayTLS/test2WayTLS in TikaServerIntegrationTest). Pinning the
+#    forked children's ActiveProcessorCount doesn't address CPU contention
+#    between the two *parent* tika-server JVMs during their own startup --
+#    that's a separate, still-open problem. Reverted the concurrency
+#    annotations and config changes; left this disabled rather than merge
+#    something unreliable. Revisit only with a fix for parent-JVM startup
+#    contention (and the temp-dir race) in hand, not just child CPU pinning.
+junit.jupiter.execution.parallel.enabled = false
+junit.jupiter.execution.parallel.mode.default = same_thread
+junit.jupiter.execution.parallel.mode.classes.default = same_thread
+junit.jupiter.execution.parallel.config.strategy = fixed
+junit.jupiter.execution.parallel.config.fixed.parallelism = 4

Reply via email to