blob: c39dcd3857277d2bff7a0f5ef50b190a36dbca7f [file] [log] [blame]
/*=========================================================================
* Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
* This product is protected by U.S. and international copyright
* and intellectual property laws. Pivotal products are covered by
* one or more patents listed at http://www.pivotal.io/patents.
*=========================================================================
*/
package dunit;
import java.io.File;
import java.io.PrintWriter;
import java.io.Serializable;
import java.io.StringWriter;
import java.net.UnknownHostException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import junit.framework.TestCase;
import com.gemstone.gemfire.LogWriter;
import com.gemstone.gemfire.admin.internal.AdminDistributedSystemImpl;
import com.gemstone.gemfire.cache.Cache;
import com.gemstone.gemfire.cache.DiskStoreFactory;
import com.gemstone.gemfire.cache.hdfs.internal.HDFSStoreImpl;
import com.gemstone.gemfire.cache.hdfs.internal.hoplog.HoplogConfig;
import com.gemstone.gemfire.cache.query.QueryTestUtils;
import com.gemstone.gemfire.cache.query.internal.QueryObserverHolder;
import com.gemstone.gemfire.cache30.GlobalLockingDUnitTest;
import com.gemstone.gemfire.cache30.MultiVMRegionTestCase;
import com.gemstone.gemfire.cache30.RegionTestCase;
import com.gemstone.gemfire.distributed.DistributedSystem;
import com.gemstone.gemfire.distributed.internal.DistributionConfig;
import com.gemstone.gemfire.distributed.internal.DistributionConfigImpl;
import com.gemstone.gemfire.distributed.internal.DistributionMessageObserver;
import com.gemstone.gemfire.distributed.internal.InternalDistributedSystem;
import com.gemstone.gemfire.distributed.internal.InternalDistributedSystem.CreationStackGenerator;
import com.gemstone.gemfire.distributed.internal.membership.jgroup.JGroupMembershipManager;
import com.gemstone.gemfire.distributed.internal.membership.jgroup.MembershipManagerHelper;
import com.gemstone.gemfire.internal.AvailablePort;
import com.gemstone.gemfire.internal.InternalDataSerializer;
import com.gemstone.gemfire.internal.InternalInstantiator;
import com.gemstone.gemfire.internal.OSProcess;
import com.gemstone.gemfire.internal.SocketCreator;
import com.gemstone.gemfire.internal.admin.ClientStatsManager;
import com.gemstone.gemfire.internal.cache.GemFireCacheImpl;
import com.gemstone.gemfire.internal.cache.InitialImageOperation;
import com.gemstone.gemfire.internal.cache.tier.InternalBridgeMembership;
import com.gemstone.gemfire.internal.cache.tier.sockets.CacheServerTestUtil;
import com.gemstone.gemfire.internal.cache.tier.sockets.ClientProxyMembershipID;
import com.gemstone.gemfire.internal.cache.tier.sockets.DataSerializerPropogationDUnitTest;
import com.gemstone.gemfire.internal.logging.InternalLogWriter;
import com.gemstone.gemfire.internal.logging.LocalLogWriter;
import com.gemstone.gemfire.internal.logging.LogService;
import com.gemstone.gemfire.internal.logging.LogWriterFactory;
import com.gemstone.gemfire.internal.logging.LogWriterImpl;
import com.gemstone.gemfire.internal.logging.ManagerLogWriter;
import com.gemstone.gemfire.internal.logging.log4j.LogWriterLogger;
import com.gemstone.gemfire.management.internal.cli.LogWrapper;
import com.gemstone.org.jgroups.Event;
import com.gemstone.org.jgroups.JChannel;
import com.gemstone.org.jgroups.stack.IpAddress;
import com.gemstone.org.jgroups.stack.Protocol;
import com.gemstone.org.jgroups.util.GemFireTracer;
import dunit.standalone.DUnitLauncher;
/**
* This class is the superclass of all distributed unit tests.
*
* tests/hydra/JUnitTestTask is the main DUnit driver. It supports two
* additional public static methods if they are defined in the test case:
*
* public static void caseSetUp() -- comparable to JUnit's BeforeClass annotation
*
* public static void caseTearDown() -- comparable to JUnit's AfterClass annotation
*
* @author David Whitlock
*/
@SuppressWarnings("serial")
public abstract class DistributedTestCase extends TestCase implements java.io.Serializable {
private static final Logger logger = LogService.getLogger();
private static final LogWriterLogger oldLogger = LogWriterLogger.create(logger);
private static void setUpCreationStackGenerator() {
// the following is moved from InternalDistributedSystem to fix #51058
InternalDistributedSystem.TEST_CREATION_STACK_GENERATOR.set(
new CreationStackGenerator() {
@Override
public Throwable generateCreationStack(final DistributionConfig config) {
final StringBuilder sb = new StringBuilder();
final String[] validAttributeNames = config.getAttributeNames();
for (int i = 0; i < validAttributeNames.length; i++) {
final String attName = validAttributeNames[i];
final Object actualAtt = config.getAttributeObject(attName);
String actualAttStr = actualAtt.toString();
sb.append(" ");
sb.append(attName);
sb.append("=\"");
if (actualAtt.getClass().isArray()) {
actualAttStr = InternalDistributedSystem.arrayToString(actualAtt);
}
sb.append(actualAttStr);
sb.append("\"");
sb.append("\n");
}
return new Throwable("Creating distributed system with the following configuration:\n" + sb.toString());
}
});
}
private static void tearDownCreationStackGenerator() {
InternalDistributedSystem.TEST_CREATION_STACK_GENERATOR.set(InternalDistributedSystem.DEFAULT_CREATION_STACK_GENERATOR);
}
/** This VM's connection to the distributed system */
public static InternalDistributedSystem system;
private static Class lastSystemCreatedInTest;
private static Properties lastSystemProperties;
public static volatile String testName;
private static ConcurrentLinkedQueue<ExpectedException> expectedExceptions = new ConcurrentLinkedQueue<ExpectedException>();
/** For formatting timing info */
private static final DecimalFormat format = new DecimalFormat("###.###");
public static boolean reconnect = false;
public static final boolean logPerTest = Boolean.getBoolean("dunitLogPerTest");
/////////////////////// Utility Methods ///////////////////////
public void attachDebugger(VM vm, final String msg) {
vm.invoke(new SerializableRunnable("Attach Debugger") {
public void run() {
com.gemstone.gemfire.internal.util.DebuggerSupport.
waitForJavaDebugger(getSystem().getLogWriter().convertToLogWriterI18n(), msg);
}
});
}
/**
* Invokes a <code>SerializableRunnable</code> in every VM that
* DUnit knows about.
*
* @see VM#invoke(Runnable)
*/
public static void invokeInEveryVM(SerializableRunnable work) {
for (int h = 0; h < Host.getHostCount(); h++) {
Host host = Host.getHost(h);
for (int v = 0; v < host.getVMCount(); v++) {
VM vm = host.getVM(v);
vm.invoke(work);
}
}
}
public static void invokeInLocator(SerializableRunnable work) {
Host.getLocator().invoke(work);
}
/**
* Invokes a <code>SerializableCallable</code> in every VM that
* DUnit knows about.
*
* @return a Map of results, where the key is the VM and the value is the result
* @see VM#invoke(Callable)
*/
protected static Map invokeInEveryVM(SerializableCallable work) {
HashMap ret = new HashMap();
for (int h = 0; h < Host.getHostCount(); h++) {
Host host = Host.getHost(h);
for (int v = 0; v < host.getVMCount(); v++) {
VM vm = host.getVM(v);
ret.put(vm, vm.invoke(work));
}
}
return ret;
}
/**
* Invokes a method in every remote VM that DUnit knows about.
*
* @see VM#invoke(Class, String)
*/
protected static void invokeInEveryVM(Class c, String method) {
for (int h = 0; h < Host.getHostCount(); h++) {
Host host = Host.getHost(h);
for (int v = 0; v < host.getVMCount(); v++) {
VM vm = host.getVM(v);
vm.invoke(c, method);
}
}
}
/**
* Invokes a method in every remote VM that DUnit knows about.
*
* @see VM#invoke(Class, String)
*/
protected static void invokeInEveryVM(Class c, String method, Object[] methodArgs) {
for (int h = 0; h < Host.getHostCount(); h++) {
Host host = Host.getHost(h);
for (int v = 0; v < host.getVMCount(); v++) {
VM vm = host.getVM(v);
vm.invoke(c, method, methodArgs);
}
}
}
/**
* The number of milliseconds to try repeating validation code in the
* event that AssertionFailedError is thrown. For ACK scopes, no
* repeat should be necessary.
*/
protected long getRepeatTimeoutMs() {
return 0;
}
protected void invokeRepeatingIfNecessary(VM vm, RepeatableRunnable task) {
vm.invokeRepeatingIfNecessary(task, getRepeatTimeoutMs());
}
/**
* Invokes a <code>SerializableRunnable</code> in every VM that
* DUnit knows about. If work.run() throws an assertion failure,
* its execution is repeated, until no assertion failure occurs or
* repeatTimeout milliseconds have passed.
*
* @see VM#invoke(Runnable)
*/
protected void invokeInEveryVMRepeatingIfNecessary(RepeatableRunnable work) {
for (int h = 0; h < Host.getHostCount(); h++) {
Host host = Host.getHost(h);
for (int v = 0; v < host.getVMCount(); v++) {
VM vm = host.getVM(v);
vm.invokeRepeatingIfNecessary(work, getRepeatTimeoutMs());
}
}
}
/** Return the total number of VMs on all hosts */
protected static int getVMCount() {
int count = 0;
for (int h = 0; h < Host.getHostCount(); h++) {
Host host = Host.getHost(h);
count += host.getVMCount();
}
return count;
}
/** print a stack dump for this vm
@author bruce
@since 5.0
*/
public static void dumpStack() {
com.gemstone.gemfire.internal.OSProcess.printStacks(0, false);
}
/** print a stack dump for the given vm
@author bruce
@since 5.0
*/
public static void dumpStack(VM vm) {
vm.invoke(dunit.DistributedTestCase.class, "dumpStack");
}
/** print stack dumps for all vms on the given host
@author bruce
@since 5.0
*/
public static void dumpStack(Host host) {
for (int v=0; v < host.getVMCount(); v++) {
host.getVM(v).invoke(dunit.DistributedTestCase.class, "dumpStack");
}
}
/** print stack dumps for all vms
@author bruce
@since 5.0
*/
public static void dumpAllStacks() {
for (int h=0; h < Host.getHostCount(); h++) {
dumpStack(Host.getHost(h));
}
}
public static String noteTiming(long operations, String operationUnit,
long beginTime, long endTime,
String timeUnit)
{
long delta = endTime - beginTime;
StringBuffer sb = new StringBuffer();
sb.append(" Performed ");
sb.append(operations);
sb.append(" ");
sb.append(operationUnit);
sb.append(" in ");
sb.append(delta);
sb.append(" ");
sb.append(timeUnit);
sb.append("\n");
double ratio = ((double) operations) / ((double) delta);
sb.append(" ");
sb.append(format.format(ratio));
sb.append(" ");
sb.append(operationUnit);
sb.append(" per ");
sb.append(timeUnit);
sb.append("\n");
ratio = ((double) delta) / ((double) operations);
sb.append(" ");
sb.append(format.format(ratio));
sb.append(" ");
sb.append(timeUnit);
sb.append(" per ");
sb.append(operationUnit);
sb.append("\n");
return sb.toString();
}
/**
* Creates a new LogWriter and adds it to the config properties. The config
* can then be used to connect to DistributedSystem, thus providing early
* access to the LogWriter before connecting. This call does not connect
* to the DistributedSystem. It simply creates and returns the LogWriter
* that will eventually be used by the DistributedSystem that connects using
* config.
*
* @param config the DistributedSystem config properties to add LogWriter to
* @return early access to the DistributedSystem LogWriter
*/
protected static LogWriter createLogWriter(Properties config) { // TODO:LOG:CONVERT: this is being used for ExpectedExceptions
Properties nonDefault = config;
if (nonDefault == null) {
nonDefault = new Properties();
}
addHydraProperties(nonDefault);
DistributionConfig dc = new DistributionConfigImpl(nonDefault);
LogWriter logger = LogWriterFactory.createLogWriterLogger(
false/*isLoner*/, false/*isSecurityLog*/, dc,
false);
// if config was non-null, then these will be added to it...
nonDefault.put(DistributionConfig.LOG_WRITER_NAME, logger);
return logger;
}
/**
* Fetches the GemFireDescription for this test and adds its
* DistributedSystem properties to the provided props parameter.
*
* @param config the properties to add hydra's test properties to
*/
protected static void addHydraProperties(Properties config) {
Properties p = DUnitEnv.get().getDistributedSystemProperties();
for (Iterator iter = p.entrySet().iterator();
iter.hasNext(); ) {
Map.Entry entry = (Map.Entry) iter.next();
String key = (String) entry.getKey();
String value = (String) entry.getValue();
if (config.getProperty(key) == null) {
config.setProperty(key, value);
}
}
}
//////////////////////// Constructors ////////////////////////
/**
* Creates a new <code>DistributedTestCase</code> test with the
* given name.
*/
public DistributedTestCase(String name) {
super(name);
DUnitLauncher.launchIfNeeded();
}
/////////////////////// Instance Methods ///////////////////////
protected Class getTestClass() {
Class clazz = getClass();
while (clazz.getDeclaringClass() != null) {
clazz = clazz.getDeclaringClass();
}
return clazz;
}
/**
* This finds the log level configured for the test run. It should be used
* when creating a new distributed system if you want to specify a log level.
* @return the dunit log-level setting
*/
public static String getDUnitLogLevel() {
Properties p = DUnitEnv.get().getDistributedSystemProperties();
String result = p.getProperty(DistributionConfig.LOG_LEVEL_NAME);
if (result == null) {
result = ManagerLogWriter.levelToString(DistributionConfig.DEFAULT_LOG_LEVEL);
}
return result;
}
public final static Properties getAllDistributedSystemProperties(Properties props) {
Properties p = DUnitEnv.get().getDistributedSystemProperties();
// our tests do not expect auto-reconnect to be on by default
if (!p.contains(DistributionConfig.DISABLE_AUTO_RECONNECT_NAME)) {
p.put(DistributionConfig.DISABLE_AUTO_RECONNECT_NAME, "true");
}
for (Iterator iter = props.entrySet().iterator();
iter.hasNext(); ) {
Map.Entry entry = (Map.Entry) iter.next();
String key = (String) entry.getKey();
Object value = entry.getValue();
p.put(key, value);
}
return p;
}
public void setSystem(Properties props, DistributedSystem ds) {
system = (InternalDistributedSystem)ds;
lastSystemProperties = props;
lastSystemCreatedInTest = getTestClass();
}
/**
* Returns this VM's connection to the distributed system. If
* necessary, the connection will be lazily created using the given
* <code>Properties</code>. Note that this method uses hydra's
* configuration to determine the location of log files, etc.
* Note: "final" was removed so that WANTestBase can override this method.
* This was part of the xd offheap merge.
*
* @see hydra.DistributedConnectionMgr#connect
* @since 3.0
*/
public /*final*/ InternalDistributedSystem getSystem(Properties props) {
// Setting the default disk store name is now done in setUp
if (system == null) {
system = InternalDistributedSystem.getAnyInstance();
}
if (system == null || !system.isConnected()) {
// Figure out our distributed system properties
Properties p = getAllDistributedSystemProperties(props);
lastSystemCreatedInTest = getTestClass();
if (logPerTest) {
String testMethod = getTestName();
String testName = lastSystemCreatedInTest.getName() + '-' + testMethod;
String oldLogFile = p.getProperty(DistributionConfig.LOG_FILE_NAME);
p.put(DistributionConfig.LOG_FILE_NAME,
oldLogFile.replace("system.log", testName+".log"));
String oldStatFile = p.getProperty(DistributionConfig.STATISTIC_ARCHIVE_FILE_NAME);
p.put(DistributionConfig.STATISTIC_ARCHIVE_FILE_NAME,
oldStatFile.replace("statArchive.gfs", testName+".gfs"));
}
system = (InternalDistributedSystem)DistributedSystem.connect(p);
lastSystemProperties = p;
} else {
boolean needNewSystem = false;
if(!getTestClass().equals(lastSystemCreatedInTest)) {
Properties newProps = getAllDistributedSystemProperties(props);
needNewSystem = !newProps.equals(lastSystemProperties);
if(needNewSystem) {
getLogWriter().info(
"Test class has changed and the new DS properties are not an exact match. "
+ "Forcing DS disconnect. Old props = "
+ lastSystemProperties + "new props=" + newProps);
}
} else {
Properties activeProps = system.getProperties();
for (Iterator iter = props.entrySet().iterator();
iter.hasNext(); ) {
Map.Entry entry = (Map.Entry) iter.next();
String key = (String) entry.getKey();
String value = (String) entry.getValue();
if (!value.equals(activeProps.getProperty(key))) {
needNewSystem = true;
getLogWriter().info("Forcing DS disconnect. For property " + key
+ " old value = " + activeProps.getProperty(key)
+ " new value = " + value);
break;
}
}
}
if(needNewSystem) {
// the current system does not meet our needs to disconnect and
// call recursively to get a new system.
getLogWriter().info("Disconnecting from current DS in order to make a new one");
disconnectFromDS();
getSystem(props);
}
}
return system;
}
/**
* Crash the cache in the given VM in such a way that it immediately stops communicating with
* peers. This forces the VM's membership manager to throw a ForcedDisconnectException by
* forcibly terminating the JGroups protocol stack with a fake EXIT event.<p>
*
* NOTE: if you use this method be sure that you clean up the VM before the end of your
* test with disconnectFromDS() or disconnectAllFromDS().
*/
public boolean crashDistributedSystem(VM vm) {
return (Boolean)vm.invoke(new SerializableCallable("crash distributed system") {
public Object call() throws Exception {
DistributedSystem msys = InternalDistributedSystem.getAnyInstance();
crashDistributedSystem(msys);
return true;
}
});
}
/**
* Crash the cache in the given VM in such a way that it immediately stops communicating with
* peers. This forces the VM's membership manager to throw a ForcedDisconnectException by
* forcibly terminating the JGroups protocol stack with a fake EXIT event.<p>
*
* NOTE: if you use this method be sure that you clean up the VM before the end of your
* test with disconnectFromDS() or disconnectAllFromDS().
*/
public void crashDistributedSystem(final DistributedSystem msys) {
MembershipManagerHelper.inhibitForcedDisconnectLogging(true);
MembershipManagerHelper.playDead(msys);
JChannel c = MembershipManagerHelper.getJChannel(msys);
Protocol udp = c.getProtocolStack().findProtocol("UDP");
udp.stop();
udp.passUp(new Event(Event.EXIT, new RuntimeException("killing member's ds")));
try {
MembershipManagerHelper.getJChannel(msys).waitForClose();
}
catch (InterruptedException ie) {
Thread.currentThread().interrupt();
// attempt rest of work with interrupt bit set
}
MembershipManagerHelper.inhibitForcedDisconnectLogging(false);
WaitCriterion wc = new WaitCriterion() {
public boolean done() {
return !msys.isConnected();
}
public String description() {
return "waiting for distributed system to finish disconnecting: " + msys;
}
};
// try {
waitForCriterion(wc, 10000, 1000, true);
// } finally {
// dumpMyThreads(getLogWriter());
// }
}
private String getDefaultDiskStoreName() {
String vmid = System.getProperty("vmid");
return "DiskStore-" + vmid + "-"+ getTestClass().getCanonicalName() + "." + getTestName();
}
/**
* Returns this VM's connection to the distributed system. If
* necessary, the connection will be lazily created using the
* <code>Properties</code> returned by {@link
* #getDistributedSystemProperties}.
*
* @see #getSystem(Properties)
*
* @since 3.0
*/
public final InternalDistributedSystem getSystem() {
return getSystem(this.getDistributedSystemProperties());
}
/**
* Returns a loner distributed system that isn't connected to other
* vms
*
* @since 6.5
*/
public final InternalDistributedSystem getLonerSystem() {
Properties props = this.getDistributedSystemProperties();
props.put(DistributionConfig.MCAST_PORT_NAME, "0");
props.put(DistributionConfig.LOCATORS_NAME, "");
return getSystem(props);
}
/**
* Returns a loner distributed system in combination with enforceUniqueHost
* and redundancyZone properties.
* Added specifically to test scenario of defect #47181.
*/
public final InternalDistributedSystem getLonerSystemWithEnforceUniqueHost() {
Properties props = this.getDistributedSystemProperties();
props.put(DistributionConfig.MCAST_PORT_NAME, "0");
props.put(DistributionConfig.LOCATORS_NAME, "");
props.put(DistributionConfig.ENFORCE_UNIQUE_HOST_NAME, "true");
props.put(DistributionConfig.REDUNDANCY_ZONE_NAME, "zone1");
return getSystem(props);
}
/**
* Returns an mcast distributed system that is connected to other
* vms using a random mcast port.
*/
public final InternalDistributedSystem getMcastSystem() {
Properties props = this.getDistributedSystemProperties();
int port = AvailablePort.getRandomAvailablePort(AvailablePort.JGROUPS);
props.put(DistributionConfig.MCAST_PORT_NAME, ""+port);
props.put(DistributionConfig.MCAST_TTL_NAME, "0");
props.put(DistributionConfig.LOCATORS_NAME, "");
return getSystem(props);
}
/**
* Returns an mcast distributed system that is connected to other
* vms using the given mcast port.
*/
public final InternalDistributedSystem getMcastSystem(int jgroupsPort) {
Properties props = this.getDistributedSystemProperties();
props.put(DistributionConfig.MCAST_PORT_NAME, ""+jgroupsPort);
props.put(DistributionConfig.MCAST_TTL_NAME, "0");
props.put(DistributionConfig.LOCATORS_NAME, "");
return getSystem(props);
}
/**
* Returns whether or this VM is connected to a {@link
* DistributedSystem}.
*/
public final boolean isConnectedToDS() {
return system != null && system.isConnected();
}
/**
* Returns a <code>Properties</code> object used to configure a
* connection to a {@link
* com.gemstone.gemfire.distributed.DistributedSystem}.
* Unless overridden, this method will return an empty
* <code>Properties</code> object.
*
* @since 3.0
*/
public Properties getDistributedSystemProperties() {
return new Properties();
}
/**
* Sets up the test (noop).
*/
@Override
public void setUp() throws Exception {
setUpCreationStackGenerator();
testName = getName();
System.setProperty(HoplogConfig.ALLOW_LOCAL_HDFS_PROP, "true");
if (testName != null) {
GemFireCacheImpl.setDefaultDiskStoreName(getDefaultDiskStoreName());
String baseDefaultDiskStoreName = getTestClass().getCanonicalName() + "." + getTestName();
for (int h = 0; h < Host.getHostCount(); h++) {
Host host = Host.getHost(h);
for (int v = 0; v < host.getVMCount(); v++) {
VM vm = host.getVM(v);
String vmDefaultDiskStoreName = "DiskStore-" + h + "-" + v + "-" + baseDefaultDiskStoreName;
vm.invoke(DistributedTestCase.class, "perVMSetUp", new Object[] {testName, vmDefaultDiskStoreName});
}
}
}
System.out.println("\n\n[setup] START TEST " + getClass().getSimpleName()+"."+testName+"\n\n");
}
public static void perVMSetUp(String name, String defaultDiskStoreName) {
setTestName(name);
GemFireCacheImpl.setDefaultDiskStoreName(defaultDiskStoreName);
System.setProperty(HoplogConfig.ALLOW_LOCAL_HDFS_PROP, "true");
}
public static void setTestName(String name) {
testName = name;
}
public static String getTestName() {
return testName;
}
/**
* For logPerTest to work, we have to disconnect from the DS, but all
* subclasses do not call super.tearDown(). To prevent this scenario
* this method has been declared final. Subclasses must now override
* {@link #tearDown2()} instead.
* @throws Exception
*/
@Override
public final void tearDown() throws Exception {
tearDownCreationStackGenerator();
tearDown2();
realTearDown();
tearDownAfter();
}
/**
* Tears down the test. This method is called by the final {@link #tearDown()} method and should be overridden to
* perform actual test cleanup and release resources used by the test. The tasks executed by this method are
* performed before the DUnit test framework using Hydra cleans up the client VMs.
* <p/>
* @throws Exception if the tear down process and test cleanup fails.
* @see #tearDown
* @see #tearDownAfter()
*/
// TODO rename this method to tearDownBefore and change the access modifier to protected!
public void tearDown2() throws Exception {
}
protected void realTearDown() throws Exception {
if (logPerTest) {
disconnectFromDS();
invokeInEveryVM(DistributedTestCase.class, "disconnectFromDS");
}
cleanupAllVms();
}
/**
* Tears down the test. Performs additional tear down tasks after the DUnit tests framework using Hydra cleans up
* the client VMs. This method is called by the final {@link #tearDown()} method and should be overridden to perform
* post tear down activities.
* <p/>
* @throws Exception if the test tear down process fails.
* @see #tearDown()
* @see #tearDown2()
*/
protected void tearDownAfter() throws Exception {
}
public static void cleanupAllVms()
{
cleanupThisVM();
invokeInEveryVM(DistributedTestCase.class, "cleanupThisVM");
invokeInLocator(new SerializableRunnable() {
public void run() {
DistributionMessageObserver.setInstance(null);
unregisterInstantiatorsInThisVM();
}
});
DUnitLauncher.closeAndCheckForSuspects();
}
private static void cleanupThisVM() {
IpAddress.resolve_dns = true;
SocketCreator.resolve_dns = true;
InitialImageOperation.slowImageProcessing = 0;
DistributionMessageObserver.setInstance(null);
QueryTestUtils.setCache(null);
CacheServerTestUtil.clearCacheReference();
RegionTestCase.preSnapshotRegion = null;
GlobalLockingDUnitTest.region_testBug32356 = null;
LogWrapper.close();
ClientProxyMembershipID.system = null;
MultiVMRegionTestCase.CCRegion = null;
InternalBridgeMembership.unregisterAllListeners();
ClientStatsManager.cleanupForTests();
unregisterInstantiatorsInThisVM();
GemFireTracer.DEBUG = Boolean.getBoolean("DistributionManager.DEBUG_JAVAGROUPS");
Protocol.trace = GemFireTracer.DEBUG;
DistributionMessageObserver.setInstance(null);
QueryObserverHolder.reset();
if (InternalDistributedSystem.systemAttemptingReconnect != null) {
InternalDistributedSystem.systemAttemptingReconnect.stopReconnecting();
}
ExpectedException ex;
while((ex = expectedExceptions.poll()) != null) {
ex.remove();
}
}
public static void unregisterAllDataSerializersFromAllVms()
{
unregisterDataSerializerInThisVM();
invokeInEveryVM(new SerializableRunnable() {
public void run() {
unregisterDataSerializerInThisVM();
}
});
invokeInLocator(new SerializableRunnable() {
public void run() {
unregisterDataSerializerInThisVM();
}
});
}
public static void unregisterInstantiatorsInThisVM() {
// unregister all the instantiators
InternalInstantiator.reinitialize();
assertEquals(0, InternalInstantiator.getInstantiators().length);
}
public static void unregisterDataSerializerInThisVM()
{
DataSerializerPropogationDUnitTest.successfullyLoadedTestDataSerializer = false;
// unregister all the Dataserializers
InternalDataSerializer.reinitialize();
// ensure that all are unregistered
assertEquals(0, InternalDataSerializer.getSerializers().length);
}
protected static void disconnectAllFromDS() {
disconnectFromDS();
invokeInEveryVM(DistributedTestCase.class,
"disconnectFromDS");
}
/**
* Disconnects this VM from the distributed system
*/
public static void disconnectFromDS() {
testName = null;
GemFireCacheImpl.testCacheXml = null;
if (system != null) {
system.disconnect();
system = null;
}
for (;;) {
DistributedSystem ds = InternalDistributedSystem.getConnectedInstance();
if (ds == null) {
break;
}
try {
ds.disconnect();
}
catch (Exception e) {
// ignore
}
}
{
AdminDistributedSystemImpl ads =
AdminDistributedSystemImpl.getConnectedInstance();
if (ads != null) {// && ads.isConnected()) {
ads.disconnect();
}
}
}
/**
* Strip the package off and gives just the class name.
* Needed because of Windows file name limits.
*/
private String getShortClassName() {
String result = this.getClass().getName();
int idx = result.lastIndexOf('.');
if (idx != -1) {
result = result.substring(idx+1);
}
return result;
}
/** get the host name to use for a server cache in client/server dunit
* testing
* @param host
* @return the host name
*/
public static String getServerHostName(Host host) {
return System.getProperty("gemfire.server-bind-address") != null?
System.getProperty("gemfire.server-bind-address")
: host.getHostName();
}
/** get the IP literal name for the current host, use this instead of
* "localhost" to avoid IPv6 name resolution bugs in the JDK/machine config.
* @return an ip literal, this method honors java.net.preferIPvAddresses
*/
public static String getIPLiteral() {
try {
return SocketCreator.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
throw new Error("problem determining host IP address", e);
}
}
/**
* Get the port that the standard dunit locator is listening on.
* @return
*/
public static int getDUnitLocatorPort() {
return DUnitEnv.get().getLocatorPort();
}
/**
* Returns a unique name for this test method. It is based on the
* name of the class as well as the name of the method.
*/
public String getUniqueName() {
return getShortClassName() + "_" + this.getName();
}
/**
* Returns a <code>LogWriter</code> for logging information
* @deprecated Use a static logger from the log4j2 LogService.getLogger instead.
*/
@Deprecated
public static InternalLogWriter getLogWriter() {
return oldLogger;
}
/**
* Helper method that causes this test to fail because of the given
* exception.
*/
public static void fail(String message, Throwable ex) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw, true);
pw.print(message);
pw.print(": ");
ex.printStackTrace(pw);
fail(sw.toString());
}
// utility methods
/** pause for a default interval */
protected void pause() {
pause(250);
}
/**
* Use of this function indicates a place in the tests tree where t
* he use of Thread.sleep() is
* highly questionable.
* <p>
* Some places in the system, especially those that test expirations and other
* timeouts, have a very good reason to call {@link Thread#sleep(long)}. The
* <em>other</em> places are marked by the use of this method.
*
* @param ms
*/
static public final void staticPause(int ms) {
// getLogWriter().info("FIXME: Pausing for " + ms + " ms..."/*, new Exception()*/);
final long target = System.currentTimeMillis() + ms;
try {
for (;;) {
long msLeft = target - System.currentTimeMillis();
if (msLeft <= 0) {
break;
}
Thread.sleep(msLeft);
}
}
catch (InterruptedException e) {
fail("interrupted", e);
}
}
/** pause for specified ms interval
* Make sure system clock has advanced by the specified number of millis before
* returning.
*/
public static final void pause(int ms) {
getLogWriter().info("Pausing for " + ms + " ms..."/*, new Exception()*/);
final long target = System.currentTimeMillis() + ms;
try {
for (;;) {
long msLeft = target - System.currentTimeMillis();
if (msLeft <= 0) {
break;
}
Thread.sleep(msLeft);
}
}
catch (InterruptedException e) {
fail("interrupted", e);
}
}
public interface WaitCriterion {
public boolean done();
public String description();
}
public interface WaitCriterion2 extends WaitCriterion {
/**
* If this method returns true then quit waiting even if we are not done.
* This allows a wait to fail early.
*/
public boolean stopWaiting();
}
/**
* If true, we randomize the amount of time we wait before polling a
* {@link WaitCriterion}.
*/
static private final boolean USE_JITTER = true;
static private final Random jitter = new Random();
/**
* Return a jittered interval up to a maximum of <code>ms</code>
* milliseconds, inclusive.
*
* The result is bounded by 50 ms as a minimum and 5000 ms as a maximum.
*
* @param ms total amount of time to wait
* @return randomized interval we should wait
*/
private static int jitterInterval(long ms) {
final int minLegal = 50;
final int maxLegal = 5000;
if (ms <= minLegal) {
return (int)ms; // Don't ever jitter anything below this.
}
int maxReturn = maxLegal;
if (ms < maxLegal) {
maxReturn = (int)ms;
}
return minLegal + jitter.nextInt(maxReturn - minLegal + 1);
}
/**
* Wait until given criterion is met
* @param ev criterion to wait on
* @param ms total time to wait, in milliseconds
* @param interval pause interval between waits
* @param throwOnTimeout if false, don't generate an error
*/
static public void waitForCriterion(WaitCriterion ev, long ms,
long interval, boolean throwOnTimeout) {
long waitThisTime;
if (USE_JITTER) {
waitThisTime = jitterInterval(interval);
}
else {
waitThisTime = interval;
}
final long tilt = System.currentTimeMillis() + ms;
for (;;) {
// getLogWriter().info("Testing to see if event has occurred: " + ev.description());
if (ev.done()) {
return; // success
}
if (ev instanceof WaitCriterion2) {
WaitCriterion2 ev2 = (WaitCriterion2)ev;
if (ev2.stopWaiting()) {
if (throwOnTimeout) {
fail("stopWaiting returned true: " + ev.description());
}
return;
}
}
// Calculate time left
long timeLeft = tilt - System.currentTimeMillis();
if (timeLeft <= 0) {
if (!throwOnTimeout) {
return; // not an error, but we're done
}
fail("Event never occurred after " + ms + " ms: " + ev.description());
}
if (waitThisTime > timeLeft) {
waitThisTime = timeLeft;
}
// Wait a little bit
Thread.yield();
try {
// getLogWriter().info("waiting " + waitThisTime + "ms for " + ev.description());
Thread.sleep(waitThisTime);
} catch (InterruptedException e) {
fail("interrupted");
}
}
}
/**
* Wait on a mutex. This is done in a loop in order to address the
* "spurious wakeup" "feature" in Java.
* @param ev condition to test
* @param mutex object to lock and wait on
* @param ms total amount of time to wait
* @param interval interval to pause for the wait
* @param throwOnTimeout if false, no error is thrown.
*/
static public void waitMutex(WaitCriterion ev, Object mutex, long ms,
long interval, boolean throwOnTimeout) {
final long tilt = System.currentTimeMillis() + ms;
long waitThisTime;
if (USE_JITTER) {
waitThisTime = jitterInterval(interval);
}
else {
waitThisTime = interval;
}
synchronized (mutex) {
for (;;) {
if (ev.done()) {
break;
}
long timeLeft = tilt - System.currentTimeMillis();
if (timeLeft <= 0) {
if (!throwOnTimeout) {
return; // not an error, but we're done
}
fail("Event never occurred after " + ms + " ms: " + ev.description());
}
if (waitThisTime > timeLeft) {
waitThisTime = timeLeft;
}
try {
mutex.wait(waitThisTime);
} catch (InterruptedException e) {
fail("interrupted");
}
} // for
} // synchronized
}
/**
* Wait for a thread to join
* @param t thread to wait on
* @param ms maximum time to wait
* @throws AssertionFailure if the thread does not terminate
*/
static public void join(Thread t, long ms, LogWriter logger) {
final long tilt = System.currentTimeMillis() + ms;
final long incrementalWait;
if (USE_JITTER) {
incrementalWait = jitterInterval(ms);
}
else {
incrementalWait = ms; // wait entire time, no looping.
}
final long start = System.currentTimeMillis();
for (;;) {
// I really do *not* understand why this check is necessary
// but it is, at least with JDK 1.6. According to the source code
// and the javadocs, one would think that join() would exit immediately
// if the thread is dead. However, I can tell you from experimentation
// that this is not the case. :-( djp 2008-12-08
if (!t.isAlive()) {
break;
}
try {
t.join(incrementalWait);
} catch (InterruptedException e) {
fail("interrupted");
}
if (System.currentTimeMillis() >= tilt) {
break;
}
} // for
if (logger == null) {
logger = new LocalLogWriter(LogWriterImpl.INFO_LEVEL, System.out);
}
if (t.isAlive()) {
logger.info("HUNG THREAD");
dumpStackTrace(t, t.getStackTrace(), logger);
dumpMyThreads(logger);
t.interrupt(); // We're in trouble!
fail("Thread did not terminate after " + ms + " ms: " + t);
// getLogWriter().warning("Thread did not terminate"
// /* , new Exception()*/
// );
}
long elapsedMs = (System.currentTimeMillis() - start);
if (elapsedMs > 0) {
String msg = "Thread " + t + " took "
+ elapsedMs
+ " ms to exit.";
logger.info(msg);
}
}
public static void dumpStackTrace(Thread t, StackTraceElement[] stack, LogWriter logger) {
StringBuilder msg = new StringBuilder();
msg.append("Thread=<")
.append(t)
.append("> stackDump:\n");
for (int i=0; i < stack.length; i++) {
msg.append("\t")
.append(stack[i])
.append("\n");
}
logger.info(msg.toString());
}
/**
* Dump all thread stacks
*/
public static void dumpMyThreads(LogWriter logger) {
OSProcess.printStacks(0, false);
}
/**
* A class that represents an currently logged expected exception, which
* should be removed
*
* @author Mitch Thomas
* @since 5.7bugfix
*/
public static class ExpectedException implements Serializable {
private static final long serialVersionUID = 1L;
final String ex;
final transient VM v;
public ExpectedException(String exception) {
this.ex = exception;
this.v = null;
}
ExpectedException(String exception, VM vm) {
this.ex = exception;
this.v = vm;
}
public String getRemoveString() {
return "<ExpectedException action=remove>" + ex + "</ExpectedException>";
}
public String getAddString() {
return "<ExpectedException action=add>" + ex + "</ExpectedException>";
}
public void remove() {
SerializableRunnable removeRunnable = new SerializableRunnable(
"removeExpectedExceptions") {
public void run() {
final String remove = getRemoveString();
final InternalDistributedSystem sys = InternalDistributedSystem
.getConnectedInstance();
if (sys != null) {
sys.getLogWriter().info(remove);
}
try {
getLogWriter().info(remove);
} catch (Exception noHydraLogger) {
}
logger.info(remove);
}
};
if (this.v != null) {
v.invoke(removeRunnable);
}
else {
invokeInEveryVM(removeRunnable);
}
String s = getRemoveString();
LogManager.getLogger(LogService.BASE_LOGGER_NAME).info(s);
// log it locally
final InternalDistributedSystem sys = InternalDistributedSystem
.getConnectedInstance();
if (sys != null) { // avoid creating a system
sys.getLogWriter().info(s);
}
getLogWriter().info(s);
}
}
/**
* Log in all VMs, in both the test logger and the GemFire logger the
* expected exception string to prevent grep logs from complaining. The
* expected string is used by the GrepLogs utility and so can contain
* regular expression characters.
*
* If you do not remove the expected exception, it will be removed at the
* end of your test case automatically.
*
* @since 5.7bugfix
* @param exception
* the exception string to expect
* @return an ExpectedException instance for removal
*/
public static ExpectedException addExpectedException(final String exception) {
return addExpectedException(exception, null);
}
/**
* Log in all VMs, in both the test logger and the GemFire logger the
* expected exception string to prevent grep logs from complaining. The
* expected string is used by the GrepLogs utility and so can contain
* regular expression characters.
*
* @since 5.7bugfix
* @param exception
* the exception string to expect
* @param v
* the VM on which to log the expected exception or null for all VMs
* @return an ExpectedException instance for removal purposes
*/
public static ExpectedException addExpectedException(final String exception,
VM v) {
final ExpectedException ret;
if (v != null) {
ret = new ExpectedException(exception, v);
}
else {
ret = new ExpectedException(exception);
}
// define the add and remove expected exceptions
final String add = ret.getAddString();
SerializableRunnable addRunnable = new SerializableRunnable(
"addExpectedExceptions") {
public void run() {
final InternalDistributedSystem sys = InternalDistributedSystem
.getConnectedInstance();
if (sys != null) {
sys.getLogWriter().info(add);
}
try {
getLogWriter().info(add);
} catch (Exception noHydraLogger) {
}
logger.info(add);
}
};
if (v != null) {
v.invoke(addRunnable);
}
else {
invokeInEveryVM(addRunnable);
}
LogManager.getLogger(LogService.BASE_LOGGER_NAME).info(add);
// Log it locally too
final InternalDistributedSystem sys = InternalDistributedSystem
.getConnectedInstance();
if (sys != null) { // avoid creating a cache
sys.getLogWriter().info(add);
}
getLogWriter().info(add);
expectedExceptions.add(ret);
return ret;
}
/**
* delete locator state files. Use this after getting a random port
* to ensure that an old locator state file isn't picked up by the
* new locator you're starting.
* @param ports
*/
public void deleteLocatorStateFile(int... ports) {
for (int i=0; i<ports.length; i++) {
File stateFile = new File("locator"+ports[i]+"state.dat");
if (stateFile.exists()) {
stateFile.delete();
}
}
}
}