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 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. + * + *

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.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 boolean reject(Thread t) { } } - 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 @@ private static ExtractionRequest newRequest( @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 void testExtractTextAndMetadata() throws Exception { @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 void testExtractWithSaxHandlerXml() throws Exception { @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 @@ private ExtractionRequest newRequest(String file, String contentType, String con @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 void testMaxCharsLimitEnforced() throws Exception { @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 void setUpClient() throws Exception { // 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 @@ protected NamedList getBaseBackupRepositoryConfiguration() { NamedList 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 = "\n" @@ -134,7 +134,7 @@ public static void setupClass() throws Exception { .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 static void setupClass() throws Exception { 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.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 S3MockContainerRule(String bucketName) { @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