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

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


The following commit(s) were added to refs/heads/main by this push:
     new 17c0dd1e32c Fix testcontainers assume swallowing legit test failures 
(#4868)
17c0dd1e32c is described below

commit 17c0dd1e32c7f5f2f0ec52794cfa7d569f882cf5
Author: Eric Pugh <[email protected]>
AuthorDate: Thu Sep 3 10:48:55 2026 -0400

    Fix testcontainers assume swallowing legit test failures (#4868)
---
 .../ExtractingRequestHandlerTikaServerTest.java    | 50 +++-----------
 .../extraction/TikaServerContainerRule.java        | 79 ++++++++++++++++++++++
 .../TikaServerExtractionBackendTest.java           | 54 +++------------
 .../org/apache/solr/s3/AbstractS3ClientTest.java   |  4 +-
 .../org/apache/solr/s3/S3BackupRepositoryTest.java |  8 +--
 .../apache/solr/s3/S3IncrementalBackupTest.java    |  4 +-
 .../org/apache/solr/s3/S3InstallShardTest.java     |  4 +-
 .../org/apache/solr/s3/S3MockContainerRule.java    | 10 +--
 .../org/apache/solr/s3/S3OutputStreamTest.java     |  5 +-
 9 files changed, 118 insertions(+), 100 deletions(-)

diff --git 
a/solr/modules/extraction/src/test/org/apache/solr/handler/extraction/ExtractingRequestHandlerTikaServerTest.java
 
b/solr/modules/extraction/src/test/org/apache/solr/handler/extraction/ExtractingRequestHandlerTikaServerTest.java
index 23470049c98..f32dbb5289c 100644
--- 
a/solr/modules/extraction/src/test/org/apache/solr/handler/extraction/ExtractingRequestHandlerTikaServerTest.java
+++ 
b/solr/modules/extraction/src/test/org/apache/solr/handler/extraction/ExtractingRequestHandlerTikaServerTest.java
@@ -20,57 +20,27 @@ import 
com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters;
 import java.lang.invoke.MethodHandles;
 import org.apache.lucene.tests.util.QuickPatchThreadsFilter;
 import org.apache.solr.SolrIgnoredThreadsFilter;
-import org.junit.AfterClass;
-import org.junit.Assume;
 import org.junit.BeforeClass;
+import org.junit.ClassRule;
 import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.testcontainers.containers.GenericContainer;
-import org.testcontainers.containers.wait.strategy.Wait;
 
 @ThreadLeakFilters(filters = {SolrIgnoredThreadsFilter.class, 
QuickPatchThreadsFilter.class})
 public class ExtractingRequestHandlerTikaServerTest extends 
ExtractingRequestHandlerTestAbstract {
   private static final Logger log = 
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
-  public static GenericContainer<?> tika;
+  @ClassRule
+  public static final TikaServerContainerRule tikaContainer = new 
TikaServerContainerRule();
 
   @BeforeClass
-  @SuppressWarnings("resource")
-  public static void beforeClassTika() {
-    Assume.assumeFalse(
-        "Skipping on s390x", 
"s390x".equalsIgnoreCase(System.getProperty("os.arch")));
-
-    String baseUrl;
-    try {
-      tika =
-          new GenericContainer<>("apache/tika:3.2.3.0-full")
-              .withExposedPorts(9998)
-              .waitingFor(Wait.forListeningPort());
-      tika.start();
-      baseUrl = "http://"; + tika.getHost() + ":" + tika.getMappedPort(9998);
-      System.setProperty("solr.test.tikaserver.url", baseUrl);
-      System.setProperty("solr.test.extraction.backend", "tikaserver");
-      System.setProperty("solr.test.tikaserver.metadata.compatibility", 
"true");
-      log.info("Using extraction backend 'tikaserver'. Tika server running on 
{}", baseUrl);
-      initCore("solrconfig.xml", "schema.xml", getFile("extraction/solr"));
-    } catch (Throwable t) {
-      // Skip tests if Docker/Testcontainers are not available in the 
environment
-      Assume.assumeNoException("Docker/Testcontainers not available; skipping 
test", t);
-    }
-  }
-
-  @AfterClass
-  public static void afterClassTika() {
-    if (tika != null) {
-      try {
-        tika.stop();
-      } catch (Throwable t) {
-        // ignore
-      } finally {
-        tika = null;
-      }
-    }
+  public static void beforeClassTika() throws Exception {
+    String baseUrl = tikaContainer.getBaseUrl();
+    System.setProperty("solr.test.tikaserver.url", baseUrl);
+    System.setProperty("solr.test.extraction.backend", "tikaserver");
+    System.setProperty("solr.test.tikaserver.metadata.compatibility", "true");
+    log.info("Using extraction backend 'tikaserver'. Tika server running on 
{}", baseUrl);
+    initCore("solrconfig.xml", "schema.xml", getFile("extraction/solr"));
   }
 
   @Test
diff --git 
a/solr/modules/extraction/src/test/org/apache/solr/handler/extraction/TikaServerContainerRule.java
 
b/solr/modules/extraction/src/test/org/apache/solr/handler/extraction/TikaServerContainerRule.java
new file mode 100644
index 00000000000..7a6ae393799
--- /dev/null
+++ 
b/solr/modules/extraction/src/test/org/apache/solr/handler/extraction/TikaServerContainerRule.java
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+package org.apache.solr.handler.extraction;
+
+import java.lang.invoke.MethodHandles;
+import org.junit.Assume;
+import org.junit.rules.ExternalResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testcontainers.DockerClientFactory;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.wait.strategy.Wait;
+
+/**
+ * JUnit rule that manages a single Apache Tika Server Testcontainer. Declare 
as a
+ * {@code @ClassRule} so the (expensive to start) server is shared across all 
{@code @Test} methods
+ * in a class instead of being restarted for each one; JUnit starts it before, 
and stops it after,
+ * the whole class runs.
+ *
+ * <p>Skips the calling test (via {@link Assume}) instead of failing outright 
if
+ * Docker/Testcontainers isn't available in this environment.
+ */
+public class TikaServerContainerRule extends ExternalResource {
+
+  private static final Logger log = 
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+  public static final String TIKA_DOCKER_IMAGE = "apache/tika:3.2.3.0-full";
+
+  private GenericContainer<?> tika;
+  private String baseUrl;
+
+  @Override
+  @SuppressWarnings("resource")
+  protected void before() {
+    Assume.assumeFalse(
+        "Skipping on s390x", 
"s390x".equalsIgnoreCase(System.getProperty("os.arch")));
+    Assume.assumeTrue(
+        "Docker/Testcontainers not available; skipping test",
+        DockerClientFactory.instance().isDockerAvailable());
+
+    tika =
+        new GenericContainer<>(TIKA_DOCKER_IMAGE)
+            .withExposedPorts(9998)
+            .waitingFor(Wait.forListeningPort());
+    tika.start();
+    baseUrl = "http://"; + tika.getHost() + ":" + tika.getMappedPort(9998);
+  }
+
+  @Override
+  protected void after() {
+    if (tika != null) {
+      try {
+        tika.stop();
+      } catch (Exception e) {
+        log.error("Exception stopping Tika container", e);
+      } finally {
+        tika = null;
+      }
+    }
+  }
+
+  public String getBaseUrl() {
+    return baseUrl;
+  }
+}
diff --git 
a/solr/modules/extraction/src/test/org/apache/solr/handler/extraction/TikaServerExtractionBackendTest.java
 
b/solr/modules/extraction/src/test/org/apache/solr/handler/extraction/TikaServerExtractionBackendTest.java
index b7723da94ff..326ab818596 100644
--- 
a/solr/modules/extraction/src/test/org/apache/solr/handler/extraction/TikaServerExtractionBackendTest.java
+++ 
b/solr/modules/extraction/src/test/org/apache/solr/handler/extraction/TikaServerExtractionBackendTest.java
@@ -29,11 +29,8 @@ import org.apache.solr.SolrIgnoredThreadsFilter;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.handler.extraction.fromtika.ToXMLContentHandler;
-import org.junit.AfterClass;
-import org.junit.Assume;
-import org.junit.BeforeClass;
+import org.junit.ClassRule;
 import org.junit.Test;
-import org.testcontainers.containers.GenericContainer;
 
 /**
  * Integration tests for TikaServerExtractionBackend using a real Tika Server 
via Testcontainers.
@@ -60,35 +57,8 @@ public class TikaServerExtractionBackendTest extends 
SolrTestCaseJ4 {
     }
   }
 
-  private static GenericContainer<?> tika;
-  private static String baseUrl;
-
-  @SuppressWarnings("resource")
-  @BeforeClass
-  public static void startTikaServer() {
-    Assume.assumeFalse(
-        "Skipping on s390x", 
"s390x".equalsIgnoreCase(System.getProperty("os.arch")));
-
-    try {
-      tika = new 
GenericContainer<>("apache/tika:3.2.3.0-full").withExposedPorts(9998);
-      tika.start();
-      baseUrl = "http://"; + tika.getHost() + ":" + tika.getMappedPort(9998);
-    } catch (Throwable t) {
-      // Skip tests if Docker/Testcontainers are not available in the 
environment
-      Assume.assumeNoException("Docker/Testcontainers not available; skipping 
TikaServer tests", t);
-    }
-  }
-
-  @AfterClass
-  public static void stopTikaServer() {
-    if (tika != null) {
-      try {
-        tika.stop();
-      } catch (Throwable ignore) {
-      }
-      tika = null;
-    }
-  }
+  @ClassRule
+  public static final TikaServerContainerRule tikaContainer = new 
TikaServerContainerRule();
 
   private static ExtractionRequest newRequest(
       String resourceName,
@@ -109,8 +79,8 @@ public class TikaServerExtractionBackendTest extends 
SolrTestCaseJ4 {
 
   @Test
   public void testExtractTextAndMetadata() throws Exception {
-    Assume.assumeTrue("Tika server container not started", tika != null);
-    try (TikaServerExtractionBackend backend = new 
TikaServerExtractionBackend(baseUrl)) {
+    try (TikaServerExtractionBackend backend =
+        new TikaServerExtractionBackend(tikaContainer.getBaseUrl())) {
       byte[] data = "Hello TestContainers".getBytes(StandardCharsets.UTF_8);
       try (ByteArrayInputStream in = new ByteArrayInputStream(data)) {
         ExtractionResult res = backend.extract(in, newRequest("test.txt", 
"text/plain", "text"));
@@ -129,8 +99,8 @@ public class TikaServerExtractionBackendTest extends 
SolrTestCaseJ4 {
 
   @Test
   public void testExtractWithSaxHandlerXml() throws Exception {
-    Assume.assumeTrue("Tika server container not started", tika != null);
-    try (TikaServerExtractionBackend backend = new 
TikaServerExtractionBackend(baseUrl)) {
+    try (TikaServerExtractionBackend backend =
+        new TikaServerExtractionBackend(tikaContainer.getBaseUrl())) {
       byte[] data = "Hello XML".getBytes(StandardCharsets.UTF_8);
       ExtractionRequest request = newRequest("test.txt", "text/plain", "xml");
       try (ByteArrayInputStream in = new ByteArrayInputStream(data)) {
@@ -151,8 +121,8 @@ public class TikaServerExtractionBackendTest extends 
SolrTestCaseJ4 {
 
   @Test
   public void testPdfWithImageRecursive() throws Exception {
-    Assume.assumeTrue("Tika server container not started", tika != null);
-    try (TikaServerExtractionBackend backend = new 
TikaServerExtractionBackend(baseUrl)) {
+    try (TikaServerExtractionBackend backend =
+        new TikaServerExtractionBackend(tikaContainer.getBaseUrl())) {
       byte[] data = 
Files.readAllBytes(getFile("extraction/pdf-with-image.pdf"));
       // Enable recursive extraction and set header to extract images from PDF
       ExtractionRequest request =
@@ -182,11 +152,10 @@ public class TikaServerExtractionBackendTest extends 
SolrTestCaseJ4 {
 
   @Test
   public void testMaxCharsLimitEnforced() throws Exception {
-    Assume.assumeTrue("Tika server container not started", tika != null);
     // Set a very small max chars limit and attempt to extract more than that
     long maxChars = 10L;
     try (TikaServerExtractionBackend backend =
-        new TikaServerExtractionBackend(baseUrl, 180, null, maxChars)) {
+        new TikaServerExtractionBackend(tikaContainer.getBaseUrl(), 180, null, 
maxChars)) {
       byte[] data =
           ("This content is definitely longer than ten characters.")
               .getBytes(StandardCharsets.UTF_8);
@@ -205,10 +174,9 @@ public class TikaServerExtractionBackendTest extends 
SolrTestCaseJ4 {
 
   @Test
   public void testMaxCharsLimitEnforcedWithSaxHandler() throws Exception {
-    Assume.assumeTrue("Tika server container not started", tika != null);
     long maxChars = 10L;
     try (TikaServerExtractionBackend backend =
-        new TikaServerExtractionBackend(baseUrl, 180, null, maxChars)) {
+        new TikaServerExtractionBackend(tikaContainer.getBaseUrl(), 180, null, 
maxChars)) {
       byte[] data =
           ("This content is definitely longer than ten characters.")
               .getBytes(StandardCharsets.UTF_8);
diff --git 
a/solr/modules/s3-repository/src/test/org/apache/solr/s3/AbstractS3ClientTest.java
 
b/solr/modules/s3-repository/src/test/org/apache/solr/s3/AbstractS3ClientTest.java
index a68c16d0636..24bc0e1f3c5 100644
--- 
a/solr/modules/s3-repository/src/test/org/apache/solr/s3/AbstractS3ClientTest.java
+++ 
b/solr/modules/s3-repository/src/test/org/apache/solr/s3/AbstractS3ClientTest.java
@@ -44,7 +44,7 @@ public class AbstractS3ClientTest extends SolrTestCaseJ4 {
   protected static final String BUCKET_NAME = "test-bucket";
 
   @ClassRule
-  public static final S3MockContainerRule S3_MOCK_RULE = new 
S3MockContainerRule(BUCKET_NAME);
+  public static final S3MockContainerRule s3MockContainer = new 
S3MockContainerRule(BUCKET_NAME);
 
   S3StorageClient client;
   private SocketProxy proxy;
@@ -58,7 +58,7 @@ public class AbstractS3ClientTest extends SolrTestCaseJ4 {
 
     // We are using a proxy in front of S3Mock to be able to test connection 
loss
     proxy = new SocketProxy();
-    proxy.open(URI.create(S3_MOCK_RULE.getHttpEndpoint()));
+    proxy.open(URI.create(s3MockContainer.getHttpEndpoint()));
     client =
         new S3StorageClient(
             BUCKET_NAME,
diff --git 
a/solr/modules/s3-repository/src/test/org/apache/solr/s3/S3BackupRepositoryTest.java
 
b/solr/modules/s3-repository/src/test/org/apache/solr/s3/S3BackupRepositoryTest.java
index e75f9bef733..5d4f6c3af54 100644
--- 
a/solr/modules/s3-repository/src/test/org/apache/solr/s3/S3BackupRepositoryTest.java
+++ 
b/solr/modules/s3-repository/src/test/org/apache/solr/s3/S3BackupRepositoryTest.java
@@ -63,7 +63,7 @@ public class S3BackupRepositoryTest extends 
AbstractBackupRepositoryTest {
   public Path temporaryFolder;
 
   @ClassRule
-  public static final S3MockContainerRule S3_MOCK_RULE = new 
S3MockContainerRule(BUCKET_NAME);
+  public static final S3MockContainerRule s3MockContainer = new 
S3MockContainerRule(BUCKET_NAME);
 
   @Before
   @Override
@@ -338,18 +338,18 @@ public class S3BackupRepositoryTest extends 
AbstractBackupRepositoryTest {
     NamedList<Object> args = new NamedList<>();
     args.add(S3BackupRepositoryConfig.REGION, Region.US_EAST_1.id());
     args.add(S3BackupRepositoryConfig.BUCKET_NAME, BUCKET_NAME);
-    args.add(S3BackupRepositoryConfig.ENDPOINT, 
S3_MOCK_RULE.getHttpEndpoint());
+    args.add(S3BackupRepositoryConfig.ENDPOINT, 
s3MockContainer.getHttpEndpoint());
     return args;
   }
 
   private void pushObject(String path, String content) {
-    try (S3Client s3 = S3_MOCK_RULE.createS3ClientV2()) {
+    try (S3Client s3 = s3MockContainer.createS3ClientV2()) {
       s3.putObject(b -> b.bucket(BUCKET_NAME).key(path), 
RequestBody.fromString(content));
     }
   }
 
   private Path pullObject(String path) throws IOException {
-    try (S3Client s3 = S3_MOCK_RULE.createS3ClientV2()) {
+    try (S3Client s3 = s3MockContainer.createS3ClientV2()) {
       Path file = Files.createTempFile(temporaryFolder, "junit", null);
       InputStream input =
           s3.getObject(
diff --git 
a/solr/modules/s3-repository/src/test/org/apache/solr/s3/S3IncrementalBackupTest.java
 
b/solr/modules/s3-repository/src/test/org/apache/solr/s3/S3IncrementalBackupTest.java
index d2afd7e8119..f20d3f89cac 100644
--- 
a/solr/modules/s3-repository/src/test/org/apache/solr/s3/S3IncrementalBackupTest.java
+++ 
b/solr/modules/s3-repository/src/test/org/apache/solr/s3/S3IncrementalBackupTest.java
@@ -50,7 +50,7 @@ public class S3IncrementalBackupTest extends 
AbstractIncrementalBackupTest {
   private static final String BUCKET_NAME = 
S3IncrementalBackupTest.class.getSimpleName();
 
   @ClassRule
-  public static final S3MockContainerRule S3_MOCK_RULE = new 
S3MockContainerRule(BUCKET_NAME);
+  public static final S3MockContainerRule s3MockContainer = new 
S3MockContainerRule(BUCKET_NAME);
 
   public static final String SOLR_XML =
       "<solr>\n"
@@ -134,7 +134,7 @@ public class S3IncrementalBackupTest extends 
AbstractIncrementalBackupTest {
                 .replace("BAD_BUCKET", BUCKET_NAME)
                 .replace("BUCKET", BUCKET_NAME)
                 .replace("REGION", Region.US_EAST_1.id())
-                .replace("ENDPOINT", S3_MOCK_RULE.getHttpEndpoint()))
+                .replace("ENDPOINT", s3MockContainer.getHttpEndpoint()))
         .configure();
   }
 
diff --git 
a/solr/modules/s3-repository/src/test/org/apache/solr/s3/S3InstallShardTest.java
 
b/solr/modules/s3-repository/src/test/org/apache/solr/s3/S3InstallShardTest.java
index b239ae4b660..af6b77e031b 100644
--- 
a/solr/modules/s3-repository/src/test/org/apache/solr/s3/S3InstallShardTest.java
+++ 
b/solr/modules/s3-repository/src/test/org/apache/solr/s3/S3InstallShardTest.java
@@ -67,7 +67,7 @@ public class S3InstallShardTest extends 
AbstractInstallShardTest {
       
AbstractInstallShardTest.defaultSolrXmlTextWithBackupRepository(BACKUP_REPOSITORY_XML);
 
   @ClassRule
-  public static final S3MockContainerRule S3_MOCK_RULE = new 
S3MockContainerRule(BUCKET_NAME);
+  public static final S3MockContainerRule s3MockContainer = new 
S3MockContainerRule(BUCKET_NAME);
 
   @BeforeClass
   public static void setupClass() throws Exception {
@@ -82,7 +82,7 @@ public class S3InstallShardTest extends 
AbstractInstallShardTest {
             SOLR_XML
                 .replace("BUCKET", BUCKET_NAME)
                 .replace("REGION", Region.US_EAST_1.id())
-                .replace("ENDPOINT", S3_MOCK_RULE.getHttpEndpoint()))
+                .replace("ENDPOINT", s3MockContainer.getHttpEndpoint()))
         .configure();
 
     bootstrapBackupRepositoryData("/");
diff --git 
a/solr/modules/s3-repository/src/test/org/apache/solr/s3/S3MockContainerRule.java
 
b/solr/modules/s3-repository/src/test/org/apache/solr/s3/S3MockContainerRule.java
index 48eb7a84021..216483e8edf 100644
--- 
a/solr/modules/s3-repository/src/test/org/apache/solr/s3/S3MockContainerRule.java
+++ 
b/solr/modules/s3-repository/src/test/org/apache/solr/s3/S3MockContainerRule.java
@@ -24,6 +24,7 @@ import org.junit.Assume;
 import org.junit.rules.ExternalResource;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.testcontainers.DockerClientFactory;
 import org.testcontainers.utility.DockerImageName;
 import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
 import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
@@ -55,14 +56,13 @@ public class S3MockContainerRule extends ExternalResource {
 
   @Override
   protected void before() {
+    Assume.assumeTrue(
+        "Docker/Testcontainers not available; skipping test",
+        DockerClientFactory.instance().isDockerAvailable());
     s3MockContainer =
         new S3MockContainer(DockerImageName.parse(S3MOCK_DOCKER_IMAGE))
             .withInitialBuckets(bucketName);
-    try {
-      s3MockContainer.start();
-    } catch (Throwable t) {
-      Assume.assumeNoException("Docker/Testcontainers not available; skipping 
test", t);
-    }
+    s3MockContainer.start();
   }
 
   @Override
diff --git 
a/solr/modules/s3-repository/src/test/org/apache/solr/s3/S3OutputStreamTest.java
 
b/solr/modules/s3-repository/src/test/org/apache/solr/s3/S3OutputStreamTest.java
index f887c55cd0f..4df12150956 100644
--- 
a/solr/modules/s3-repository/src/test/org/apache/solr/s3/S3OutputStreamTest.java
+++ 
b/solr/modules/s3-repository/src/test/org/apache/solr/s3/S3OutputStreamTest.java
@@ -42,13 +42,14 @@ public class S3OutputStreamTest extends SolrTestCaseJ4 {
 
   private static final String BUCKET = 
S3OutputStreamTest.class.getSimpleName();
 
-  @ClassRule public static final S3MockContainerRule S3_MOCK_RULE = new 
S3MockContainerRule(BUCKET);
+  @ClassRule
+  public static final S3MockContainerRule s3MockContainer = new 
S3MockContainerRule(BUCKET);
 
   private S3Client s3;
 
   @Before
   public void setUpClient() {
-    s3 = S3_MOCK_RULE.createS3ClientV2();
+    s3 = s3MockContainer.createS3ClientV2();
   }
 
   @After

Reply via email to