| /* |
| * 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.geode.test.dunit.rules; |
| |
| import static org.apache.geode.test.dunit.Disconnect.disconnectFromDS; |
| import static org.apache.geode.test.dunit.DistributedTestUtils.unregisterInstantiatorsInThisVM; |
| import static org.apache.geode.test.dunit.Invoke.invokeInEveryVM; |
| import static org.apache.geode.test.dunit.Invoke.invokeInLocator; |
| import static org.apache.geode.test.dunit.VM.DEFAULT_VM_COUNT; |
| |
| import org.apache.geode.cache.query.QueryTestUtils; |
| import org.apache.geode.cache.query.internal.QueryObserverHolder; |
| import org.apache.geode.cache30.ClientServerTestCase; |
| import org.apache.geode.cache30.RegionTestCase; |
| import org.apache.geode.distributed.internal.DistributionConfig; |
| import org.apache.geode.distributed.internal.DistributionMessageObserver; |
| import org.apache.geode.distributed.internal.InternalDistributedSystem; |
| import org.apache.geode.distributed.internal.tcpserver.TcpClient; |
| import org.apache.geode.internal.admin.ClientStatsManager; |
| import org.apache.geode.internal.cache.DiskStoreObserver; |
| import org.apache.geode.internal.cache.InitialImageOperation; |
| import org.apache.geode.internal.cache.tier.InternalClientMembership; |
| import org.apache.geode.internal.cache.tier.sockets.CacheServerTestUtil; |
| import org.apache.geode.internal.cache.tier.sockets.ClientProxyMembershipID; |
| import org.apache.geode.internal.cache.tier.sockets.Message; |
| import org.apache.geode.internal.cache.xmlcache.CacheCreation; |
| import org.apache.geode.internal.net.SocketCreator; |
| import org.apache.geode.internal.net.SocketCreatorFactory; |
| import org.apache.geode.management.internal.cli.LogWrapper; |
| import org.apache.geode.pdx.internal.TypeRegistry; |
| import org.apache.geode.test.dunit.IgnoredException; |
| import org.apache.geode.test.dunit.internal.DUnitLauncher; |
| import org.apache.geode.test.junit.rules.serializable.SerializableExternalResource; |
| |
| /** |
| * JUnit Rule that launches DistributedTest VMs and scans all log output for suspect strings without |
| * {@code DistributedTestCase}. The test class may need to implement {@code Serializable} if it |
| * uses lambdas to perform {@code RMI} invocations on {@code VM}s. |
| * |
| * <p> |
| * {@code DistributedRule} can be used in DistributedTests as a {@code Rule}. This will ensure |
| * that checking for suspect strings is performed after each test method. |
| * |
| * <pre> |
| * {@literal @}Rule |
| * public DistributedRule distributedRule = new DistributedRule(); |
| * |
| * {@literal @}Test |
| * public void shouldHaveFourVMsByDefault() { |
| * assertThat(getVMCount()).isEqualTo(4); |
| * } |
| * </pre> |
| * |
| * <p> |
| * You may specify a non-default number of {@code VM}s for the test when constructing |
| * {@code DistributedRule}. |
| * |
| * <p> |
| * Example of specifying fewer that the default number of {@code VM}s (which is 4): |
| * |
| * <pre> |
| * {@literal @}Rule |
| * public DistributedRule distributedRule = new DistributedRule(1); |
| * |
| * {@literal @}Test |
| * public void hasOneVM() { |
| * assertThat(getVMCount()).isEqualTo(1); |
| * } |
| * </pre> |
| * |
| * <p> |
| * Example of specifying greater that the default number of {@code VM}s (which is 4): |
| * |
| * <pre> |
| * {@literal @}Rule |
| * public DistributedRule distributedRule = new DistributedRule(8); |
| * |
| * {@literal @}Test |
| * public void hasEightVMs() { |
| * assertThat(getVMCount()).isEqualTo(8); |
| * } |
| * </pre> |
| * |
| * <p> |
| * {@code DistributedRule} can also be used in DistributedTests as a {@code ClassRule}. This ensures |
| * that DUnit VMs will be available to non-Class {@code Rule}s. However, you may want to declare |
| * {@code DistributedRule.TearDown} as a non-Class {@code Rule} so that check for suspect strings is |
| * performed after each test method. |
| * |
| * <pre> |
| * {@literal @}ClassRule |
| * public static DistributedRule distributedRule = new DistributedRule(); |
| * |
| * {@literal @}Rule |
| * public DistributedRule.TearDown distributedRuleTearDown = new DistributedRule.TearDown(); |
| * |
| * {@literal @}Test |
| * public void shouldHaveFourDUnitVMsByDefault() { |
| * assertThat(getVMCount()).isEqualTo(4); |
| * } |
| * </pre> |
| */ |
| @SuppressWarnings("unused") |
| public class DistributedRule extends AbstractDistributedRule { |
| |
| /** |
| * Use {@code Builder} for more options in constructing {@code DistributedRule}. |
| */ |
| public static Builder builder() { |
| return new Builder(); |
| } |
| |
| /** |
| * Constructs DistributedRule and launches the default number of {@code VM}s (which is 4). |
| */ |
| public DistributedRule() { |
| this(new Builder()); |
| } |
| |
| /** |
| * Constructs DistributedRule and launches the specified number of {@code VM}s. |
| * |
| * @param vmCount specified number of VMs |
| */ |
| public DistributedRule(final int vmCount) { |
| this(new Builder().withVMCount(vmCount)); |
| } |
| |
| private DistributedRule(final Builder builder) { |
| super(builder.vmCount); |
| } |
| |
| @Override |
| protected void after() { |
| TearDown.doTearDown(); |
| } |
| |
| /** |
| * Builds an instance of DistributedRule. |
| */ |
| public static class Builder { |
| |
| private int vmCount = DEFAULT_VM_COUNT; |
| |
| public Builder withVMCount(final int vmCount) { |
| if (vmCount < 0) { |
| throw new IllegalArgumentException("VM count must be positive integer"); |
| } |
| this.vmCount = vmCount; |
| return this; |
| } |
| |
| public DistributedRule build() { |
| return new DistributedRule(this); |
| } |
| } |
| |
| /** |
| * Cleans up horrendous things like static state and non-default instances in Geode. |
| * |
| * <p> |
| * {@link DistributedRule#after()} invokes the same cleanup that this Rule does, but if you |
| * defined {@code DistributedRule} as a {@code ClassRule} then you should declare TearDown |
| * as a non-class {@code Rule} in your test: |
| * |
| * <pre> |
| * {@literal @}ClassRule |
| * public static DistributedRule distributedRule = new DistributedRule(); |
| * |
| * {@literal @}Rule |
| * public DistributedRule.TearDown tearDownRule = new DistributedRule.TearDown(); |
| * |
| * {@literal @}Test |
| * public void shouldHaveFourDUnitVMsByDefault() { |
| * assertThat(getVMCount()).isEqualTo(4); |
| * } |
| * </pre> |
| * |
| * <p> |
| * Note: {@link CacheRule} handles its own cleanup of Cache and Regions. |
| */ |
| public static class TearDown extends SerializableExternalResource { |
| |
| @Override |
| protected void before() { |
| // nothing |
| } |
| |
| @Override |
| protected void after() { |
| doTearDown(); |
| } |
| |
| static void doTearDown() { |
| tearDownInVM(); |
| invokeInEveryVM(() -> { |
| tearDownInVM(); |
| }); |
| invokeInLocator(() -> { |
| DistributionMessageObserver.setInstance(null); |
| unregisterInstantiatorsInThisVM(); |
| }); |
| DUnitLauncher.closeAndCheckForSuspects(); |
| } |
| |
| public static void tearDownInVM() { |
| // 1. Please do NOT add to this list. I'm trying to DELETE this list. |
| // 2. Instead, please add to the after() of your test or your rule. |
| |
| disconnectFromDS(); |
| |
| // keep alphabetized to detect duplicate lines |
| CacheCreation.clearThreadLocals(); |
| CacheServerTestUtil.clearCacheReference(); |
| ClientProxyMembershipID.system = null; |
| ClientServerTestCase.AUTO_LOAD_BALANCE = false; |
| ClientStatsManager.cleanupForTests(); |
| DiskStoreObserver.setInstance(null); |
| unregisterInstantiatorsInThisVM(); |
| DistributionMessageObserver.setInstance(null); |
| InitialImageOperation.slowImageProcessing = 0; |
| InternalClientMembership.unregisterAllListeners(); |
| LogWrapper.close(); |
| QueryObserverHolder.reset(); |
| QueryTestUtils.setCache(null); |
| RegionTestCase.preSnapshotRegion = null; |
| SocketCreator.resetHostNameCache(); |
| SocketCreator.resolve_dns = true; |
| TcpClient.clearStaticData(); |
| |
| // clear system properties -- keep alphabetized |
| System.clearProperty(DistributionConfig.GEMFIRE_PREFIX + "log-level"); |
| System.clearProperty("jgroups.resolve_dns"); |
| System.clearProperty(Message.MAX_MESSAGE_SIZE_PROPERTY); |
| |
| if (InternalDistributedSystem.systemAttemptingReconnect != null) { |
| InternalDistributedSystem.systemAttemptingReconnect.stopReconnecting(); |
| } |
| |
| IgnoredException.removeAllExpectedExceptions(); |
| SocketCreatorFactory.close(); |
| TypeRegistry.setPdxSerializer(null); |
| TypeRegistry.init(); |
| } |
| } |
| } |