| /* |
| * 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; |
| |
| import com.carrotsearch.randomizedtesting.JUnit4MethodProvider; |
| import com.carrotsearch.randomizedtesting.MixWithSuiteName; |
| import com.carrotsearch.randomizedtesting.RandomizedRunner; |
| import com.carrotsearch.randomizedtesting.annotations.Listeners; |
| import com.carrotsearch.randomizedtesting.annotations.SeedDecorators; |
| import com.carrotsearch.randomizedtesting.annotations.TestMethodProviders; |
| import com.carrotsearch.randomizedtesting.annotations.ThreadLeakAction; |
| import com.carrotsearch.randomizedtesting.annotations.ThreadLeakGroup; |
| import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope; |
| import com.carrotsearch.randomizedtesting.rules.StaticFieldsInvariantRule; |
| import org.apache.lucene.document.Field; |
| import org.apache.lucene.index.IndexableField; |
| import org.apache.lucene.util.FailureMarker; |
| import org.apache.lucene.util.LuceneJUnit3MethodProvider; |
| import org.apache.lucene.util.LuceneTestCase; |
| import com.carrotsearch.randomizedtesting.RandomizedTest; |
| import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters; |
| import com.carrotsearch.randomizedtesting.annotations.ThreadLeakLingering; |
| import com.carrotsearch.randomizedtesting.rules.SystemPropertiesRestoreRule; |
| import org.apache.lucene.codecs.lucene50.Lucene50StoredFieldsFormat; |
| import org.apache.lucene.codecs.lucene86.Lucene86Codec; |
| import org.apache.lucene.util.QuickPatchThreadsFilter; |
| import org.apache.lucene.util.RunListenerPrintReproduceInfo; |
| import org.apache.lucene.util.TestRuleMarkFailure; |
| import org.apache.solr.client.solrj.impl.Http2SolrClient; |
| import org.apache.solr.client.solrj.impl.HttpClientUtil; |
| import org.apache.solr.cloud.ZkSolrResourceLoader; |
| import org.apache.solr.common.AlreadyClosedException; |
| import org.apache.solr.common.ParWork; |
| import org.apache.solr.common.ParWorkExecutor; |
| import org.apache.solr.common.StringUtils; |
| import org.apache.solr.common.TimeTracker; |
| import org.apache.solr.common.params.ModifiableSolrParams; |
| import org.apache.solr.common.util.CloseTracker; |
| import org.apache.solr.common.util.ObjectReleaseTracker; |
| import org.apache.solr.common.util.SolrQueuedThreadPool; |
| import org.apache.solr.common.util.SysStats; |
| import org.apache.solr.security.PublicKeyHandler; |
| import org.apache.solr.servlet.SolrDispatchFilter; |
| import org.apache.solr.util.CryptoKeys; |
| import org.apache.solr.util.ExternalPaths; |
| import org.apache.solr.util.RandomizeSSL; |
| import org.apache.solr.util.RevertDefaultThreadHandlerRule; |
| import org.apache.solr.util.SSLTestConfig; |
| import org.apache.solr.util.StartupLoggingUtils; |
| import org.apache.solr.util.TestInjection; |
| import org.eclipse.jetty.util.BlockingArrayQueue; |
| import org.junit.After; |
| import org.junit.AfterClass; |
| import org.junit.Assert; |
| import org.junit.Before; |
| import org.junit.BeforeClass; |
| import org.junit.ClassRule; |
| import org.junit.Rule; |
| import org.junit.rules.RuleChain; |
| import org.junit.rules.TestRule; |
| import org.junit.rules.TestWatcher; |
| import org.junit.runner.Description; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.model.Statement; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import java.io.File; |
| import java.lang.annotation.Documented; |
| import java.lang.annotation.ElementType; |
| import java.lang.annotation.Inherited; |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.lang.annotation.Target; |
| import java.lang.invoke.MethodHandles; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Random; |
| import java.util.Set; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.ExecutorService; |
| import java.util.concurrent.TimeUnit; |
| |
| /** |
| * All Solr test cases should derive from this class eventually. This is originally a result of async logging, see: |
| * SOLR-12055 and associated. To enable async logging, we must gracefully shut down logging. Many Solr tests subclass |
| * LuceneTestCase. |
| * |
| * Rather than add the cruft from SolrTestCaseJ4 to all the Solr tests that currently subclass LuceneTestCase, |
| * we'll add the shutdown to this class and subclass it. |
| * |
| * Other changes that should affect every Solr test case may go here if they don't require the added capabilities in |
| * SolrTestCaseJ4. |
| */ |
| //0p-@TimeoutSuite(millis = 130 * TimeUnits.SECOND) |
| |
| @RunWith(RandomizedRunner.class) |
| @TestMethodProviders({ |
| LuceneJUnit3MethodProvider.class, |
| JUnit4MethodProvider.class |
| }) |
| @Listeners({ |
| RunListenerPrintReproduceInfo.class, |
| FailureMarker.class |
| }) |
| @ThreadLeakScope(ThreadLeakScope.Scope.SUITE) |
| @ThreadLeakGroup(ThreadLeakGroup.Group.MAIN) |
| @ThreadLeakAction({ThreadLeakAction.Action.WARN, ThreadLeakAction.Action.INTERRUPT}) |
| @SeedDecorators({MixWithSuiteName.class}) // See LUCENE-3995 for rationale. |
| |
| @ThreadLeakFilters(defaultFilters = true, filters = { |
| SolrIgnoredThreadsFilter.class, |
| QuickPatchThreadsFilter.class |
| }) |
| @LuceneTestCase.SuppressSysoutChecks(bugUrl = "Solr dumps tons of logs to console.") |
| @LuceneTestCase.SuppressFileSystems("ExtrasFS") // might be ok, the failures with e.g. nightly runs might be "normal" |
| @RandomizeSSL() |
| @ThreadLeakLingering(linger = 0) |
| public class SolrTestCase extends Assert { |
| |
| protected static final boolean VERBOSE = false; |
| |
| /** |
| * <b>DO NOT REMOVE THIS LOGGER</b> |
| * <p> |
| * For reasons that aren't 100% obvious, the existence of this logger is neccessary to ensure |
| * that the logging framework is properly initialized (even if concrete subclasses do not |
| * themselves initialize any loggers) so that the async logger threads can be properly shutdown |
| * on completion of the test suite |
| * </p> |
| * @see <a href="https://issues.apache.org/jira/browse/SOLR-14247">SOLR-14247</a> |
| * @see #afterSolrTestCase() |
| */ |
| private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); |
| public static final String[] EMPTY_STRING_ARRAY = new String[0]; |
| |
| /** |
| * Max 10mb of static data stored in a test suite class after the suite is complete. |
| * Prevents static data structures leaking and causing OOMs in subsequent tests. |
| */ |
| private final static long STATIC_LEAK_THRESHOLD = 600; // MRM TODO: I dropped this down hard and enabled it again |
| |
| public static final boolean TEST_NIGHTLY = LuceneTestCase.TEST_NIGHTLY; |
| |
| public static TestRuleThreadAndTestName threadAndTestNameRule = new TestRuleThreadAndTestName(); |
| |
| private static TestRuleSetupTeardownChained parentChainCallRule = new TestRuleSetupTeardownChained(); |
| |
| protected static TestRuleMarkFailure suiteFailureMarker; |
| |
| public static TestRuleTemporaryFilesCleanup tempFilesCleanupRule; |
| |
| private static TestRuleSetupAndRestoreClassEnv classEnvRule; |
| @ClassRule |
| public static TestRule solrClassRules = |
| RuleChain.outerRule(new SystemPropertiesRestoreRule()) |
| .around(suiteFailureMarker = new TestRuleMarkFailure()) |
| .around(tempFilesCleanupRule = new TestRuleTemporaryFilesCleanup(suiteFailureMarker)).around(new StaticFieldsInvariantRule(STATIC_LEAK_THRESHOLD, true) { |
| @Override |
| protected boolean accept(java.lang.reflect.Field field) { |
| // Don't count known classes that consume memory once. |
| if (LuceneTestCase.STATIC_LEAK_IGNORED_TYPES.contains(field.getType().getName())) { |
| return false; |
| } |
| // Don't count references from ourselves, we're top-level. |
| if (field.getDeclaringClass() == SolrTestCase.class) { |
| return false; |
| } |
| return super.accept(field); |
| } |
| }) |
| .around(classEnvRule = new TestRuleSetupAndRestoreClassEnv()).around(new RevertDefaultThreadHandlerRule()); |
| private final SolrTestUtil solrTestUtil = new SolrTestUtil(); |
| |
| @Rule |
| public TestRule solrTestRules = RuleChain.outerRule(new SystemPropertiesRestoreRule()) |
| .around(new RevertDefaultThreadHandlerRule()).around(threadAndTestNameRule).around(parentChainCallRule); |
| |
| private static volatile Random random; |
| |
| private volatile static ParWorkExecutor testExecutor; |
| |
| private static volatile CryptoKeys.RSAKeyPair reusedKeys; |
| |
| private static CryptoKeys.RSAKeyPair getRsaKeyPair() { |
| String publicKey = System.getProperty("pkiHandlerPublicKeyPath"); |
| String privateKey = System.getProperty("pkiHandlerPrivateKeyPath"); |
| // If both properties unset, then we fall back to generating a new key pair |
| if (StringUtils.isEmpty(publicKey) && StringUtils.isEmpty(privateKey)) { |
| return new CryptoKeys.RSAKeyPair(); |
| } |
| |
| try { |
| return new CryptoKeys.RSAKeyPair(new URL(privateKey), new URL(publicKey)); |
| } catch (Exception e) { |
| log.error("Error in pblic key/private key URLs", e); |
| } |
| return new CryptoKeys.RSAKeyPair(); |
| } |
| |
| public static synchronized void enableReuseOfCryptoKeys() { |
| if (reusedKeys == null) { |
| reusedKeys = getRsaKeyPair(); |
| } |
| PublicKeyHandler.REUSABLE_KEYPAIR = reusedKeys; |
| } |
| |
| public static void disableReuseOfCryptoKeys() { |
| PublicKeyHandler.REUSABLE_KEYPAIR = null; |
| } |
| |
| |
| /** |
| * Annotation for test classes that want to disable SSL |
| */ |
| @Documented |
| @Inherited |
| @Retention(RetentionPolicy.RUNTIME) |
| @Target(ElementType.TYPE) |
| public @interface SuppressSSL { |
| /** Point to JIRA entry. */ |
| public String bugUrl() default "None"; |
| } |
| |
| /** |
| * Annotation for test classes that always need SSL |
| */ |
| @Documented |
| @Inherited |
| @Retention(RetentionPolicy.RUNTIME) |
| @Target(ElementType.TYPE) |
| public @interface AlwaysUseSSL { |
| |
| } |
| |
| /** |
| * Annotation for test classes that want to disable SSL |
| */ |
| @Documented |
| @Inherited |
| @Retention(RetentionPolicy.RUNTIME) |
| @Target(ElementType.TYPE) |
| public @interface SuppressObjectReleaseTracker { |
| public String object(); |
| } |
| |
| |
| public static final int DEFAULT_ZK_SESSION_TIMEOUT = 30000; // default socket connection timeout in ms |
| public static final int DEFAULT_CONNECTION_TIMEOUT = 10000; // default socket connection timeout in ms |
| public static final int DEFAULT_SOCKET_TIMEOUT_MILLIS = 30000; |
| |
| private static final int SOLR_TEST_TIMEOUT = Integer.getInteger("solr.test.timeout", 25); |
| |
| private static long testStartTime; |
| |
| // these are meant to be accessed sequentially, but are volatile just to ensure any test |
| // thread will read the latest value |
| protected static volatile SSLTestConfig sslConfig; |
| |
| private final static Set<String> interuptThreadWithNameContains = ConcurrentHashMap.newKeySet(); |
| |
| public static Random random() { |
| return random; |
| } |
| |
| /** |
| * Sets the <code>solr.default.confdir</code> system property to the value of |
| * {@link ExternalPaths#DEFAULT_CONFIGSET} if and only if the system property is not already set, |
| * and the <code>DEFAULT_CONFIGSET</code> exists and is a readable directory. |
| * <p> |
| * Logs INFO/WARNing messages as appropriate based on these 2 conditions. |
| * </p> |
| * @see SolrDispatchFilter#SOLR_DEFAULT_CONFDIR_ATTRIBUTE |
| */ |
| @BeforeClass |
| public static void beforeSolrTestCase() throws Exception { |
| log.info("*******************************************************************"); |
| log.info("@BeforeClass ------------------------------------------------------"); |
| |
| System.setProperty("org.eclipse.jetty.util.log.class", "org.apache.logging.log4j.appserver.jetty.Log4j2Logger"); |
| |
| interruptThreadsOnTearDown(false, "SessionTracker"); |
| |
| if (!SysStats.getSysStats().isAlive()) { |
| SysStats.reStartSysStats(); |
| } |
| |
| // other methods like starting a jetty instance need these too |
| System.setProperty("solr.test.sys.prop1", "propone"); |
| System.setProperty("solr.test.sys.prop2", "proptwo"); |
| |
| // random is expensive, you are supposed to cache it |
| random = LuceneTestCase.random(); |
| |
| testStartTime = System.nanoTime(); |
| |
| sslConfig = SolrTestUtil.buildSSLConfig(); |
| if (sslConfig != null && sslConfig.isSSLMode()) { |
| HttpClientUtil.setSocketFactoryRegistryProvider(sslConfig.buildClientSocketFactoryRegistryProvider()); |
| Http2SolrClient.setDefaultSSLConfig(sslConfig.buildClientSSLConfig()); |
| } |
| // based on randomized SSL config, set SocketFactoryRegistryProvider appropriately |
| if (isSSLMode()) { |
| // SolrCloud tests should usually clear this |
| System.setProperty("urlScheme", "https"); |
| } else { |
| System.setProperty("urlScheme", "http"); |
| } |
| |
| System.setProperty("lucene.cms.override_spins", "true"); // TODO: detecting spins for every core, every IW#ConcurrentMergeScheduler can be a bit costly, let's detect and cache somehow? |
| |
| System.setProperty("useCompoundFile", "true"); |
| System.setProperty("solr.tests.maxBufferedDocs", "1000"); |
| |
| System.setProperty("solr.getleader.looptimeout", "1500"); |
| |
| System.setProperty("pkiHandlerPrivateKeyPath", SolrTestCaseJ4.class.getClassLoader().getResource("cryptokeys/priv_key512_pkcs8.pem").toExternalForm()); |
| System.setProperty("pkiHandlerPublicKeyPath", SolrTestCaseJ4.class.getClassLoader().getResource("cryptokeys/pub_key512.der").toExternalForm()); |
| |
| System.setProperty("solr.createCollectionTimeout", "10000"); |
| System.setProperty("solr.enablePublicKeyHandler", "true"); |
| System.setProperty("solr.zkclienttimeout", "30000"); |
| System.setProperty("solr.v2RealPath", "true"); |
| System.setProperty("zookeeper.forceSync", "no"); |
| System.setProperty("jetty.testMode", "true"); |
| System.setProperty("tests.shardhandler.randomSeed", Long.toString(random().nextLong())); |
| System.setProperty("solr.clustering.enabled", "false"); |
| System.setProperty("solr.peerSync.useRangeVersions", String.valueOf(random().nextBoolean())); |
| System.setProperty("zookeeper.nio.directBufferBytes", Integer.toString(32 * 1024 * 2)); |
| |
| // we need something as a default, at least these are fast |
| System.setProperty(SolrTestCaseJ4.USE_NUMERIC_POINTS_SYSPROP, "false"); |
| System.setProperty("solr.tests.IntegerFieldType", "org.apache.solr.schema.TrieIntField"); |
| System.setProperty("solr.tests.FloatFieldType", "org.apache.solr.schema.TrieFloatField"); |
| System.setProperty("solr.tests.LongFieldType", "org.apache.solr.schema.TrieLongField"); |
| System.setProperty("solr.tests.DoubleFieldType", "org.apache.solr.schema.TrieDoubleField"); |
| System.setProperty("solr.tests.DateFieldType", "org.apache.solr.schema.TrieDateField"); |
| System.setProperty("solr.tests.EnumFieldType", "org.apache.solr.schema.EnumFieldType"); |
| System.setProperty("solr.tests.numeric.dv", "true"); |
| |
| System.setProperty("solr.tests.ramBufferSizeMB", "100"); |
| System.setProperty("solr.tests.ramPerThreadHardLimitMB", "100"); |
| |
| System.setProperty("solr.tests.mergePolicyFactory", "org.apache.solr.index.NoMergePolicyFactory"); |
| System.setProperty("solr.tests.mergeScheduler", "org.apache.lucene.index.ConcurrentMergeScheduler"); |
| System.setProperty("solr.mscheduler", "org.apache.lucene.index.ConcurrentMergeScheduler"); |
| //enableReuseOfCryptoKeys(); |
| |
| |
| // default field types |
| System.setProperty(SolrTestCaseJ4.USE_NUMERIC_POINTS_SYSPROP, "false"); |
| System.setProperty("solr.tests.IntegerFieldType", "org.apache.solr.schema.TrieIntField"); |
| System.setProperty("solr.tests.FloatFieldType", "org.apache.solr.schema.TrieFloatField"); |
| System.setProperty("solr.tests.LongFieldType", "org.apache.solr.schema.TrieLongField"); |
| System.setProperty("solr.tests.DoubleFieldType", "org.apache.solr.schema.TrieDoubleField"); |
| System.setProperty("solr.tests.DateFieldType", "org.apache.solr.schema.TrieDateField"); |
| System.setProperty("solr.tests.EnumFieldType", "org.apache.solr.schema.EnumFieldType"); |
| System.setProperty("solr.tests.numeric.dv", "true"); |
| |
| //System.setProperty("managed.schema.mutable", "false"); |
| |
| if (!LuceneTestCase.TEST_NIGHTLY) { |
| //TestInjection.randomDelayMaxInCoreCreationInSec = 2; |
| Lucene86Codec codec = new Lucene86Codec(Lucene50StoredFieldsFormat.Mode.BEST_SPEED); |
| //Codec.setDefault(codec); |
| disableReuseOfCryptoKeys(); |
| System.setProperty("solr.zkstatewriter.throttle", "0"); |
| System.setProperty("solr.stateworkqueue.throttle", "0"); |
| |
| System.setProperty("zkReaderGetLeaderRetryTimeoutMs", "800"); |
| |
| System.setProperty("solr.enablePublicKeyHandler", "false"); |
| System.setProperty("solr.lbclient.live_check_interval", "3000"); |
| System.setProperty("zookeeper.request.timeout", "15000"); |
| |
| |
| System.setProperty("solr.concurrentRequests.max", "15"); |
| System.setProperty("solr.tests.infostream", "false"); |
| System.setProperty("numVersionBuckets", "16384"); // TODO: wrong sys prop, also not usually setup in conf to work |
| |
| // System.setProperty("solr.per_thread_exec.max_threads", "2"); |
| // System.setProperty("solr.per_thread_exec.min_threads", "1"); |
| |
| System.setProperty("zookeeper.nio.numSelectorThreads", "2"); |
| System.setProperty("zookeeper.nio.numWorkerThreads", "3"); |
| System.setProperty("zookeeper.commitProcessor.numWorkerThreads", "2"); |
| System.setProperty("zookeeper.skipACL", "true"); |
| System.setProperty("zookeeper.nio.shutdownTimeout", "10"); |
| |
| // can make things quite slow |
| System.setProperty("solr.disableDefaultJmxReporter", "true"); |
| |
| System.setProperty("solr.skipCommitOnClose", "false"); |
| |
| // can generate tons of URL garbage and can happen too often, defaults to false now anyway |
| System.setProperty("solr.reloadSPI", "false"); |
| |
| // MRM TODO: - not used again yet |
| // System.setProperty("solr.OverseerStateUpdateDelay", "0"); |
| |
| System.setProperty("solr.enableMetrics", "false"); |
| |
| // System.setProperty("solr.leaderThrottle", "1000"); |
| // System.setProperty("solr.recoveryThrottle", "1000"); |
| |
| System.setProperty("solr.suppressDefaultConfigBootstrap", "true"); |
| |
| System.setProperty("solr.defaultCollectionActiveWait", "10"); |
| |
| System.setProperty("solr.http2solrclient.maxpool.size", "16"); |
| System.setProperty("solr.http2solrclient.pool.keepalive", "1500"); |
| |
| System.setProperty("solr.dependentupdate.timeout", "1500"); |
| |
| // System.setProperty("lucene.cms.override_core_count", "3"); |
| |
| // unlimited - System.setProperty("solr.maxContainerThreads", "300"); |
| System.setProperty("solr.lowContainerThreadsThreshold", "-1"); |
| System.setProperty("solr.minContainerThreads", "8"); |
| System.setProperty("solr.rootSharedThreadPoolCoreSize", "12"); |
| System.setProperty("solr.minHttp2ClientThreads", "6"); |
| |
| System.setProperty("solr.containerThreadsIdleTimeout", "1000"); |
| System.setProperty("solr.containerThreadsIdle", "1000"); |
| |
| |
| System.setProperty("solr.tests.maxBufferedDocs", "1000000"); |
| System.setProperty("solr.tests.ramPerThreadHardLimitMB", "90"); |
| |
| System.setProperty("solr.tests.ramBufferSizeMB", "100"); |
| |
| System.setProperty("solr.http2solrclient.default.idletimeout", "15000"); |
| System.setProperty("distribUpdateSoTimeout", "15000"); |
| System.setProperty("socketTimeout", "30000"); |
| System.setProperty("connTimeout", "30000"); |
| System.setProperty("solr.test.socketTimeout.default", "30000"); |
| System.setProperty("solr.connect_timeout.default", "10000"); |
| System.setProperty("solr.so_commit_timeout.default", "15000"); |
| System.setProperty("solr.httpclient.defaultConnectTimeout", "10000"); |
| System.setProperty("solr.httpclient.defaultSoTimeout", "30000"); |
| |
| |
| System.setProperty("solr.indexfetcher.sotimeout", "30000"); |
| System.setProperty("solr.indexfetch.so_timeout.default", "30000"); |
| |
| System.setProperty("prepRecoveryReadTimeoutExtraWait", "100"); |
| System.setProperty("validateAfterInactivity", "-1"); |
| System.setProperty("leaderVoteWait", "2500"); // this is also apparently controlling how long we wait for a leader on register MRM TODO: |
| System.setProperty("leaderConflictResolveWait", "10000"); |
| |
| System.setProperty("bucketVersionLockTimeoutMs", "8000"); |
| System.setProperty("socketTimeout", "15000"); |
| System.setProperty("connTimeout", "10000"); |
| System.setProperty("solr.cloud.wait-for-updates-with-stale-state-pause", "0"); |
| System.setProperty("solr.cloud.starting-recovery-delay-milli-seconds", "0"); |
| |
| |
| System.setProperty("solr.default.collection_op_timeout", "15000"); |
| |
| System.setProperty("useCompoundFile", "false"); |
| |
| System.setProperty("solr.httpclient.retries", "1"); |
| System.setProperty("solr.retries.on.forward", "1"); |
| System.setProperty("solr.retries.to.followers", "1"); |
| |
| SolrTestCaseJ4.useFactory("org.apache.solr.core.RAMDirectoryFactory"); |
| System.setProperty("solr.lock.type", "single"); |
| System.setProperty("solr.tests.lockType", "single"); |
| |
| System.setProperty("solr.codec", "solr.SchemaCodecFactory"); |
| System.setProperty("tests.COMPRESSION_MODE", "BEST_COMPRESSION"); |
| } |
| |
| |
| final String existingValue = System.getProperty(SolrDispatchFilter.SOLR_DEFAULT_CONFDIR_ATTRIBUTE); |
| if (null != existingValue) { |
| log.info("Test env includes configset dir system property '{}'='{}'", SolrDispatchFilter.SOLR_DEFAULT_CONFDIR_ATTRIBUTE, existingValue); |
| return; |
| } |
| final File extPath = new File(ExternalPaths.DEFAULT_CONFIGSET); |
| if (extPath.canRead(/* implies exists() */) && extPath.isDirectory()) { |
| log.info("Setting '{}' system property to test-framework derived value of '{}'", |
| SolrDispatchFilter.SOLR_DEFAULT_CONFDIR_ATTRIBUTE, ExternalPaths.DEFAULT_CONFIGSET); |
| assert null == existingValue; |
| System.setProperty(SolrDispatchFilter.SOLR_DEFAULT_CONFDIR_ATTRIBUTE, ExternalPaths.DEFAULT_CONFIGSET); |
| } else { |
| log.warn("System property '{}' is not already set, but test-framework derived value ('{}') either " + |
| "does not exist or is not a readable directory, you may need to set the property yourself " + |
| "for tests to run properly", |
| SolrDispatchFilter.SOLR_DEFAULT_CONFDIR_ATTRIBUTE, ExternalPaths.DEFAULT_CONFIGSET); |
| } |
| |
| log.info("@BeforeClass end ------------------------------------------------------"); |
| log.info("*******************************************************************"); |
| } |
| |
| protected static boolean isSSLMode() { |
| return sslConfig != null && sslConfig.isSSLMode(); |
| } |
| |
| /** |
| * Special hook for sanity checking if any tests trigger failures when an |
| * Assumption failure occures in a {@link BeforeClass} method |
| * @lucene.internal |
| */ |
| // @BeforeClass |
| // public static void checkSyspropForceBeforeClassAssumptionFailure() { |
| // // ant test -Dargs="-Dtests.force.assumption.failure.beforeclass=true" |
| // final String PROP = "tests.force.assumption.failure.beforeclass"; |
| // assumeFalse(PROP + " == true", |
| // systemPropertyAsBoolean(PROP, false)); |
| // } |
| |
| @After |
| public void afterSolrTestCase() throws Exception { |
| |
| } |
| |
| @AfterClass |
| public static void afterSolrTestCaseClass() throws Exception { |
| log.info("*******************************************************************"); |
| log.info("@After Class ------------------------------------------------------"); |
| try { |
| |
| SysStats.getSysStats().stopMonitor(); |
| |
| if (testExecutor != null) { |
| testExecutor.disableCloseLock(); |
| testExecutor.shutdown(); |
| } |
| |
| |
| AlreadyClosedException lastAlreadyClosedExp = CloseTracker.lastAlreadyClosedEx; |
| if (lastAlreadyClosedExp != null) { |
| CloseTracker.lastAlreadyClosedEx = null; |
| throw lastAlreadyClosedExp; |
| } |
| |
| |
| IllegalCallerException lastIllegalCallerEx = CloseTracker.lastIllegalCallerEx; |
| if (lastIllegalCallerEx != null) { |
| CloseTracker.lastIllegalCallerEx = null; |
| throw lastIllegalCallerEx; |
| } |
| |
| String object = null; |
| // if the tests passed, make sure everything was closed / released |
| if (RandomizedTest.getContext().getTargetClass().isAnnotationPresent(SuppressObjectReleaseTracker.class)) { |
| SuppressObjectReleaseTracker sor = RandomizedTest.getContext().getTargetClass().getAnnotation(SuppressObjectReleaseTracker.class); |
| object = sor.object(); |
| } |
| |
| String orr = ObjectReleaseTracker.checkEmpty(object); |
| ObjectReleaseTracker.clear(); |
| assertNull(orr, orr); |
| |
| if (testExecutor != null) { |
| boolean success = testExecutor.awaitTermination(5, TimeUnit.SECONDS); |
| assertTrue(success); |
| testExecutor = null; |
| } |
| ParWork.shutdownParWorkExecutor(); |
| |
| } finally { |
| ObjectReleaseTracker.clear(); |
| TestInjection.reset(); |
| } |
| try { |
| HttpClientUtil.resetHttpClientBuilder(); |
| Http2SolrClient.resetSslContextFactory(); |
| TestInjection.reset(); |
| JSONTestUtil.failRepeatedKeys = false; |
| ZkSolrResourceLoader.CONFIG_CACHE.clear(); |
| random = null; |
| reusedKeys = null; |
| sslConfig = null; |
| |
| long testTime = TimeUnit.SECONDS.convert(System.nanoTime() - testStartTime, TimeUnit.NANOSECONDS); |
| if (!LuceneTestCase.TEST_NIGHTLY && testTime > SOLR_TEST_TIMEOUT) { |
| log.error("This test suite is too long for non @Nightly runs! Please improve it's performance, break it up, make parts of it @Nightly or make the whole suite @Nightly: {}" |
| , testTime); |
| // fail( |
| // "This test suite is too long for non @Nightly runs! Please improve it's performance, break it up, make parts of it @Nightly or make the whole suite @Nightly: " |
| // + testTime); |
| } |
| } finally { |
| |
| Class<? extends Object> clazz = null; |
| Long tooLongTime = 0L; |
| String times = null; |
| // try { |
| // synchronized (TimeTracker.CLOSE_TIMES) { |
| // Map<String, TimeTracker> closeTimes = TimeTracker.CLOSE_TIMES; |
| // for (TimeTracker closeTime : closeTimes.values()) { |
| // int closeTimeout = Integer.getInteger("solr.parWorkTestTimeout", 10000); |
| // if (closeTime.getElapsedMS() > closeTimeout) { |
| // tooLongTime = closeTime.getElapsedMS(); |
| // clazz = closeTime.getClazz(); |
| // times = closeTime.getCloseTimes(); |
| // } |
| // // turn off until layout is fixed again |
| // // closeTime.printCloseTimes(); |
| // } |
| // } |
| // |
| // } finally { |
| TimeTracker.CLOSE_TIMES.clear(); |
| // } |
| |
| if (clazz != null) { |
| // MRM TODO: - leave this on |
| if (!LuceneTestCase.TEST_NIGHTLY) fail("A " + clazz.getName() + " took too long to close: " + tooLongTime + "\n" + times); |
| } |
| } |
| if (log.isInfoEnabled()) { |
| log.info("@AfterClass end ------------------------------------------------------"); |
| } |
| if (log.isInfoEnabled()) { |
| log.info("*******************************************************************"); |
| } |
| |
| checkForInterruptRequest(); |
| interuptThreadWithNameContains.clear(); |
| StartupLoggingUtils.shutdown(); |
| } |
| |
| private static void checkForInterruptRequest() { |
| try { |
| interruptThreadsOnTearDown(true, interuptThreadWithNameContains.toArray(EMPTY_STRING_ARRAY)); |
| } catch (Exception e) { |
| ParWork.propagateInterrupt(e); |
| log.error("", e); |
| } |
| } |
| |
| /** |
| * For subclasses to override. Overrides must call {@code super.setUp()}. |
| */ |
| @Before |
| public void setUp() throws Exception { |
| parentChainCallRule.setupCalled = true; |
| } |
| |
| /** |
| * For subclasses to override. Overrides must call {@code super.tearDown()}. |
| */ |
| @After |
| public void tearDown() throws Exception { |
| parentChainCallRule.teardownCalled = true; |
| } |
| |
| // expert - for special cases |
| public static void interruptThreadsOnTearDown(boolean now, String... nameContains) { |
| if (!now) { |
| interuptThreadWithNameContains.addAll(Arrays.asList(nameContains)); |
| return; |
| } |
| |
| log.info("Checking leaked threads after test"); |
| |
| // System.out.println("DO FORCED INTTERUPTS"); |
| // we need to filter and only do this for known threads? dont want users to count on this behavior unless necessary |
| String testThread = Thread.currentThread().getName(); |
| // System.out.println("test thread:" + testThread); |
| ThreadGroup tg = Thread.currentThread().getThreadGroup(); |
| // System.out.println("test group:" + tg.getName()); |
| Set<Map.Entry<Thread,StackTraceElement[]>> threadSet = Thread.getAllStackTraces().entrySet(); |
| if (log.isInfoEnabled()) { |
| log.info("thread count={}", threadSet.size()); |
| } |
| List<Thread> waitThreads = new ArrayList<>(); |
| for (Map.Entry<Thread,StackTraceElement[]> threadEntry : threadSet) { |
| Thread thread = threadEntry.getKey(); |
| ThreadGroup threadGroup = thread.getThreadGroup(); |
| if (threadGroup != null) { |
| log.warn("thread is {}", thread.getName()); |
| if (threadGroup.getName().equals(tg.getName()) && !(thread.getName().startsWith("SUITE") && thread.getName().endsWith("]"))) { |
| if (interrupt(thread, nameContains)) { |
| waitThreads.add(thread); |
| } |
| } |
| } |
| |
| while (threadGroup != null && threadGroup.getParent() != null && !(thread.getName().startsWith("SUITE") && thread.getName().endsWith("]"))) { |
| threadGroup = threadGroup.getParent(); |
| //if (thread.getState().equals(Thread.State.TERMINATED) || nameContains != null && threadGroup.getName().equals(tg.getName())) { |
| if (threadGroup.getName().equals(tg.getName())) { |
| log.warn("thread is {}", thread.getName()); |
| if (interrupt(thread, nameContains)) { |
| waitThreads.add(thread); |
| } |
| } |
| } |
| } |
| for (Thread thread : waitThreads) { |
| SolrTestUtil.wait(thread); |
| } |
| |
| waitThreads.clear(); |
| } |
| |
| private static boolean interrupt(Thread thread, String... nameContains) { |
| if (thread.getName().contains("ForkJoinPool.") || thread.getName().contains("Log4j2-")) { |
| return false; |
| } |
| |
| if (thread.getName().contains("-SendThread")) { |
| log.warn("interrupt on {}", thread.getName()); |
| thread.interrupt(); |
| return true; |
| } |
| if (((thread.getName().contains(ParWork.ROOT_EXEC_NAME + "-") || thread.getName().contains("ParWork-") || thread.getName().contains("Core-") |
| || thread.getName().contains("ProcessThread(")) && thread.getState() != Thread.State.TERMINATED)) { |
| log.warn("interrupt on {}", thread.getName()); |
| thread.interrupt(); |
| return true; |
| } |
| if (interruptThreadListContains(nameContains, thread.getName()) && thread.getState() != Thread.State.TERMINATED) { |
| log.warn("interrupt on {}", thread.getName()); |
| thread.interrupt(); |
| return true; |
| } |
| return false; |
| } |
| |
| private static boolean interruptThreadListContains(String[] nameContains, String name) { |
| for (String interruptThread : nameContains) { |
| if (name.contains(interruptThread)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public static SolrQueuedThreadPool getQtp() throws Exception { |
| return new SolrQueuedThreadPool("solr-test-qtp"); |
| } |
| |
| |
| private static boolean changedFactory = false; |
| private static String savedFactory; |
| /** Use a different directory factory. Passing "null" sets to an FS-based factory */ |
| public static void useFactory(String factory) throws Exception { |
| // allow calling more than once so a subclass can override a base class |
| if (!changedFactory) { |
| savedFactory = System.getProperty("solr.DirectoryFactory"); |
| } |
| |
| if (factory == null) { |
| factory = random().nextInt(100) < 75 ? "solr.NRTCachingDirectoryFactory" : "solr.StandardDirectoryFactory"; // test the default most of the time |
| } |
| System.setProperty("solr.directoryFactory", factory); |
| changedFactory = true; |
| } |
| |
| public static void resetFactory() throws Exception { |
| if (!changedFactory) return; |
| changedFactory = false; |
| if (savedFactory != null) { |
| System.setProperty("solr.directoryFactory", savedFactory); |
| savedFactory = null; |
| } else { |
| System.clearProperty("solr.directoryFactory"); |
| } |
| } |
| |
| |
| public String getSaferTestName() { |
| // test names can hold additional info, like the test seed |
| // only take to first space |
| return solrTestUtil.getSaferTestName(); |
| } |
| |
| /** |
| * Generates the correct SolrParams from an even list of strings. |
| * A string in an even position will represent the name of a parameter, while the following string |
| * at position (i+1) will be the assigned value. |
| * |
| * @param params an even list of strings |
| * @return the ModifiableSolrParams generated from the given list of strings. |
| */ |
| public static ModifiableSolrParams params(String... params) { |
| if (params.length % 2 != 0) throw new RuntimeException("Params length should be even"); |
| ModifiableSolrParams msp = new ModifiableSolrParams(); |
| for (int i=0; i<params.length; i+=2) { |
| msp.add(params[i], params[i+1]); |
| } |
| return msp; |
| } |
| |
| public boolean compareSolrDocumentList(Object expected, Object actual) { |
| |
| return solrTestUtil.compareSolrDocumentList(expected, actual); |
| } |
| |
| public boolean compareSolrDocument(Object expected, Object actual) { |
| |
| return solrTestUtil.compareSolrDocument(expected, actual); |
| } |
| |
| public static ExecutorService getTestExecutor() { |
| synchronized (SolrTestCase.class) { |
| if (testExecutor != null) { |
| return testExecutor; |
| } |
| testExecutor = (ParWorkExecutor) ParWork.getParExecutorService( |
| "testExecutor", 5, 64, 500, new BlockingArrayQueue(12, 16)); |
| testExecutor.prestartAllCoreThreads(); |
| ((ParWorkExecutor) testExecutor).enableCloseLock(); |
| return testExecutor; |
| } |
| } |
| |
| private static class SolrTestWatcher extends TestWatcher { |
| @Override |
| protected void failed(Throwable e, Description description) { |
| |
| } |
| |
| @Override |
| protected void succeeded(Description description) { |
| } |
| } |
| |
| /** |
| * Saves the executing thread and method name of the test case. |
| */ |
| final static class TestRuleThreadAndTestName implements TestRule { |
| |
| public volatile Thread testCaseThread; |
| |
| /** |
| * Test method name. |
| */ |
| public volatile String testMethodName = "<unknown>"; |
| |
| @Override |
| public Statement apply(final Statement base, final Description description) { |
| return new TestStatement(base, description); |
| } |
| |
| private class TestStatement extends Statement { |
| private final Statement base; |
| private final Description description; |
| |
| public TestStatement(Statement base, Description description) { |
| this.base = base; |
| this.description = description; |
| } |
| |
| @Override |
| public void evaluate() throws Throwable { |
| try { |
| Thread current = Thread.currentThread(); |
| testCaseThread = current; |
| testMethodName = description.getMethodName(); |
| |
| base.evaluate(); |
| } finally { |
| testCaseThread = null; |
| testMethodName = null; |
| } |
| } |
| } |
| } |
| |
| private IndexableField newTextField(String value, String foo_bar_bar_bar_bar, Field.Store no) { |
| return solrTestUtil.newTextField(value, foo_bar_bar_bar_bar, no); |
| } |
| |
| protected IndexableField newStringField(String value, String bar, Field.Store yes) { |
| return solrTestUtil.newStringField(value, bar, yes); |
| } |
| |
| /** |
| * Make sure {@link LuceneTestCase#setUp()} and {@link LuceneTestCase#tearDown()} were invoked even if they |
| * have been overriden. We assume nobody will call these out of non-overriden |
| * methods (they have to be public by contract, unfortunately). The top-level |
| * methods just set a flag that is checked upon successful execution of each test |
| * case. |
| */ |
| static class TestRuleSetupTeardownChained implements TestRule { |
| /** |
| * see org.apache.lucene.util.TestRuleSetupTeardownChained |
| */ |
| public volatile boolean setupCalled; |
| |
| /** |
| * see org.apache.lucene.util.TestRuleSetupTeardownChained |
| */ |
| public volatile boolean teardownCalled; |
| |
| @Override |
| public Statement apply(final Statement base, Description description) { |
| return new Statement() { |
| @Override |
| public void evaluate() throws Throwable { |
| setupCalled = false; |
| teardownCalled = false; |
| base.evaluate(); |
| |
| // I assume we don't want to check teardown chaining if something happens in the |
| // test because this would obscure the original exception? |
| if (!setupCalled) { |
| Assert.fail("One of the overrides of setUp does not propagate the call."); |
| } |
| if (!teardownCalled) { |
| Assert.fail("One of the overrides of tearDown does not propagate the call."); |
| } |
| } |
| }; |
| } |
| } |
| } |