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

dsmiley 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 21bc46c43c9 SOLR-16573:SolrClientTestRule for EmbeddedSolrServer 
(#1218)
21bc46c43c9 is described below

commit 21bc46c43c94cc90589cc536ffc7518908e64240
Author: Joshua Ouma <[email protected]>
AuthorDate: Wed Feb 8 06:17:46 2023 +0300

    SOLR-16573:SolrClientTestRule for EmbeddedSolrServer (#1218)
    
    New SolrClientTestRule abstraction with one implementation: 
EmbeddedSolrServerTestRule
    
    EmbeddedSolrServerTestBase and AbstractEmbeddedSolrServerTestCase were 
modified to use this abstraction, and yet were also marked deprecated to 
encourage direct use of the rule. A solr.xml file is optional. Some affected 
tests will run more efficiently because (A) they no longer do file I/O to 
create a temporary solr-home because the rule is able to use existing solr 
homes and configSets in-place and because (B) initCore() can double-parse a 
solrconfig.xml and schema.xml.
    
    SolrTestCaseJ4 / SolrTestCase: Moved ObjectReleaseTracker checking to use a 
Rule so that it's compatible with Rules that use ObjectReleaseTracker.
    
    ---------
    
    Co-authored-by: David Smiley <[email protected]>
---
 solr/CHANGES.txt                                   |   3 +
 .../solr/update/UpdateShardHandlerConfig.java      |   9 ++
 .../test/org/apache/solr/update/RootFieldTest.java |  12 +-
 .../AbstractAtomicUpdatesMultivalueTestBase.java   |  33 +++--
 .../JavaBinAtomicUpdateMultivalueTest.java         |   7 +-
 .../processor/XMLAtomicUpdateMultivalueTest.java   |   7 +-
 .../org/apache/solr/client/solrj/GetByIdTest.java  |  12 +-
 .../solr/client/solrj/LargeVolumeTestBase.java     |   8 +-
 .../AbstractEmbeddedSolrServerTestCase.java        |  32 +++--
 .../solrj/embedded/LargeVolumeBinaryJettyTest.java |   5 +-
 .../solrj/embedded/LargeVolumeEmbeddedTest.java    |   5 +-
 .../solrj/embedded/LargeVolumeJettyTest.java       |   6 +-
 .../solrj/embedded/TestEmbeddedSolrServer.java     |  49 +++----
 .../client/solrj/embedded/TestSolrProperties.java  |  12 +-
 .../solr/client/solrj/request/SolrPingTest.java    |  15 +--
 .../solr/client/solrj/request/TestCoreAdmin.java   |  51 +------
 ...DirectJsonQueryRequestFacetingEmbeddedTest.java |  42 ++----
 .../client/solrj/response/TermsResponseTest.java   |   9 +-
 .../solrj/response/TestSpellCheckResponse.java     |  19 ++-
 .../apache/solr/EmbeddedSolrServerTestBase.java    | 105 ++-------------
 .../src/java/org/apache/solr/SolrTestCase.java     |  37 ++++--
 .../src/java/org/apache/solr/SolrTestCaseJ4.java   |   2 -
 .../solr/util/EmbeddedSolrServerTestRule.java      | 110 +++++++++++++++
 .../org/apache/solr/util/SolrClientTestRule.java   | 147 +++++++++++++++++++++
 .../src/java/org/apache/solr/util/TestHarness.java |  12 +-
 25 files changed, 447 insertions(+), 302 deletions(-)

diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 174c88d8283..0da2215662d 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -268,6 +268,9 @@ Other Changes
 
 * SOLR-15955: Upgrade to Jetty 10.x (Mark Miller, Kevin Risden)
 
+* SOLR-16573: Introduce EmbeddedSolrServerTestRule, a JUnit 4 TestRule for 
Solr testing
+  (Joshua Ouma, David Smiley)
+
 ==================  9.1.1 ==================
 
 Bug Fixes
diff --git 
a/solr/core/src/java/org/apache/solr/update/UpdateShardHandlerConfig.java 
b/solr/core/src/java/org/apache/solr/update/UpdateShardHandlerConfig.java
index eb10a59e745..9913f86e397 100644
--- a/solr/core/src/java/org/apache/solr/update/UpdateShardHandlerConfig.java
+++ b/solr/core/src/java/org/apache/solr/update/UpdateShardHandlerConfig.java
@@ -32,6 +32,15 @@ public class UpdateShardHandlerConfig {
           DEFAULT_METRICNAMESTRATEGY,
           DEFAULT_MAXRECOVERYTHREADS);
 
+  public static final UpdateShardHandlerConfig TEST_DEFAULT =
+      new UpdateShardHandlerConfig(
+          HttpClientUtil.DEFAULT_MAXCONNECTIONS,
+          HttpClientUtil.DEFAULT_MAXCONNECTIONSPERHOST,
+          30000,
+          30000,
+          UpdateShardHandlerConfig.DEFAULT_METRICNAMESTRATEGY,
+          UpdateShardHandlerConfig.DEFAULT_MAXRECOVERYTHREADS);
+
   private final int maxUpdateConnections;
 
   private final int maxUpdateConnectionsPerHost;
diff --git a/solr/core/src/test/org/apache/solr/update/RootFieldTest.java 
b/solr/core/src/test/org/apache/solr/update/RootFieldTest.java
index 1592e71f5d0..372c6ac08ed 100644
--- a/solr/core/src/test/org/apache/solr/update/RootFieldTest.java
+++ b/solr/core/src/test/org/apache/solr/update/RootFieldTest.java
@@ -19,7 +19,9 @@ package org.apache.solr.update;
 
 import static org.hamcrest.CoreMatchers.is;
 
+import java.nio.file.Path;
 import org.apache.solr.EmbeddedSolrServerTestBase;
+import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrQuery;
 import org.apache.solr.common.SolrDocument;
@@ -46,7 +48,15 @@ public class RootFieldTest extends 
EmbeddedSolrServerTestBase {
     useRootSchema = random().nextBoolean();
     // schema15.xml declares _root_ field, while schema-rest.xml does not.
     String schema = useRootSchema ? "schema15.xml" : "schema-rest.xml";
-    initCore("solrconfig.xml", schema);
+    SolrTestCaseJ4.newRandomConfig();
+
+    solrClientTestRule.startSolr(Path.of(SolrTestCaseJ4.TEST_HOME()));
+
+    solrClientTestRule
+        .newCollection()
+        .withConfigSet("../collection1")
+        .withSchemaFile(schema)
+        .create();
   }
 
   @Test
diff --git 
a/solr/core/src/test/org/apache/solr/update/processor/AbstractAtomicUpdatesMultivalueTestBase.java
 
b/solr/core/src/test/org/apache/solr/update/processor/AbstractAtomicUpdatesMultivalueTestBase.java
index e461b68091a..9919126dc8c 100644
--- 
a/solr/core/src/test/org/apache/solr/update/processor/AbstractAtomicUpdatesMultivalueTestBase.java
+++ 
b/solr/core/src/test/org/apache/solr/update/processor/AbstractAtomicUpdatesMultivalueTestBase.java
@@ -16,11 +16,13 @@
  */
 package org.apache.solr.update.processor;
 
+import static org.apache.solr.SolrTestCaseJ4.sdoc;
 import static org.hamcrest.CoreMatchers.hasItems;
 import static org.hamcrest.CoreMatchers.not;
 
 import com.google.common.collect.ImmutableMap;
 import java.io.IOException;
+import java.nio.file.Paths;
 import java.time.ZonedDateTime;
 import java.util.Arrays;
 import java.util.Collection;
@@ -31,21 +33,25 @@ import java.util.UUID;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 import org.apache.solr.EmbeddedSolrServerTestBase;
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.client.solrj.SolrQuery;
 import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer;
 import 
org.apache.solr.client.solrj.embedded.EmbeddedSolrServer.RequestWriterSupplier;
+import org.apache.solr.client.solrj.response.QueryResponse;
 import org.apache.solr.common.util.ByteArrayUtf8CharSequence;
 import org.hamcrest.MatcherAssert;
 import org.junit.Before;
-import org.junit.BeforeClass;
 import org.junit.Test;
 
 public abstract class AbstractAtomicUpdatesMultivalueTestBase extends 
EmbeddedSolrServerTestBase {
 
-  @BeforeClass
-  public static void beforeClass() throws Exception {
+  protected static void initWithRequestWriter(RequestWriterSupplier 
requestWriterSupplier)
+      throws Exception {
     System.setProperty("enable.update.log", "true");
-    initCore("solrconfig.xml", "schema.xml");
+    SolrTestCaseJ4.newRandomConfig();
+    solrClientTestRule.startSolr(Paths.get(SolrTestCaseJ4.TEST_HOME()));
+
+    
solrClientTestRule.newCollection().withConfigSet("../collection1").create();
   }
 
   @Before
@@ -53,19 +59,12 @@ public abstract class 
AbstractAtomicUpdatesMultivalueTestBase extends EmbeddedSo
     getSolrClient().deleteByQuery("*:*");
   }
 
-  abstract RequestWriterSupplier getRequestWriterSupplier();
-
-  @Override
-  public synchronized EmbeddedSolrServer getSolrClient() {
-    return new EmbeddedSolrServer(
-        h.getCoreContainer(), DEFAULT_CORE_NAME, getRequestWriterSupplier());
-  }
+  private void assertQR(final String fieldName, final String queryValue, final 
int numFound)
+      throws SolrServerException, IOException {
 
-  private static void assertQR(
-      final String fieldName, final String queryValue, final int numFound) {
-    assertQ(
-        req("q", fieldName + ":" + queryValue, "indent", "true"),
-        "//result[@numFound = '" + numFound + "']");
+    SolrQuery query = new SolrQuery("q", fieldName + ":" + queryValue);
+    QueryResponse rsp = getSolrClient().query(query);
+    assertEquals(numFound, rsp.getResults().getNumFound());
   }
 
   private void runTestForField(
diff --git 
a/solr/core/src/test/org/apache/solr/update/processor/JavaBinAtomicUpdateMultivalueTest.java
 
b/solr/core/src/test/org/apache/solr/update/processor/JavaBinAtomicUpdateMultivalueTest.java
index 8d4ae2cf362..eb79d8b7436 100644
--- 
a/solr/core/src/test/org/apache/solr/update/processor/JavaBinAtomicUpdateMultivalueTest.java
+++ 
b/solr/core/src/test/org/apache/solr/update/processor/JavaBinAtomicUpdateMultivalueTest.java
@@ -17,11 +17,12 @@
 package org.apache.solr.update.processor;
 
 import 
org.apache.solr.client.solrj.embedded.EmbeddedSolrServer.RequestWriterSupplier;
+import org.junit.BeforeClass;
 
 public class JavaBinAtomicUpdateMultivalueTest extends 
AbstractAtomicUpdatesMultivalueTestBase {
 
-  @Override
-  RequestWriterSupplier getRequestWriterSupplier() {
-    return RequestWriterSupplier.JavaBin;
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    initWithRequestWriter(RequestWriterSupplier.JavaBin);
   }
 }
diff --git 
a/solr/core/src/test/org/apache/solr/update/processor/XMLAtomicUpdateMultivalueTest.java
 
b/solr/core/src/test/org/apache/solr/update/processor/XMLAtomicUpdateMultivalueTest.java
index cee43ad0ccd..6493f5d8254 100644
--- 
a/solr/core/src/test/org/apache/solr/update/processor/XMLAtomicUpdateMultivalueTest.java
+++ 
b/solr/core/src/test/org/apache/solr/update/processor/XMLAtomicUpdateMultivalueTest.java
@@ -17,11 +17,12 @@
 package org.apache.solr.update.processor;
 
 import 
org.apache.solr.client.solrj.embedded.EmbeddedSolrServer.RequestWriterSupplier;
+import org.junit.BeforeClass;
 
 public class XMLAtomicUpdateMultivalueTest extends 
AbstractAtomicUpdatesMultivalueTestBase {
 
-  @Override
-  RequestWriterSupplier getRequestWriterSupplier() {
-    return RequestWriterSupplier.XML;
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    initWithRequestWriter(RequestWriterSupplier.XML);
   }
 }
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/GetByIdTest.java 
b/solr/solrj/src/test/org/apache/solr/client/solrj/GetByIdTest.java
index 5e241afd888..1626dc2bbc8 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/GetByIdTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/GetByIdTest.java
@@ -16,12 +16,16 @@
  */
 package org.apache.solr.client.solrj;
 
+import static org.apache.solr.SolrTestCaseJ4.params;
+import static org.apache.solr.SolrTestCaseJ4.sdoc;
+
 import java.util.Arrays;
 import org.apache.solr.EmbeddedSolrServerTestBase;
 import org.apache.solr.common.SolrDocument;
 import org.apache.solr.common.SolrDocumentList;
 import org.apache.solr.common.params.CommonParams;
 import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.util.ExternalPaths;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -30,7 +34,9 @@ public class GetByIdTest extends EmbeddedSolrServerTestBase {
 
   @BeforeClass
   public static void beforeClass() throws Exception {
-    initCore();
+    solrClientTestRule.startSolr();
+
+    
solrClientTestRule.newCollection().withConfigSet(ExternalPaths.TECHPRODUCTS_CONFIGSET).create();
   }
 
   @Before
@@ -139,7 +145,9 @@ public class GetByIdTest extends EmbeddedSolrServerTestBase 
{
   @Test
   public void testGetIdsWithParams() throws Exception {
     SolrDocumentList rsp =
-        getSolrClient().getById(Arrays.asList("0", "1", "2"), 
params(CommonParams.FL, "id"));
+        solrClientTestRule
+            .getSolrClient()
+            .getById(Arrays.asList("0", "1", "2"), params(CommonParams.FL, 
"id"));
     assertEquals(2, rsp.getNumFound());
 
     assertEquals("1", rsp.get(0).get("id"));
diff --git 
a/solr/solrj/src/test/org/apache/solr/client/solrj/LargeVolumeTestBase.java 
b/solr/solrj/src/test/org/apache/solr/client/solrj/LargeVolumeTestBase.java
index 748353b28f0..77771c79e24 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/LargeVolumeTestBase.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/LargeVolumeTestBase.java
@@ -41,7 +41,7 @@ public abstract class LargeVolumeTestBase extends 
EmbeddedSolrServerTestBase {
 
   @Test
   public void testMultiThreaded() throws Exception {
-    SolrClient client = this.getSolrClient();
+    SolrClient client = getSolrClient();
     client.deleteByQuery("*:*"); // delete everything!
 
     DocThread[] threads = new DocThread[threadCount];
@@ -65,20 +65,20 @@ public abstract class LargeVolumeTestBase extends 
EmbeddedSolrServerTestBase {
   }
 
   private void query(int count) throws SolrServerException, IOException {
-    SolrClient client = this.getSolrClient();
+    SolrClient client = getSolrClient();
     SolrQuery query = new SolrQuery("*:*");
     QueryResponse response = client.query(query);
     assertEquals(0, response.getStatus());
     assertEquals(count, response.getResults().getNumFound());
   }
 
-  public class DocThread extends Thread {
+  public static class DocThread extends Thread {
 
     final SolrClient client;
     final String name;
 
     public DocThread(String name) {
-      client = createNewSolrClient();
+      client = getSolrClient();
       this.name = name;
     }
 
diff --git 
a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/AbstractEmbeddedSolrServerTestCase.java
 
b/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/AbstractEmbeddedSolrServerTestCase.java
index 7a46507c808..15027618384 100644
--- 
a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/AbstractEmbeddedSolrServerTestCase.java
+++ 
b/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/AbstractEmbeddedSolrServerTestCase.java
@@ -16,28 +16,37 @@
  */
 package org.apache.solr.client.solrj.embedded;
 
+import com.carrotsearch.randomizedtesting.rules.SystemPropertiesRestoreRule;
 import java.io.File;
+import java.io.IOException;
 import java.nio.file.Path;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.file.PathUtils;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.core.CoreContainer;
+import org.apache.solr.util.EmbeddedSolrServerTestRule;
 import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.rules.TestRule;
 
+@Deprecated
 public abstract class AbstractEmbeddedSolrServerTestCase extends 
SolrTestCaseJ4 {
 
   protected static Path SOLR_HOME;
   protected static Path CONFIG_HOME;
 
   protected CoreContainer cores = null;
-  protected File tempDir;
+
+  @Rule public EmbeddedSolrServerTestRule solrClientTestRule = new 
EmbeddedSolrServerTestRule();
+
+  @Rule public TestRule testRule = new SystemPropertiesRestoreRule();
 
   @BeforeClass
-  public static void setUpHome() throws Exception {
+  public static void setUpHome() throws IOException {
     CONFIG_HOME = getFile("solrj/solr/shared").toPath().toAbsolutePath();
     SOLR_HOME = createTempDir("solrHome");
     FileUtils.copyDirectory(CONFIG_HOME.toFile(), SOLR_HOME.toFile());
@@ -48,26 +57,23 @@ public abstract class AbstractEmbeddedSolrServerTestCase 
extends SolrTestCaseJ4
   public void setUp() throws Exception {
     super.setUp();
 
-    System.setProperty("solr.solr.home", SOLR_HOME.toString());
     System.setProperty(
         "configSetBaseDir", 
CONFIG_HOME.resolve("../configsets").normalize().toString());
-    System.out.println("Solr home: " + SOLR_HOME.toString());
+    System.setProperty("coreRootDirectory", "."); // relative to Solr home
 
     // The index is always stored within a temporary directory
-    tempDir = createTempDir().toFile();
+    File tempDir = createTempDir().toFile();
 
     File dataDir = new File(tempDir, "data1");
     File dataDir2 = new File(tempDir, "data2");
     System.setProperty("dataDir1", dataDir.getAbsolutePath());
     System.setProperty("dataDir2", dataDir2.getAbsolutePath());
     System.setProperty("tempDir", tempDir.getAbsolutePath());
-    System.setProperty("tests.shardhandler.randomSeed", 
Long.toString(random().nextLong()));
-    cores = CoreContainer.createAndLoad(SOLR_HOME, getSolrXml());
-    // cores.setPersistent(false);
-  }
+    SolrTestCaseJ4.newRandomConfig();
 
-  protected Path getSolrXml() throws Exception {
-    return SOLR_HOME.resolve("solr.xml");
+    solrClientTestRule.startSolr(SOLR_HOME);
+
+    cores = solrClientTestRule.getCoreContainer();
   }
 
   @Override
@@ -104,4 +110,8 @@ public abstract class AbstractEmbeddedSolrServerTestCase 
extends SolrTestCaseJ4
   protected SolrClient getSolrCore(String name) {
     return new EmbeddedSolrServer(cores, name);
   }
+
+  protected SolrClient getSolrAdmin() {
+    return solrClientTestRule.getAdminClient();
+  }
 }
diff --git 
a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/LargeVolumeBinaryJettyTest.java
 
b/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/LargeVolumeBinaryJettyTest.java
index 125d99d66f5..69f5bed712c 100644
--- 
a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/LargeVolumeBinaryJettyTest.java
+++ 
b/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/LargeVolumeBinaryJettyTest.java
@@ -17,6 +17,7 @@
 package org.apache.solr.client.solrj.embedded;
 
 import org.apache.solr.client.solrj.LargeVolumeTestBase;
+import org.apache.solr.util.ExternalPaths;
 import org.junit.BeforeClass;
 
 /**
@@ -26,6 +27,8 @@ import org.junit.BeforeClass;
 public class LargeVolumeBinaryJettyTest extends LargeVolumeTestBase {
   @BeforeClass
   public static void beforeTest() throws Exception {
-    initCore();
+    solrClientTestRule.startSolr();
+
+    
solrClientTestRule.newCollection().withConfigSet(ExternalPaths.TECHPRODUCTS_CONFIGSET).create();
   }
 }
diff --git 
a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/LargeVolumeEmbeddedTest.java
 
b/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/LargeVolumeEmbeddedTest.java
index 52c68a267d8..59d867bc17a 100644
--- 
a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/LargeVolumeEmbeddedTest.java
+++ 
b/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/LargeVolumeEmbeddedTest.java
@@ -17,11 +17,14 @@
 package org.apache.solr.client.solrj.embedded;
 
 import org.apache.solr.client.solrj.LargeVolumeTestBase;
+import org.apache.solr.util.ExternalPaths;
 import org.junit.BeforeClass;
 
 public class LargeVolumeEmbeddedTest extends LargeVolumeTestBase {
   @BeforeClass
   public static void beforeTest() throws Exception {
-    initCore();
+    solrClientTestRule.startSolr();
+
+    
solrClientTestRule.newCollection().withConfigSet(ExternalPaths.TECHPRODUCTS_CONFIGSET).create();
   }
 }
diff --git 
a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/LargeVolumeJettyTest.java
 
b/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/LargeVolumeJettyTest.java
index 32b926e6a91..dde40c8d6e3 100644
--- 
a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/LargeVolumeJettyTest.java
+++ 
b/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/LargeVolumeJettyTest.java
@@ -17,11 +17,15 @@
 package org.apache.solr.client.solrj.embedded;
 
 import org.apache.solr.client.solrj.LargeVolumeTestBase;
+import org.apache.solr.util.ExternalPaths;
 import org.junit.BeforeClass;
 
 public class LargeVolumeJettyTest extends LargeVolumeTestBase {
   @BeforeClass
   public static void beforeTest() throws Exception {
-    initCore();
+    // TODO
+    solrClientTestRule.startSolr();
+
+    
solrClientTestRule.newCollection().withConfigSet(ExternalPaths.TECHPRODUCTS_CONFIGSET).create();
   }
 }
diff --git 
a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/TestEmbeddedSolrServer.java
 
b/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/TestEmbeddedSolrServer.java
index 11415926d65..f23bb602a22 100644
--- 
a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/TestEmbeddedSolrServer.java
+++ 
b/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/TestEmbeddedSolrServer.java
@@ -16,44 +16,33 @@
  */
 package org.apache.solr.client.solrj.embedded;
 
-import java.io.IOException;
-import java.lang.invoke.MethodHandles;
-import java.util.ArrayList;
-import java.util.List;
-import org.apache.solr.core.SolrCore;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
 
-public class TestEmbeddedSolrServer extends AbstractEmbeddedSolrServerTestCase 
{
-  private static final Logger log = 
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+import java.io.IOException;
+import org.apache.solr.SolrTestCase;
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.core.CoreContainer;
+import org.junit.BeforeClass;
 
-  @Override
-  protected EmbeddedSolrServer getSolrCore1() {
-    return new EmbeddedSolrServer(cores, "core1");
-  }
+public class TestEmbeddedSolrServer extends SolrTestCase {
 
-  public void testGetCoreContainer() {
-    assertEquals(cores, ((EmbeddedSolrServer) 
getSolrCore0()).getCoreContainer());
-    assertEquals(cores, (getSolrCore1()).getCoreContainer());
+  @BeforeClass
+  public static void beforeClass() {
+    SolrTestCaseJ4.assumeWorkingMockito();
   }
 
   public void testClose() throws IOException {
-
-    EmbeddedSolrServer solrServer = (EmbeddedSolrServer) getSolrCore0();
-
-    assertEquals(3, cores.getCores().size());
-    List<SolrCore> solrCores = new ArrayList<>();
-    for (SolrCore solrCore : cores.getCores()) {
-      assertFalse(solrCore.isClosed());
-      solrCores.add(solrCore);
-    }
-
+    // create a CoreContainer first then pass to EmbeddedSolrServer.
+    CoreContainer cores = mock(CoreContainer.class);
+    EmbeddedSolrServer solrServer = new EmbeddedSolrServer(cores, null);
     solrServer.close();
 
-    assertEquals(3, cores.getCores().size());
+    verify(cores, never()).shutdown();
 
-    for (SolrCore solrCore : solrCores) {
-      assertFalse(solrCore.isClosed());
-    }
+    // We could test the reverse, that if EmbeddedSolrServer is created 
without a
+    // CoreContainer passed in that it propagates the shutdown, but honestly 
tons of
+    // tests would fail, so we're covered.
   }
 }
diff --git 
a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/TestSolrProperties.java
 
b/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/TestSolrProperties.java
index 77a081cb602..b11458efe85 100644
--- 
a/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/TestSolrProperties.java
+++ 
b/solr/solrj/src/test/org/apache/solr/client/solrj/embedded/TestSolrProperties.java
@@ -16,7 +16,6 @@
  */
 package org.apache.solr.client.solrj.embedded;
 
-import java.lang.invoke.MethodHandles;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrQuery;
@@ -27,18 +26,9 @@ import org.apache.solr.client.solrj.request.UpdateRequest;
 import org.apache.solr.client.solrj.response.CoreAdminResponse;
 import org.apache.solr.common.SolrInputDocument;
 import org.junit.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
-/**
- * @since solr 1.3
- */
+/** Test properties in configuration files. */
 public class TestSolrProperties extends AbstractEmbeddedSolrServerTestCase {
-  private static final Logger log = 
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
-
-  protected SolrClient getSolrAdmin() {
-    return new EmbeddedSolrServer(cores, null);
-  }
 
   @Test
   public void testProperties() throws Exception {
diff --git 
a/solr/solrj/src/test/org/apache/solr/client/solrj/request/SolrPingTest.java 
b/solr/solrj/src/test/org/apache/solr/client/solrj/request/SolrPingTest.java
index 40fdf457279..d00e6fc404e 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/request/SolrPingTest.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/request/SolrPingTest.java
@@ -16,9 +16,8 @@
  */
 package org.apache.solr.client.solrj.request;
 
-import java.io.File;
-import org.apache.commons.io.FileUtils;
 import org.apache.solr.EmbeddedSolrServerTestBase;
+import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.client.solrj.response.SolrPingResponse;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.SolrInputDocument;
@@ -31,18 +30,18 @@ public class SolrPingTest extends 
EmbeddedSolrServerTestBase {
 
   @BeforeClass
   public static void beforeClass() throws Exception {
-    File testHome = createTempDir().toFile();
-    FileUtils.copyDirectory(getFile("solrj/solr"), testHome);
-    initCore("solrconfig.xml", "schema.xml", testHome.getAbsolutePath(), 
"collection1");
+    
solrClientTestRule.startSolr(SolrTestCaseJ4.getFile("solrj/solr").toPath());
+
+    SolrTestCaseJ4.newRandomConfig();
+    
solrClientTestRule.newCollection().withConfigSet("../collection1").create();
   }
 
   @Before
   @Override
   public void setUp() throws Exception {
     super.setUp();
-    clearIndex();
-    assertU(commit());
-    assertU(optimize());
+    solrClientTestRule.clearIndex();
+
     SolrInputDocument doc = new SolrInputDocument();
     doc.setField("id", 1);
     doc.setField("terms_s", "samsung");
diff --git 
a/solr/solrj/src/test/org/apache/solr/client/solrj/request/TestCoreAdmin.java 
b/solr/solrj/src/test/org/apache/solr/client/solrj/request/TestCoreAdmin.java
index 87256553395..de7ecfefad9 100644
--- 
a/solr/solrj/src/test/org/apache/solr/client/solrj/request/TestCoreAdmin.java
+++ 
b/solr/solrj/src/test/org/apache/solr/client/solrj/request/TestCoreAdmin.java
@@ -19,7 +19,6 @@ package org.apache.solr.client.solrj.request;
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.core.Is.is;
 
-import com.carrotsearch.randomizedtesting.rules.SystemPropertiesRestoreRule;
 import com.codahale.metrics.MetricRegistry;
 import java.io.File;
 import java.io.IOException;
@@ -33,7 +32,6 @@ import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrQuery;
 import org.apache.solr.client.solrj.SolrServerException;
 import 
org.apache.solr.client.solrj.embedded.AbstractEmbeddedSolrServerTestCase;
-import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer;
 import org.apache.solr.client.solrj.request.CoreAdminRequest.Create;
 import org.apache.solr.client.solrj.request.CoreAdminRequest.RequestRecovery;
 import org.apache.solr.client.solrj.response.CoreAdminResponse;
@@ -47,36 +45,10 @@ import org.apache.solr.core.SolrCore;
 import org.apache.solr.metrics.SolrCoreMetricManager;
 import org.apache.solr.metrics.SolrMetricManager;
 import org.hamcrest.MatcherAssert;
-import org.junit.After;
-import org.junit.BeforeClass;
-import org.junit.Rule;
 import org.junit.Test;
-import org.junit.rules.RuleChain;
-import org.junit.rules.TestRule;
 
 public class TestCoreAdmin extends AbstractEmbeddedSolrServerTestCase {
 
-  private static String tempDirProp;
-
-  @Rule public TestRule testRule = RuleChain.outerRule(new 
SystemPropertiesRestoreRule());
-
-  /*
-  @Override
-  protected File getSolrXml() throws Exception {
-    // This test writes on the directory where the solr.xml is located. Better
-    // to copy the solr.xml to
-    // the temporary directory where we store the index
-    File origSolrXml = new File(SOLR_HOME, SOLR_XML);
-    File solrXml = new File(tempDir, SOLR_XML);
-    FileUtils.copyFile(origSolrXml, solrXml);
-    return solrXml;
-  }
-  */
-
-  protected SolrClient getSolrAdmin() {
-    return new EmbeddedSolrServer(cores, null);
-  }
-
   @Test
   public void testConfigSet() throws Exception {
 
@@ -291,7 +263,7 @@ public class TestCoreAdmin extends 
AbstractEmbeddedSolrServerTestCase {
     useFactory(null); // use FS factory
 
     try {
-      cores = CoreContainer.createAndLoad(SOLR_HOME, getSolrXml());
+      cores = CoreContainer.createAndLoad(SOLR_HOME);
 
       String ddir = CoreAdminRequest.getCoreStatus("core0", 
getSolrCore0()).getDataDirectory();
       Path data = Paths.get(ddir, "index");
@@ -304,7 +276,7 @@ public class TestCoreAdmin extends 
AbstractEmbeddedSolrServerTestCase {
 
       // destroy the index
       Files.move(data.resolve("_0.si"), data.resolve("backup"));
-      cores = CoreContainer.createAndLoad(SOLR_HOME, getSolrXml());
+      cores = CoreContainer.createAndLoad(SOLR_HOME);
 
       // Need to run a query to confirm that the core couldn't load
       expectThrows(SolrException.class, () -> getSolrCore0().query(new 
SolrQuery("*:*")));
@@ -316,25 +288,8 @@ public class TestCoreAdmin extends 
AbstractEmbeddedSolrServerTestCase {
       CoreAdminRequest.reloadCore("core0", getSolrCore0());
       assertEquals(1, getSolrCore0().query(new 
SolrQuery("*:*")).getResults().getNumFound());
     } finally {
+      cores.shutdown();
       resetFactory();
     }
   }
-
-  @BeforeClass
-  public static void before() {
-    // wtf?
-    if (System.getProperty("tempDir") != null) tempDirProp = 
System.getProperty("tempDir");
-  }
-
-  @After
-  public void after() {
-    // wtf?
-    if (tempDirProp != null) {
-      System.setProperty("tempDir", tempDirProp);
-    } else {
-      System.clearProperty("tempDir");
-    }
-
-    System.clearProperty("solr.solr.home");
-  }
 }
diff --git 
a/solr/solrj/src/test/org/apache/solr/client/solrj/request/json/DirectJsonQueryRequestFacetingEmbeddedTest.java
 
b/solr/solrj/src/test/org/apache/solr/client/solrj/request/json/DirectJsonQueryRequestFacetingEmbeddedTest.java
index eebd3e6c5ae..8103dda8e19 100644
--- 
a/solr/solrj/src/test/org/apache/solr/client/solrj/request/json/DirectJsonQueryRequestFacetingEmbeddedTest.java
+++ 
b/solr/solrj/src/test/org/apache/solr/client/solrj/request/json/DirectJsonQueryRequestFacetingEmbeddedTest.java
@@ -17,16 +17,13 @@
 
 package org.apache.solr.client.solrj.request.json;
 
-import java.io.File;
-import java.io.OutputStreamWriter;
-import java.io.Writer;
+import static org.apache.solr.SolrTestCaseJ4.getFile;
+
 import java.util.List;
-import java.util.Properties;
-import org.apache.commons.io.FileUtils;
 import org.apache.lucene.tests.util.LuceneTestCase;
 import org.apache.solr.EmbeddedSolrServerTestBase;
 import org.apache.solr.SolrTestCaseJ4.SuppressSSL;
-import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer;
+import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.request.AbstractUpdateRequest;
 import org.apache.solr.client.solrj.request.ContentStreamUpdateRequest;
 import org.apache.solr.client.solrj.response.QueryResponse;
@@ -41,7 +38,7 @@ import org.junit.Test;
 @SuppressSSL
 public class DirectJsonQueryRequestFacetingEmbeddedTest extends 
EmbeddedSolrServerTestBase {
 
-  private static final String COLLECTION_NAME = "techproducts";
+  private static final String COLLECTION_NAME = "collection1";
   private static final int NUM_TECHPRODUCTS_DOCS = 32;
   private static final int NUM_IN_STOCK = 17;
   private static final int NUM_ELECTRONICS = 12;
@@ -53,32 +50,15 @@ public class DirectJsonQueryRequestFacetingEmbeddedTest 
extends EmbeddedSolrServ
 
   @BeforeClass
   public static void beforeClass() throws Exception {
-    final String sourceHome = ExternalPaths.SOURCE_HOME;
-
-    final File tempSolrHome = LuceneTestCase.createTempDir().toFile();
-    FileUtils.copyFileToDirectory(new File(sourceHome, 
"server/solr/solr.xml"), tempSolrHome);
-    final File collectionDir = new File(tempSolrHome, COLLECTION_NAME);
-    FileUtils.forceMkdir(collectionDir);
-    final File configSetDir =
-        new File(sourceHome, 
"server/solr/configsets/sample_techproducts_configs/conf");
-    FileUtils.copyDirectoryToDirectory(configSetDir, collectionDir);
-
-    final Properties props = new Properties();
-    props.setProperty("name", COLLECTION_NAME);
-
-    try (Writer writer =
-        new OutputStreamWriter(
-            FileUtils.openOutputStream(new File(collectionDir, 
"core.properties")), "UTF-8"); ) {
-      props.store(writer, null);
-    }
 
-    final String config =
-        tempSolrHome.getAbsolutePath() + "/" + COLLECTION_NAME + 
"/conf/solrconfig.xml";
-    final String schema =
-        tempSolrHome.getAbsolutePath() + "/" + COLLECTION_NAME + 
"/conf/managed-schema";
-    initCore(config, schema, tempSolrHome.getAbsolutePath(), COLLECTION_NAME);
+    solrClientTestRule.startSolr(LuceneTestCase.createTempDir());
+
+    solrClientTestRule
+        .newCollection(COLLECTION_NAME)
+        .withConfigSet(ExternalPaths.TECHPRODUCTS_CONFIGSET)
+        .create();
 
-    client = new EmbeddedSolrServer(h.getCoreContainer(), COLLECTION_NAME);
+    SolrClient client = solrClientTestRule.getSolrClient(COLLECTION_NAME);
 
     ContentStreamUpdateRequest up = new ContentStreamUpdateRequest("/update");
     up.setParam("collection", COLLECTION_NAME);
diff --git 
a/solr/solrj/src/test/org/apache/solr/client/solrj/response/TermsResponseTest.java
 
b/solr/solrj/src/test/org/apache/solr/client/solrj/response/TermsResponseTest.java
index 31441fa412d..617a72b3bb8 100644
--- 
a/solr/solrj/src/test/org/apache/solr/client/solrj/response/TermsResponseTest.java
+++ 
b/solr/solrj/src/test/org/apache/solr/client/solrj/response/TermsResponseTest.java
@@ -22,6 +22,7 @@ import org.apache.solr.client.solrj.SolrQuery;
 import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.client.solrj.response.TermsResponse.Term;
 import org.apache.solr.common.SolrInputDocument;
+import org.apache.solr.util.ExternalPaths;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -31,16 +32,16 @@ public class TermsResponseTest extends 
EmbeddedSolrServerTestBase {
 
   @BeforeClass
   public static void beforeClass() throws Exception {
-    initCore();
+    solrClientTestRule.startSolr();
+
+    
solrClientTestRule.newCollection().withConfigSet(ExternalPaths.TECHPRODUCTS_CONFIGSET).create();
   }
 
   @Before
   @Override
   public void setUp() throws Exception {
     super.setUp();
-    clearIndex();
-    assertU(commit());
-    assertU(optimize());
+    solrClientTestRule.clearIndex();
   }
 
   @Test
diff --git 
a/solr/solrj/src/test/org/apache/solr/client/solrj/response/TestSpellCheckResponse.java
 
b/solr/solrj/src/test/org/apache/solr/client/solrj/response/TestSpellCheckResponse.java
index 3aa536a1386..319905717e1 100644
--- 
a/solr/solrj/src/test/org/apache/solr/client/solrj/response/TestSpellCheckResponse.java
+++ 
b/solr/solrj/src/test/org/apache/solr/client/solrj/response/TestSpellCheckResponse.java
@@ -18,6 +18,7 @@ package org.apache.solr.client.solrj.response;
 
 import java.util.List;
 import org.apache.solr.EmbeddedSolrServerTestBase;
+import org.apache.solr.client.solrj.SolrClient;
 import org.apache.solr.client.solrj.SolrQuery;
 import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.client.solrj.response.SpellCheckResponse.Collation;
@@ -25,6 +26,8 @@ import 
org.apache.solr.client.solrj.response.SpellCheckResponse.Correction;
 import org.apache.solr.common.SolrInputDocument;
 import org.apache.solr.common.params.CommonParams;
 import org.apache.solr.common.params.SpellingParams;
+import org.apache.solr.util.ExternalPaths;
+import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
@@ -34,17 +37,26 @@ import org.junit.Test;
  * @since solr 1.3
  */
 public class TestSpellCheckResponse extends EmbeddedSolrServerTestBase {
+  private static SolrClient client;
 
   @BeforeClass
   public static void beforeClass() throws Exception {
-    initCore();
+    solrClientTestRule.startSolr();
+
+    
solrClientTestRule.newCollection().withConfigSet(ExternalPaths.TECHPRODUCTS_CONFIGSET).create();
+
+    client = getSolrClient();
+  }
+
+  @AfterClass
+  public static void afterClass() {
+    client = null;
   }
 
   static String field = "name";
 
   @Test
   public void testSpellCheckResponse() throws Exception {
-    getSolrClient();
     client.deleteByQuery("*:*");
     client.commit(true, true);
     SolrInputDocument doc = new SolrInputDocument();
@@ -64,7 +76,6 @@ public class TestSpellCheckResponse extends 
EmbeddedSolrServerTestBase {
 
   @Test
   public void testSpellCheckResponse_Extended() throws Exception {
-    getSolrClient();
     client.deleteByQuery("*:*");
     client.commit(true, true);
     SolrInputDocument doc = new SolrInputDocument();
@@ -104,7 +115,7 @@ public class TestSpellCheckResponse extends 
EmbeddedSolrServerTestBase {
 
   @Test
   public void testSpellCheckCollationResponse() throws Exception {
-    getSolrClient();
+
     client.deleteByQuery("*:*");
     client.commit(true, true);
     SolrInputDocument doc = new SolrInputDocument();
diff --git 
a/solr/test-framework/src/java/org/apache/solr/EmbeddedSolrServerTestBase.java 
b/solr/test-framework/src/java/org/apache/solr/EmbeddedSolrServerTestBase.java
index 458e3ca6ddf..3eb73eb8878 100644
--- 
a/solr/test-framework/src/java/org/apache/solr/EmbeddedSolrServerTestBase.java
+++ 
b/solr/test-framework/src/java/org/apache/solr/EmbeddedSolrServerTestBase.java
@@ -16,104 +16,17 @@
  */
 package org.apache.solr;
 
-import com.google.common.io.ByteStreams;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.UncheckedIOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer;
-import org.apache.solr.common.util.ContentStream;
-import org.apache.solr.common.util.ContentStreamBase;
-import org.apache.solr.common.util.ContentStreamBase.ByteArrayStream;
-import org.junit.After;
-import org.junit.AfterClass;
+import org.apache.solr.client.solrj.SolrClient;
+import org.apache.solr.util.EmbeddedSolrServerTestRule;
+import org.junit.ClassRule;
 
-public abstract class EmbeddedSolrServerTestBase extends SolrTestCaseJ4 {
+@Deprecated // simply use EmbeddedSolrServerTestRule
+public abstract class EmbeddedSolrServerTestBase extends SolrTestCase {
 
-  protected static final String DEFAULT_CORE_NAME = "collection1";
+  @ClassRule
+  public static EmbeddedSolrServerTestRule solrClientTestRule = new 
EmbeddedSolrServerTestRule();
 
-  public static EmbeddedSolrServer client = null;
-
-  @After
-  public synchronized void afterClass() throws Exception {
-    if (client != null) client.close();
-    client = null;
-  }
-
-  @AfterClass
-  public static void afterEmbeddedSolrServerTestBase() throws Exception {}
-
-  public synchronized EmbeddedSolrServer getSolrClient() {
-    if (client == null) {
-      client = createNewSolrClient();
-    }
-    return client;
-  }
-
-  /** Create a new solr client. Subclasses should override for other options. 
*/
-  public EmbeddedSolrServer createNewSolrClient() {
-    return new EmbeddedSolrServer(h.getCoreContainer(), DEFAULT_CORE_NAME);
-  }
-
-  public void upload(final String collection, final ContentStream... contents) 
{
-    final Path base = 
Paths.get(getSolrClient().getCoreContainer().getSolrHome(), collection);
-    writeTo(base, contents);
-  }
-
-  private void writeTo(final Path base, final ContentStream... contents) {
-    try {
-      Files.createDirectories(base);
-
-      for (final ContentStream content : contents) {
-        final File file = new File(base.toFile(), content.getName());
-        file.getParentFile().mkdirs();
-
-        try (OutputStream os = new FileOutputStream(file)) {
-          ByteStreams.copy(content.getStream(), os);
-        }
-      }
-    } catch (final IOException e) {
-      throw new UncheckedIOException(e);
-    }
-  }
-
-  public Collection<ContentStream> download(final String collection, final 
String... names) {
-    final Path base = 
Paths.get(getSolrClient().getCoreContainer().getSolrHome(), collection);
-    final List<ContentStream> result = new ArrayList<>();
-
-    if (Files.exists(base)) {
-      for (final String name : names) {
-        final File file = new File(base.toFile(), name);
-        if (file.exists() && file.canRead()) {
-          try {
-            final ByteArrayOutputStream os = new ByteArrayOutputStream();
-            ByteStreams.copy(new FileInputStream(file), os);
-            final ByteArrayStream stream =
-                new ContentStreamBase.ByteArrayStream(os.toByteArray(), name);
-            result.add(stream);
-          } catch (final IOException e) {
-            throw new RuntimeException(e);
-          }
-        }
-      }
-    }
-
-    return result;
-  }
-
-  public static void initCore() throws Exception {
-    final String home = SolrJettyTestBase.legacyExampleCollection1SolrHome();
-    final String config = home + "/" + DEFAULT_CORE_NAME + 
"/conf/solrconfig.xml";
-    final String schema = home + "/" + DEFAULT_CORE_NAME + "/conf/schema.xml";
-    initCore(config, schema, home);
+  public static SolrClient getSolrClient() {
+    return solrClientTestRule.getSolrClient();
   }
 }
diff --git a/solr/test-framework/src/java/org/apache/solr/SolrTestCase.java 
b/solr/test-framework/src/java/org/apache/solr/SolrTestCase.java
index 15ea14f58ce..3858f8cb959 100644
--- a/solr/test-framework/src/java/org/apache/solr/SolrTestCase.java
+++ b/solr/test-framework/src/java/org/apache/solr/SolrTestCase.java
@@ -21,11 +21,14 @@ import static 
com.carrotsearch.randomizedtesting.RandomizedTest.systemPropertyAs
 
 import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters;
 import com.carrotsearch.randomizedtesting.annotations.ThreadLeakLingering;
+import com.carrotsearch.randomizedtesting.rules.StatementAdapter;
 import com.carrotsearch.randomizedtesting.rules.SystemPropertiesRestoreRule;
 import java.io.File;
 import java.lang.invoke.MethodHandles;
+import java.util.List;
 import java.util.regex.Pattern;
 import org.apache.lucene.tests.util.LuceneTestCase;
+import org.apache.lucene.tests.util.LuceneTestCase.SuppressSysoutChecks;
 import org.apache.lucene.tests.util.QuickPatchThreadsFilter;
 import org.apache.lucene.tests.util.VerifyTestClassNamingConvention;
 import org.apache.solr.common.util.ObjectReleaseTracker;
@@ -33,7 +36,6 @@ import org.apache.solr.servlet.SolrDispatchFilter;
 import org.apache.solr.util.ExternalPaths;
 import org.apache.solr.util.RevertDefaultThreadHandlerRule;
 import org.apache.solr.util.StartupLoggingUtils;
-import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.ClassRule;
@@ -64,6 +66,7 @@ import org.slf4j.LoggerFactory;
 // on slow machines it could take up to 1s. See discussion on SOLR-15660
 // and SOLR-16187 regarding why this is necessary.
 @ThreadLeakLingering(linger = 1000)
+@SuppressSysoutChecks(bugUrl = "Solr dumps tons of logs to console.")
 public class SolrTestCase extends LuceneTestCase {
 
   private static final Logger log = 
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@@ -83,7 +86,25 @@ public class SolrTestCase extends LuceneTestCase {
           .around(
               new VerifyTestClassNamingConvention(
                   "org.apache.solr.ltr", NAMING_CONVENTION_TEST_PREFIX))
-          .around(new RevertDefaultThreadHandlerRule());
+          .around(new RevertDefaultThreadHandlerRule())
+          .around(
+              (base, description) ->
+                  new StatementAdapter(base) {
+                    @Override
+                    protected void afterIfSuccessful() {
+                      // if the tests passed, make sure everything was closed 
/ released
+                      String orr = 
ObjectReleaseTracker.clearObjectTrackerAndCheckEmpty();
+                      assertNull(orr, orr);
+                    }
+
+                    @Override
+                    protected void afterAlways(List<Throwable> errors) {
+                      if (!errors.isEmpty()) {
+                        ObjectReleaseTracker.tryClose();
+                      }
+                      StartupLoggingUtils.shutdown();
+                    }
+                  });
 
   /**
    * Sets the <code>solr.default.confdir</code> system property to the value 
of {@link
@@ -162,16 +183,4 @@ public class SolrTestCase extends LuceneTestCase {
     final String PROP = "tests.force.assumption.failure.before";
     assumeFalse(PROP + " == true", systemPropertyAsBoolean(PROP, false));
   }
-
-  @AfterClass
-  public static void afterSolrTestCase() throws Exception {
-    if (suiteFailureMarker.wasSuccessful()) {
-      // if the tests passed, make sure everything was closed / released
-      String orr = ObjectReleaseTracker.clearObjectTrackerAndCheckEmpty();
-      assertNull(orr, orr);
-    } else {
-      ObjectReleaseTracker.tryClose();
-    }
-    StartupLoggingUtils.shutdown();
-  }
 }
diff --git a/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java 
b/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java
index a2c430f11a2..b56ece570e9 100644
--- a/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java
+++ b/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java
@@ -88,7 +88,6 @@ import org.apache.lucene.index.IndexWriterConfig;
 import org.apache.lucene.tests.analysis.MockAnalyzer;
 import org.apache.lucene.tests.analysis.MockTokenizer;
 import org.apache.lucene.tests.util.LuceneTestCase.SuppressFileSystems;
-import org.apache.lucene.tests.util.LuceneTestCase.SuppressSysoutChecks;
 import org.apache.lucene.tests.util.TestUtil;
 import org.apache.lucene.util.Constants;
 import org.apache.solr.client.solrj.ResponseParser;
@@ -178,7 +177,6 @@ import org.xml.sax.SAXException;
  * which core is used when loading the schema and solrconfig.xml, simply 
invoke the {@link
  * #initCore(String, String, String, String)} method.
  */
-@SuppressSysoutChecks(bugUrl = "Solr dumps tons of logs to console.")
 // ExtrasFS might be ok, the failures with e.g. nightly runs might be "normal"
 @SuppressFileSystems("ExtrasFS")
 @RandomizeSSL()
diff --git 
a/solr/test-framework/src/java/org/apache/solr/util/EmbeddedSolrServerTestRule.java
 
b/solr/test-framework/src/java/org/apache/solr/util/EmbeddedSolrServerTestRule.java
new file mode 100644
index 00000000000..92efc3e9445
--- /dev/null
+++ 
b/solr/test-framework/src/java/org/apache/solr/util/EmbeddedSolrServerTestRule.java
@@ -0,0 +1,110 @@
+/*
+ * 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.util;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Properties;
+import org.apache.lucene.tests.util.LuceneTestCase;
+import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer;
+import org.apache.solr.core.CoreContainer;
+import org.apache.solr.core.NodeConfig;
+import org.apache.solr.core.SolrXmlConfig;
+import org.apache.solr.update.UpdateShardHandlerConfig;
+
+/**
+ * Provides an EmbeddedSolrServer for tests. It starts and stops the server 
and provides methods for
+ * creating collections and interacting with the server.
+ */
+public class EmbeddedSolrServerTestRule extends SolrClientTestRule {
+
+  private static final String CORE_DIR_PROP = "coreRootDirectory";
+  private EmbeddedSolrServer adminClient = null;
+
+  /**
+   * Shuts down the EmbeddedSolrServer instance and clears the 
coreRootDirectory system property if
+   * necessary
+   */
+  @Override
+  protected void after() {
+    if (adminClient != null) adminClient.getCoreContainer().shutdown();
+  }
+
+  /**
+   * Starts the Solr server with the given solrHome. If solrHome contains a 
solr.xml file, it is
+   * used to configure the server. If not, a new NodeConfig is built with 
default settings for
+   * configuration.
+   */
+  @Override
+  public void startSolr(Path solrHome) {
+    NodeConfig nodeConfig;
+    if (Files.exists(solrHome.resolve(SolrXmlConfig.SOLR_XML_FILE))) {
+      // existing solr.xml; perhaps not recommended for new/most tests
+
+      // solr.xml coreRootDirectory is best set to a temp directory in a test 
so that
+      //  (a) we don't load existing cores
+      //      Because it's better for tests to explicitly create cores.
+      //  (b) we don't write data in the test to a likely template directory
+      //  But a test can insist on something if it sets the property.
+
+      Properties props = new Properties();
+      if (System.getProperty(CORE_DIR_PROP) == null) {
+        props.setProperty(CORE_DIR_PROP, 
LuceneTestCase.createTempDir("cores").toString());
+      }
+
+      nodeConfig = SolrXmlConfig.fromSolrHome(solrHome, props);
+    } else {
+      // test oriented config (preferred)
+      nodeConfig = newNodeConfigBuilder(solrHome).build();
+    }
+
+    startSolr(nodeConfig);
+  }
+
+  /** Starts Solr with custom NodeConfig */
+  public void startSolr(NodeConfig nodeConfig) {
+    var container = new CoreContainer(nodeConfig);
+    adminClient = new EmbeddedSolrServer(container, null);
+    container.load(); // do after setting adminClient so that after() can 
shutdown the container
+  }
+
+  /** Returns a NodeConfigBuilder with default settings for test configuration 
*/
+  public NodeConfig.NodeConfigBuilder newNodeConfigBuilder(Path solrHome) {
+
+    return new NodeConfig.NodeConfigBuilder("testNode", solrHome)
+        .setUpdateShardHandlerConfig(UpdateShardHandlerConfig.TEST_DEFAULT)
+        
.setCoreRootDirectory(LuceneTestCase.createTempDir("cores").toString());
+  }
+
+  /** Provides an EmbeddedSolrServer instance for administration actions */
+  @Override
+  public EmbeddedSolrServer getAdminClient() {
+    if (adminClient == null) {
+      throw new RuntimeException("Solr must be started first");
+    }
+    return adminClient;
+  }
+
+  @Override
+  public EmbeddedSolrServer getSolrClient(String name) {
+    return new EmbeddedSolrServer(getCoreContainer(), name);
+  }
+
+  public CoreContainer getCoreContainer() {
+    return getAdminClient().getCoreContainer();
+  }
+}
diff --git 
a/solr/test-framework/src/java/org/apache/solr/util/SolrClientTestRule.java 
b/solr/test-framework/src/java/org/apache/solr/util/SolrClientTestRule.java
new file mode 100644
index 00000000000..a3c9f4c9ea1
--- /dev/null
+++ b/solr/test-framework/src/java/org/apache/solr/util/SolrClientTestRule.java
@@ -0,0 +1,147 @@
+/*
+ * 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.util;
+
+import static org.apache.solr.SolrTestCaseJ4.DEFAULT_TEST_COLLECTION_NAME;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import org.apache.lucene.tests.util.LuceneTestCase;
+import org.apache.solr.client.solrj.SolrClient;
+import org.apache.solr.client.solrj.SolrServerException;
+import org.apache.solr.client.solrj.request.CoreAdminRequest;
+import org.apache.solr.client.solrj.request.UpdateRequest;
+import org.junit.rules.ExternalResource;
+
+/**
+ * Provides access to a {@link SolrClient} instance and a running Solr in 
tests. Implementations
+ * could run Solr in different ways (e.g. strictly embedded, adding 
HTTP/Jetty, adding SolrCloud, or
+ * an external process). It's a JUnit {@link ExternalResource} (a {@code 
TestRule}), and thus closes
+ * the client and Solr itself when the test completes. This test utility is 
encouraged to be used by
+ * external projects that wish to test communicating with Solr, especially for 
plugin providers.
+ */
+public abstract class SolrClientTestRule extends ExternalResource {
+
+  /** Starts the Solr server with empty solrHome. */
+  public void startSolr() {
+    startSolr(LuceneTestCase.createTempDir("solrhome"));
+  }
+
+  /**
+   * Starts the Solr server with the given solrHome. If solrHome contains a 
solr.xml file, it is
+   * used. Otherwise a default testing configuration is used.
+   */
+  public abstract void startSolr(Path solrHome);
+
+  public NewCollectionBuilder newCollection(String name) {
+    return new NewCollectionBuilder(name);
+  }
+
+  public NewCollectionBuilder newCollection() {
+    return new NewCollectionBuilder(DEFAULT_TEST_COLLECTION_NAME);
+  }
+
+  public class NewCollectionBuilder {
+    private String name;
+    private String configSet;
+    private String configFile;
+    private String schemaFile;
+
+    public NewCollectionBuilder(String name) {
+      this.name = name;
+    }
+
+    public NewCollectionBuilder withConfigSet(String configSet) {
+      // Chop off "/conf" if found -- configSet can be a path.
+      // This is a hack so that we can continue to use 
ExternalPaths.DEFAULT_CONFIGSET etc. as-is.
+      // Without this, managed resources might be written to
+      // conf/conf/_schema_analysis_stopwords_english.json because 
SolrResourceLoader points to the
+      // wrong dir.
+      if (configSet != null && configSet.endsWith("/conf")) {
+        configSet = configSet.substring(0, configSet.length() - 
"/conf".length());
+      }
+
+      this.configSet = configSet;
+      return this;
+    }
+
+    public NewCollectionBuilder withConfigFile(String configFile) {
+      this.configFile = configFile;
+      return this;
+    }
+
+    public NewCollectionBuilder withSchemaFile(String schemaFile) {
+      this.schemaFile = schemaFile;
+      return this;
+    }
+
+    public String getName() {
+      return name;
+    }
+
+    public String getConfigSet() {
+      return configSet;
+    }
+
+    public String getConfigFile() {
+      return configFile;
+    }
+
+    public String getSchemaFile() {
+      return schemaFile;
+    }
+
+    public void create() throws SolrServerException, IOException {
+      SolrClientTestRule.this.create(this);
+    }
+  }
+
+  protected void create(NewCollectionBuilder b) throws SolrServerException, 
IOException {
+
+    CoreAdminRequest.Create req = new CoreAdminRequest.Create();
+    req.setCoreName(b.getName());
+    req.setInstanceDir(b.getName());
+
+    if (b.getConfigSet() != null) {
+      req.setConfigSet(b.getConfigSet());
+    }
+
+    if (b.getConfigFile() != null) {
+      req.setConfigName(b.getConfigFile());
+    }
+
+    if (b.getSchemaFile() != null) {
+      req.setSchemaName(b.getSchemaFile());
+    }
+
+    req.process(getAdminClient());
+  }
+
+  /** Provides a SolrClient instance for administration actions */
+  public abstract SolrClient getAdminClient();
+
+  /** Provides a SolrClient instance for collection1 */
+  public SolrClient getSolrClient() {
+    return getSolrClient("collection1");
+  }
+
+  public abstract SolrClient getSolrClient(String name);
+
+  public void clearIndex() throws SolrServerException, IOException {
+    new UpdateRequest().deleteByQuery("*:*").commit(getSolrClient(), null);
+  }
+}
diff --git a/solr/test-framework/src/java/org/apache/solr/util/TestHarness.java 
b/solr/test-framework/src/java/org/apache/solr/util/TestHarness.java
index b4828bad1ca..6a9eeca24eb 100644
--- a/solr/test-framework/src/java/org/apache/solr/util/TestHarness.java
+++ b/solr/test-framework/src/java/org/apache/solr/util/TestHarness.java
@@ -27,7 +27,6 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import org.apache.solr.SolrTestCaseJ4;
-import org.apache.solr.client.solrj.impl.HttpClientUtil;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.params.CommonParams;
 import org.apache.solr.common.util.NamedList;
@@ -197,14 +196,7 @@ public class TestHarness extends BaseTestHarness {
                 .setZkClientTimeout(Integer.getInteger("zkClientTimeout", 
30000))
                 .setZkHost(System.getProperty("zkHost"))
                 .build();
-    UpdateShardHandlerConfig updateShardHandlerConfig =
-        new UpdateShardHandlerConfig(
-            HttpClientUtil.DEFAULT_MAXCONNECTIONS,
-            HttpClientUtil.DEFAULT_MAXCONNECTIONSPERHOST,
-            30000,
-            30000,
-            UpdateShardHandlerConfig.DEFAULT_METRICNAMESTRATEGY,
-            UpdateShardHandlerConfig.DEFAULT_MAXRECOVERYTHREADS);
+
     // universal default metric reporter
     Map<String, Object> attributes = new HashMap<>();
     attributes.put("name", "default");
@@ -218,7 +210,7 @@ public class TestHarness extends BaseTestHarness {
     return new NodeConfig.NodeConfigBuilder("testNode", solrHome)
         .setUseSchemaCache(Boolean.getBoolean("shareSchema"))
         .setCloudConfig(cloudConfig)
-        .setUpdateShardHandlerConfig(updateShardHandlerConfig)
+        .setUpdateShardHandlerConfig(UpdateShardHandlerConfig.TEST_DEFAULT)
         .setMetricsConfig(metricsConfig)
         .build();
   }

Reply via email to