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

tballison pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tika.git


The following commit(s) were added to refs/heads/main by this push:
     new c726a6b723 Fix tests that flake when building in parallel (#3124)
c726a6b723 is described below

commit c726a6b72316b21d8f14f44df536c7e90f99cb68
Author: Tim Allison <[email protected]>
AuthorDate: Thu Sep 3 09:40:26 2026 -0400

    Fix tests that flake when building in parallel (#3124)
---
 .../apache/tika/pipes/grpc/TikaGrpcServerTest.java | 55 ++++++++++++++++++++--
 tika-parent/pom.xml                                | 34 ++++++++++++-
 .../tika-parser-scientific-module/pom.xml          |  3 +-
 .../tika/detect/TestContainerAwareDetector.java    | 40 +++++++++++-----
 tika-pipes/tika-pipes-config-store-ignite/pom.xml  |  2 +
 tika-server/tika-server-core/pom.xml               |  3 +-
 tika-server/tika-server-standard/pom.xml           |  3 +-
 7 files changed, 118 insertions(+), 22 deletions(-)

diff --git 
a/tika-grpc/src/test/java/org/apache/tika/pipes/grpc/TikaGrpcServerTest.java 
b/tika-grpc/src/test/java/org/apache/tika/pipes/grpc/TikaGrpcServerTest.java
index 237c0c331b..7019676263 100644
--- a/tika-grpc/src/test/java/org/apache/tika/pipes/grpc/TikaGrpcServerTest.java
+++ b/tika-grpc/src/test/java/org/apache/tika/pipes/grpc/TikaGrpcServerTest.java
@@ -39,6 +39,8 @@ import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import com.asarkar.grpc.test.GrpcCleanupExtension;
 import com.asarkar.grpc.test.Resources;
@@ -55,6 +57,7 @@ import io.grpc.stub.StreamObserver;
 import org.apache.commons.io.FileUtils;
 import org.jetbrains.annotations.NotNull;
 import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
@@ -141,6 +144,47 @@ public class TikaGrpcServerTest {
         }
     }
 
+    private final List<TikaGrpcServerImpl> services = new ArrayList<>();
+
+    /**
+     * Every service built here is closed by {@link #closeServices()}. Only 
the manager that
+     * started a pipes-server child can delete that child's temp dir, so a 
service the test
+     * drops on the floor -- including on an assertion failure -- orphans it.
+     */
+    private TikaGrpcServerImpl newService(Path config) throws Exception {
+        TikaGrpcServerImpl service = new 
TikaGrpcServerImpl(config.toAbsolutePath().toString());
+        services.add(service);
+        return service;
+    }
+
+    @AfterEach
+    void closeServices() throws Exception {
+        for (TikaGrpcServerImpl service : services) {
+            //both are idempotent, so a test that already shut down in-line is 
fine
+            service.shutdown();
+            service.postShutdown();
+        }
+        services.clear();
+        assertNoOrphanedServerTempDirs();
+    }
+
+    /**
+     * Fails the test that leaked rather than leaving it for whoever notices 
the temp dir later.
+     * Reliable because surefire gives each module its own java.io.tmpdir and 
runs these
+     * classes one at a time.
+     */
+    private static void assertNoOrphanedServerTempDirs() throws Exception {
+        Path tmp = Paths.get(System.getProperty("java.io.tmpdir"));
+        try (Stream<Path> paths = Files.list(tmp)) {
+            List<String> orphans = paths
+                    .map(p -> p.getFileName().toString())
+                    .filter(n -> n.startsWith("pipes-server-"))
+                    .sorted()
+                    .collect(Collectors.toList());
+            assertTrue(orphans.isEmpty(), "orphaned pipes-server temp dirs: " 
+ orphans);
+        }
+    }
+
     static final int NUM_FETCHERS_TO_CREATE = 10;
 
     @Test
@@ -150,7 +194,7 @@ public class TikaGrpcServerTest {
         Server server = InProcessServerBuilder
                 .forName(serverName)
                 .directExecutor()
-                .addService(new 
TikaGrpcServerImpl(tikaConfigUnlocked.toAbsolutePath().toString()))
+                .addService(newService(tikaConfigUnlocked))
                 .build()
                 .start();
         resources.register(server, Duration.ofSeconds(10));
@@ -451,13 +495,14 @@ public class TikaGrpcServerTest {
         assertNotNull(reply.getMessage());
     }
 
-    private static TikaGrpc.TikaBlockingStub startServer(Resources resources, 
Path config)
+    //non-static: newService tracks the service on the per-test instance so it 
gets closed
+    private TikaGrpc.TikaBlockingStub startServer(Resources resources, Path 
config)
             throws Exception {
         String serverName = InProcessServerBuilder.generateName();
         Server server = InProcessServerBuilder
                 .forName(serverName)
                 .directExecutor()
-                .addService(new 
TikaGrpcServerImpl(config.toAbsolutePath().toString()))
+                .addService(newService(config))
                 .build()
                 .start();
         resources.register(server, Duration.ofSeconds(10));
@@ -481,7 +526,7 @@ public class TikaGrpcServerTest {
         Server server = InProcessServerBuilder
                 .forName(serverName)
                 .directExecutor()
-                .addService(new 
TikaGrpcServerImpl(tikaConfigUnlocked.toAbsolutePath().toString()))
+                .addService(newService(tikaConfigUnlocked))
                 .build()
                 .start();
         resources.register(server, Duration.ofSeconds(10));
@@ -544,7 +589,7 @@ public class TikaGrpcServerTest {
     public void testBiStream(Resources resources) throws Exception {
         String serverName = InProcessServerBuilder.generateName();
 
-        TikaGrpcServerImpl tikaGrpcServerImpl = new 
TikaGrpcServerImpl(tikaConfigUnlocked.toAbsolutePath().toString());
+        TikaGrpcServerImpl tikaGrpcServerImpl = newService(tikaConfigUnlocked);
         Server server = InProcessServerBuilder
                 .forName(serverName)
                 .directExecutor()
diff --git a/tika-parent/pom.xml b/tika-parent/pom.xml
index 9332897762..4b35d44456 100644
--- a/tika-parent/pom.xml
+++ b/tika-parent/pom.xml
@@ -310,6 +310,13 @@
     <puppycrawl.version>12.3.1</puppycrawl.version>
     <rat.version>0.18</rat.version>
     <scm.version>2.2.1</scm.version>
+    <!-- Per-module temp dir for the surefire JVM. Keeps one module's temp 
files out of
+         every other module's view: under -T1C the modules share one 
java.io.tmpdir, and
+         a test that counts apache-tika-* there races whatever else is building
+         (TestContainerAwareDetector.testRemovalTempfiles flaked exactly this 
way).
+         Created by the create-test-tmpdir antrun execution below, because the 
JVM will
+         not create a missing java.io.tmpdir; it throws NoSuchFileException. 
-->
+    <tika.test.tmpdir>${project.build.directory}/test-tmp</tika.test.tmpdir>
     
<checkstyle.configLocation>${maven.multiModuleProjectDirectory}/tika-parent/checkstyle.xml</checkstyle.configLocation>
     <spotless.goal>apply</spotless.goal>
     
<spotless.header.file>${maven.multiModuleProjectDirectory}/tika-parent/license-header.txt</spotless.header.file>
@@ -1506,9 +1513,34 @@
         <configuration>
           <!-- for manual testing of i18n, try for example: -Duser.language=zh 
-Duser.region=CN or
           -Duser.language=de -Duser.country=DE -->
-          <argLine>-Xmx4g -Djava.awt.headless=true @{surefireArgLine}</argLine>
+          <!-- java.io.tmpdir MUST be quoted: argLine is split on whitespace, 
and the Windows
+               job deliberately checks out into a path containing a space. It 
must also be a
+               real -D on the command line, not <systemPropertyVariables>: 
Files.createTempFile
+               reads the startup value, so a System.setProperty after JVM 
start is a no-op. -->
+          <argLine>-Xmx4g -Djava.awt.headless=true 
"-Djava.io.tmpdir=${tika.test.tmpdir}" @{surefireArgLine}</argLine>
         </configuration>
       </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-antrun-plugin</artifactId>
+        <version>${maven.antrun.version}</version>
+        <executions>
+          <execution>
+            <id>create-test-tmpdir</id>
+            <phase>process-test-resources</phase>
+            <goals>
+              <goal>run</goal>
+            </goals>
+            <configuration>
+              <target>
+                <!-- delete first so an incremental (unclean) build still 
starts empty -->
+                <delete dir="${tika.test.tmpdir}" />
+                <mkdir dir="${tika.test.tmpdir}" />
+              </target>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-shade-plugin</artifactId>
diff --git 
a/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/pom.xml 
b/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/pom.xml
index 9aeb79e0bf..46c135881d 100644
--- a/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/pom.xml
+++ b/tika-parsers/tika-parsers-extended/tika-parser-scientific-module/pom.xml
@@ -111,7 +111,8 @@ May the gods of dependency management fix this in the 
future.
       <plugin>
           <artifactId>maven-surefire-plugin</artifactId>
           <configuration>
-              <argLine>-Dcom.google.protobuf.use_unsafe_pre22_gencode</argLine>
+              <!-- this argLine replaces tika-parent's, so it has to repeat 
the tmpdir isolation -->
+              <argLine>-Dcom.google.protobuf.use_unsafe_pre22_gencode 
"-Djava.io.tmpdir=${tika.test.tmpdir}"</argLine>
           </configuration>
       </plugin>
       <plugin>
diff --git 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/java/org/apache/tika/detect/TestContainerAwareDetector.java
 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/java/org/apache/tika/detect/TestContainerAwareDetector.java
index 7840d8da31..2619286979 100644
--- 
a/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/java/org/apache/tika/detect/TestContainerAwareDetector.java
+++ 
b/tika-parsers/tika-parsers-standard/tika-parsers-standard-integration-tests/src/test/java/org/apache/tika/detect/TestContainerAwareDetector.java
@@ -30,8 +30,8 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Objects;
 import java.util.Random;
+import java.util.stream.Stream;
 
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream;
@@ -39,6 +39,7 @@ import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
 
 import org.apache.tika.MultiThreadedTikaTest;
 import org.apache.tika.Tika;
@@ -49,6 +50,7 @@ import org.apache.tika.detect.zip.OpenDocumentDetector;
 import org.apache.tika.detect.zip.StreamingZipContainerDetector;
 import org.apache.tika.detect.zip.ZipContainerDetector;
 import org.apache.tika.exception.TikaException;
+import org.apache.tika.io.TemporaryResources;
 import org.apache.tika.io.TikaInputStream;
 import org.apache.tika.metadata.HttpHeaders;
 import org.apache.tika.metadata.Metadata;
@@ -71,6 +73,8 @@ public class TestContainerAwareDetector extends 
MultiThreadedTikaTest {
     private final StreamingZipContainerDetector streamingZipDetector =
             new StreamingZipContainerDetector();
 
+    @TempDir
+    private Path tempDir;
 
     @AfterEach
     public void tearDown() throws TikaException {
@@ -416,23 +420,33 @@ public class TestContainerAwareDetector extends 
MultiThreadedTikaTest {
         assertRemovalTempfiles("test-documents.zip");
     }
 
-    private int countTemporaryFiles() {
-        //TODO: fix this.  This can prevent multiple parallel builds
-        //from running at the same time because there can be more than one
-        //process writing to apache-tika-*
-        return Objects.requireNonNull(new 
File(System.getProperty("java.io.tmpdir"))
-                .listFiles((dir, name) -> 
name.startsWith("apache-tika-"))).length;
+    /**
+     * Counts spool files in {@link #tempDir}. The shared java.io.tmpdir 
cannot be counted
+     * reliably: parallel module builds (-T1C) have other JVMs writing 
apache-tika-* there.
+     */
+    private int countTemporaryFiles() throws IOException {
+        try (Stream<Path> files = Files.list(tempDir)) {
+            return (int) files.count();
+        }
     }
 
     private void assertRemovalTempfiles(String fileName) throws Exception {
-        int numberOfTempFiles = countTemporaryFiles();
-
-        try (TikaInputStream tis = TikaInputStream
-                .get(getResourceAsUrl("/test-documents/" + fileName))) {
-            detector.detect(tis, new Metadata(), new ParseContext());
+        Metadata metadata = new Metadata();
+        metadata.set(TikaCoreProperties.RESOURCE_NAME_KEY, fileName);
+        TemporaryResources tmp = new TemporaryResources();
+        tmp.setTemporaryFileDirectory(tempDir);
+
+        try (InputStream is = 
getClass().getResourceAsStream("/test-documents/" + fileName)) {
+            assertNotNull(is);
+            try (TikaInputStream tis = TikaInputStream.get(is, tmp, metadata)) 
{
+                detector.detect(tis, metadata, new ParseContext());
+                //force the spool so this test can't pass vacuously
+                tis.getPath();
+                assertEquals(1, countTemporaryFiles());
+            }
         }
 
-        assertEquals(numberOfTempFiles, countTemporaryFiles());
+        assertEquals(0, countTemporaryFiles());
     }
 
     @Test
diff --git a/tika-pipes/tika-pipes-config-store-ignite/pom.xml 
b/tika-pipes/tika-pipes-config-store-ignite/pom.xml
index 349dee4645..b440392ea9 100644
--- a/tika-pipes/tika-pipes-config-store-ignite/pom.xml
+++ b/tika-pipes/tika-pipes-config-store-ignite/pom.xml
@@ -144,7 +144,9 @@
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-surefire-plugin</artifactId>
         <configuration>
+          <!-- this argLine replaces tika-parent's, so it has to repeat the 
tmpdir isolation -->
           <argLine>
+            "-Djava.io.tmpdir=${tika.test.tmpdir}"
             --add-opens java.base/java.nio=ALL-UNNAMED
             --add-opens java.base/java.util=ALL-UNNAMED
             --add-opens java.base/java.lang=ALL-UNNAMED
diff --git a/tika-server/tika-server-core/pom.xml 
b/tika-server/tika-server-core/pom.xml
index fd60838c42..6410e9e0d3 100644
--- a/tika-server/tika-server-core/pom.xml
+++ b/tika-server/tika-server-core/pom.xml
@@ -152,7 +152,8 @@
         <artifactId>maven-surefire-plugin</artifactId>
         <configuration>
           <redirectTestOutputToFile>true</redirectTestOutputToFile>
-          <argLine>-da -XX:+HeapDumpOnOutOfMemoryError -Xmx512m</argLine>
+          <!-- this argLine replaces tika-parent's, so it has to repeat the 
tmpdir isolation -->
+          <argLine>-da -XX:+HeapDumpOnOutOfMemoryError -Xmx512m 
"-Djava.io.tmpdir=${tika.test.tmpdir}"</argLine>
           <systemPropertyVariables>
             <java.util.logging.config.file>
               ${basedir}/src/main/resources/commons-logging.properties
diff --git a/tika-server/tika-server-standard/pom.xml 
b/tika-server/tika-server-standard/pom.xml
index 32fea14da8..a0bd28e946 100644
--- a/tika-server/tika-server-standard/pom.xml
+++ b/tika-server/tika-server-standard/pom.xml
@@ -122,7 +122,8 @@
         <artifactId>maven-surefire-plugin</artifactId>
         <configuration>
           <redirectTestOutputToFile>true</redirectTestOutputToFile>
-          <argLine>-da -XX:+HeapDumpOnOutOfMemoryError -Xmx512m</argLine>
+          <!-- this argLine replaces tika-parent's, so it has to repeat the 
tmpdir isolation -->
+          <argLine>-da -XX:+HeapDumpOnOutOfMemoryError -Xmx512m 
"-Djava.io.tmpdir=${tika.test.tmpdir}"</argLine>
           <systemPropertyVariables>
             <java.util.logging.config.file>
               ${basedir}/src/main/resources/log4j2.xml

Reply via email to