blob: 950d8db5190430624a1116d853e4b4a24bbbef96 [file] [log] [blame]
/*
* Copyright (c) 2007-2015 Eike Stepper (Berlin, Germany) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Eike Stepper - initial API and implementation
*/
package org.eclipse.net4j.util.tests;
import org.eclipse.net4j.internal.util.test.TestExecuter;
import org.eclipse.net4j.tests.bundle.OM;
import org.eclipse.net4j.util.ReflectUtil;
import org.eclipse.net4j.util.concurrent.ConcurrencyUtil;
import org.eclipse.net4j.util.concurrent.TrackableTimerTask;
import org.eclipse.net4j.util.event.EventUtil;
import org.eclipse.net4j.util.event.IEvent;
import org.eclipse.net4j.util.event.IListener;
import org.eclipse.net4j.util.event.INotifier;
import org.eclipse.net4j.util.io.IORuntimeException;
import org.eclipse.net4j.util.io.IOUtil;
import org.eclipse.net4j.util.io.TMPUtil;
import org.eclipse.net4j.util.lifecycle.ILifecycle;
import org.eclipse.net4j.util.lifecycle.LifecycleEventAdapter;
import org.eclipse.net4j.util.lifecycle.LifecycleUtil;
import org.eclipse.net4j.util.om.OMPlatform;
import org.eclipse.net4j.util.om.log.FileLogHandler;
import org.eclipse.net4j.util.om.log.OMLogger;
import org.eclipse.net4j.util.om.log.OMLogger.Level;
import org.eclipse.net4j.util.om.log.PrintLogHandler;
import org.eclipse.net4j.util.om.trace.ContextTracer;
import org.eclipse.net4j.util.om.trace.PrintTraceHandler;
import org.junit.Assert;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import junit.framework.TestResult;
/**
* @author Eike Stepper
*/
public abstract class AbstractOMTest extends TestCase
{
/**
* Timeout duration in millseconds if timeout <b>is not</b> expected.
*/
public static final long DEFAULT_TIMEOUT = 15 * 1000;
/**
* Timeout duration in millseconds if timeout <b>is</b> expected.
*/
public static final long DEFAULT_TIMEOUT_EXPECTED = 3 * 1000;
public static boolean EXTERNAL_LOG;
public static boolean SUPPRESS_OUTPUT;
private static final IListener DUMPER = new IListener()
{
public void notifyEvent(IEvent event)
{
IOUtil.OUT().println(event);
}
};
private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, AbstractOMTest.class);
private static boolean consoleEnabled;
private static String testName;
private transient List<File> filesToDelete = new ArrayList<File>();
private transient String codeLink;
static
{
try
{
if (EXTERNAL_LOG)
{
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
String prefix = AbstractOMTest.class.getName() + "-" + formatter.format(new Date()) + "-";
File logFile = TMPUtil.createTempFile(prefix, ".log");
OMPlatform.INSTANCE.addLogHandler(new FileLogHandler(logFile, OMLogger.Level.WARN)
{
@Override
protected void writeLog(OMLogger logger, Level level, String msg, Throwable t) throws Throwable
{
super.writeLog(logger, level, "--> " + testName + "\n" + msg, t);
}
});
IOUtil.ERR().println("Logging errors and warnings to " + logFile);
IOUtil.ERR().println();
}
}
catch (Throwable ex)
{
IOUtil.print(ex);
}
}
protected AbstractOMTest()
{
}
public String getCodeLink()
{
return codeLink;
}
public void determineCodeLink()
{
if (codeLink == null)
{
codeLink = determineCodeLink(getName());
if (codeLink == null)
{
codeLink = determineCodeLink("doSetUp");
if (codeLink == null)
{
codeLink = getClass().getName() + "." + getName() + "(" + getClass().getSimpleName() + ".java:1)";
}
}
}
}
protected String determineCodeLink(String methodName)
{
String className = getClass().getName();
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
for (StackTraceElement frame : stackTrace)
{
if (frame.getClassName().equals(className) && frame.getMethodName().equals(methodName))
{
return frame.toString();
}
}
return null;
}
protected boolean logSetUpAndTearDown()
{
return false;
}
@Override
public void setUp() throws Exception
{
testName = getClass().getName() + "." + getName() + "()";
codeLink = null;
PrintTraceHandler.CONSOLE.setShortContext(true);
OMPlatform.INSTANCE.addTraceHandler(PrintTraceHandler.CONSOLE);
OMPlatform.INSTANCE.addLogHandler(PrintLogHandler.CONSOLE);
enableConsole();
if (!SUPPRESS_OUTPUT)
{
IOUtil.OUT().println("*******************************************************"); //$NON-NLS-1$
Thread.yield();
Thread.sleep(2L);
IOUtil.ERR().println(this);
Thread.yield();
Thread.sleep(2L);
IOUtil.OUT().println("*******************************************************"); //$NON-NLS-1$
}
if (!logSetUpAndTearDown())
{
disableConsole();
}
super.setUp();
doSetUp();
if (!SUPPRESS_OUTPUT && logSetUpAndTearDown())
{
IOUtil.OUT().println();
IOUtil.OUT().println("------------------------ START ------------------------"); //$NON-NLS-1$
}
enableConsole();
}
@Override
public void tearDown() throws Exception
{
if (logSetUpAndTearDown())
{
enableConsole();
}
else
{
disableConsole();
}
if (!SUPPRESS_OUTPUT && logSetUpAndTearDown())
{
IOUtil.OUT().println("------------------------- END -------------------------"); //$NON-NLS-1$
IOUtil.OUT().println();
}
try
{
doTearDown();
}
catch (Exception ex)
{
IOUtil.print(ex);
}
try
{
super.tearDown();
}
catch (Exception ex)
{
IOUtil.print(ex);
}
try
{
TrackableTimerTask.logConstructionStackTraces(2 * DEFAULT_TIMEOUT);
}
catch (Exception ex)
{
IOUtil.print(ex);
}
try
{
clearReferences(getClass());
}
catch (Exception ex)
{
IOUtil.print(ex);
}
if (!SUPPRESS_OUTPUT)
{
IOUtil.OUT().println();
IOUtil.OUT().println();
}
}
protected void clearReferences(Class<?> c)
{
if (c != AbstractOMTest.class)
{
for (Field field : c.getDeclaredFields())
{
if (Modifier.isStatic(field.getModifiers()))
{
continue;
}
if (field.getType().isPrimitive())
{
continue;
}
ReflectUtil.setValue(field, this, null);
}
clearReferences(c.getSuperclass());
}
}
@Override
public void runBare() throws Throwable
{
TestExecuter.execute(this, new TestExecuter.Executable()
{
public void execute() throws Throwable
{
try
{
Throwable exception = null;
try
{
setUp();
// Don't call super.runBare() because it does not clean up after exceptions from setUp()
runTest();
}
catch (Throwable running)
{
exception = running;
}
finally
{
try
{
tearDown();
}
catch (Throwable tearingDown)
{
if (exception == null)
{
exception = tearingDown;
}
}
}
if (exception != null)
{
throw exception;
}
}
catch (SkipTestException ex)
{
OM.LOG.info("Skipped " + this); //$NON-NLS-1$
}
catch (Throwable t)
{
// AssertionFailedError assertionFailedError = getAssertionFailedError(t);
// if (assertionFailedError != null)
// {
// t = assertionFailedError;
// }
if (!SUPPRESS_OUTPUT)
{
t.printStackTrace(IOUtil.OUT());
}
throw t;
}
}
});
}
@Override
public void run(TestResult result)
{
try
{
super.run(result);
}
catch (SkipTestException ex)
{
OM.LOG.info("Skipped " + this); //$NON-NLS-1$
}
catch (RuntimeException ex)
{
if (!SUPPRESS_OUTPUT)
{
ex.printStackTrace(IOUtil.OUT());
}
throw ex;
}
catch (Error err)
{
// AssertionFailedError assertionFailedError = getAssertionFailedError(err);
// if (assertionFailedError != null)
// {
// err = assertionFailedError;
// }
if (!SUPPRESS_OUTPUT)
{
err.printStackTrace(IOUtil.OUT());
}
throw err;
}
}
// private AssertionFailedError getAssertionFailedError(Throwable err)
// {
// if (err.getClass() == AssertionError.class)
// {
// // JUnit4 seems to throw java.lang.AssertionError, which the JUNit view displays as error rather than failure
// AssertionFailedError replacementError = new AssertionFailedError(err.getMessage());
// replacementError.initCause(err);
// return replacementError;
// }
//
// if (err instanceof AssertionFailedError)
// {
// return (AssertionFailedError)err;
// }
//
// return null;
// }
protected void enableConsole()
{
if (!SUPPRESS_OUTPUT)
{
OMPlatform.INSTANCE.setDebugging(true);
consoleEnabled = true;
}
}
protected void disableConsole()
{
if (!SUPPRESS_OUTPUT)
{
consoleEnabled = false;
OMPlatform.INSTANCE.setDebugging(false);
// OMPlatform.INSTANCE.removeTraceHandler(PrintTraceHandler.CONSOLE);
// OMPlatform.INSTANCE.removeLogHandler(PrintLogHandler.CONSOLE);
}
}
protected void doSetUp() throws Exception
{
}
protected void doTearDown() throws Exception
{
deleteFiles();
}
public synchronized void deleteFiles()
{
for (File file : filesToDelete)
{
IOUtil.delete(file);
}
filesToDelete.clear();
}
public synchronized void addFileToDelete(File file)
{
filesToDelete.add(file);
}
public File createTempFolder() throws IORuntimeException
{
File folder = TMPUtil.createTempFolder();
addFileToDelete(folder);
return folder;
}
public File createTempFolder(String prefix) throws IORuntimeException
{
File folder = TMPUtil.createTempFolder(prefix);
addFileToDelete(folder);
return folder;
}
public File createTempFolder(String prefix, String suffix) throws IORuntimeException
{
File folder = TMPUtil.createTempFolder(prefix, suffix);
addFileToDelete(folder);
return folder;
}
public File createTempFolder(String prefix, String suffix, File directory) throws IORuntimeException
{
File folder = TMPUtil.createTempFile(prefix, suffix, directory);
addFileToDelete(folder);
return folder;
}
public File createTempFile() throws IORuntimeException
{
File file = TMPUtil.createTempFile();
addFileToDelete(file);
return file;
}
public File createTempFile(String prefix) throws IORuntimeException
{
File file = TMPUtil.createTempFile(prefix);
addFileToDelete(file);
return file;
}
public File createTempFile(String prefix, String suffix) throws IORuntimeException
{
File file = TMPUtil.createTempFile(prefix, suffix);
addFileToDelete(file);
return file;
}
public File createTempFile(String prefix, String suffix, File directory) throws IORuntimeException
{
File file = TMPUtil.createTempFile(prefix, suffix, directory);
addFileToDelete(file);
return file;
}
@Override
public String toString()
{
return getClass().getSimpleName() + "." + getName();
}
public static AbstractOMTest getCurrrentTest()
{
return (AbstractOMTest)TestExecuter.getValue();
}
/**
* @deprecated Use assertEquals(message, true, ...)
*/
@Deprecated
public static void assertTrue(String message, boolean condition)
{
throw new UnsupportedOperationException("Use assertEquals(message, true, ...)");
}
/**
* @deprecated Use assertEquals(true, ...)
*/
@Deprecated
public static void assertTrue(boolean condition)
{
throw new UnsupportedOperationException("Use assertEquals(true, ...)");
}
/**
* @deprecated Use assertEquals(message, false, ...)
*/
@Deprecated
public static void assertFalse(String message, boolean condition)
{
throw new UnsupportedOperationException("Use assertEquals(message, false, ...)");
}
/**
* @deprecated Use assertEquals(false, ...)
*/
@Deprecated
public static void assertFalse(boolean condition)
{
throw new UnsupportedOperationException("Use assertEquals(false, ...)");
}
public static void assertEquals(Object[] expected, Object[] actual)
{
if (!Arrays.deepEquals(expected, actual))
{
throw new AssertionFailedError(
"expected:" + Arrays.deepToString(expected) + " but was:" + Arrays.deepToString(actual));
}
}
public static void assertEquals(Object expected, Object actual)
{
if (actual == expected)
{
return;
}
try
{
Assert.assertEquals(expected, actual);
}
catch (AssertionError ex)
{
AssertionFailedError error = new AssertionFailedError(ex.getMessage());
error.initCause(ex);
throw error;
}
}
public static void assertEquals(String message, Object expected, Object actual)
{
if (expected == null && actual == null)
{
return;
}
if (expected != null && expected.equals(actual))
{
return;
}
// IMPORTANT: Give possible CDOLegacyWrapper a chance for actual, too
if (actual != null && actual.equals(expected))
{
return;
}
failNotEquals(message, expected, actual);
}
public static void sleep(long millis)
{
msg("Sleeping " + millis);
ConcurrencyUtil.sleep(millis);
}
public static void assertInstanceOf(Class<?> expected, Object object)
{
assertEquals("Not an instance of " + expected + ": " + object.getClass().getName(), true,
expected.isInstance(object));
}
public static void assertNotInstanceOf(Class<?> expected, Object object)
{
assertEquals("An instance of " + expected + ": " + object.getClass().getName(), false, expected.isInstance(object));
}
public static void assertActive(Object object) throws InterruptedException
{
final LatchTimeOuter timeOuter = new LatchTimeOuter();
IListener listener = new LifecycleEventAdapter()
{
@Override
protected void onActivated(ILifecycle lifecycle)
{
timeOuter.countDown();
}
};
EventUtil.addListener(object, listener);
try
{
if (LifecycleUtil.isActive(object))
{
timeOuter.countDown();
}
timeOuter.assertNoTimeOut();
}
finally
{
EventUtil.removeListener(object, listener);
}
}
public static void assertInactive(Object object) throws InterruptedException
{
final LatchTimeOuter timeOuter = new LatchTimeOuter();
IListener listener = new LifecycleEventAdapter()
{
@Override
protected void onDeactivated(ILifecycle lifecycle)
{
timeOuter.countDown();
}
};
EventUtil.addListener(object, listener);
try
{
if (!LifecycleUtil.isActive(object))
{
timeOuter.countDown();
}
timeOuter.assertNoTimeOut();
}
finally
{
EventUtil.removeListener(object, listener);
}
}
public static void assertSimilar(double expected, double actual, int precision)
{
final double factor = 10 * precision;
if (Math.round(expected * factor) != Math.round(actual * factor))
{
assertEquals(expected, actual);
}
}
public static void assertSimilar(float expected, float actual, int precision)
{
final float factor = 10 * precision;
if (Math.round(expected * factor) != Math.round(actual * factor))
{
assertEquals(expected, actual);
}
}
public static void msg(Object m)
{
if (!SUPPRESS_OUTPUT)
{
if (consoleEnabled && TRACER.isEnabled())
{
TRACER.trace("--> " + m); //$NON-NLS-1$
}
}
}
public static void dumpEvents(Object notifier)
{
dumpEvents(notifier, true);
}
public static void dumpEvents(Object notifier, boolean on)
{
if (notifier instanceof INotifier)
{
INotifier iNotifier = (INotifier)notifier;
IListener[] listeners = iNotifier.getListeners();
boolean wasOn = false;
for (int i = 0; i < listeners.length; i++)
{
if (listeners[i] == DUMPER)
{
wasOn = true;
break;
}
}
if (on && !wasOn)
{
iNotifier.addListener(DUMPER);
}
else if (!on && wasOn)
{
iNotifier.removeListener(DUMPER);
}
}
}
public static void skipTest(boolean skip)
{
if (skip)
{
throw new SkipTestException();
}
}
public static void skipTest()
{
skipTest(true);
}
/**
* @author Eike Stepper
*/
private static final class SkipTestException extends RuntimeException
{
private static final long serialVersionUID = 1L;
}
/**
* @author Eike Stepper
*/
public static class AsyncResult<T>
{
private volatile T value;
private CountDownLatch latch = new CountDownLatch(1);
public AsyncResult()
{
}
public void setValue(T value)
{
this.value = value;
latch.countDown();
}
public T getValue(long timeout) throws Exception
{
if (!latch.await(timeout, TimeUnit.MILLISECONDS))
{
throw new TimeoutException("Result value not available after " + timeout + " milli seconds");
}
return value;
}
public T getValue() throws Exception
{
return getValue(DEFAULT_TIMEOUT);
}
}
/**
* @author Eike Stepper
*/
public static interface ITimeOuter
{
public boolean timedOut(long timeoutMillis) throws InterruptedException;
}
/**
* @author Eike Stepper
*/
public static abstract class TimeOuter implements ITimeOuter
{
public boolean timedOut() throws InterruptedException
{
return timedOut(DEFAULT_TIMEOUT);
}
public void assertTimeOut(long timeoutMillis) throws InterruptedException
{
assertEquals("Timeout expected", true, timedOut(timeoutMillis));
}
public void assertTimeOut() throws InterruptedException
{
assertTimeOut(DEFAULT_TIMEOUT_EXPECTED);
}
public void assertNoTimeOut(long timeoutMillis) throws InterruptedException
{
assertEquals("Timeout after " + timeoutMillis + " millis", false, timedOut(timeoutMillis));
}
public void assertNoTimeOut() throws InterruptedException
{
assertNoTimeOut(DEFAULT_TIMEOUT);
}
}
/**
* @author Eike Stepper
*/
public static abstract class PollingTimeOuter extends TimeOuter
{
public static final long DEFAULT_SLEEP_MILLIS = 1;
private long sleepMillis = DEFAULT_SLEEP_MILLIS;
public PollingTimeOuter(long sleepMillis)
{
this.sleepMillis = sleepMillis;
}
public PollingTimeOuter()
{
}
public boolean timedOut(long timeoutMillis) throws InterruptedException
{
int retries = (int)Math.round(timeoutMillis / sleepMillis + .5d);
for (int i = 0; i < retries; i++)
{
if (successful())
{
return false;
}
sleep(sleepMillis);
}
return true;
}
protected abstract boolean successful();
}
/**
* @author Eike Stepper
*/
public static class LockTimeOuter extends TimeOuter
{
private Lock lock;
public LockTimeOuter(Lock lock)
{
this.lock = lock;
}
public Lock getLock()
{
return lock;
}
public boolean timedOut(long timeoutMillis) throws InterruptedException
{
Condition condition = lock.newCondition();
return !condition.await(timeoutMillis, TimeUnit.MILLISECONDS);
}
}
/**
* @author Eike Stepper
*/
public static class LatchTimeOuter extends TimeOuter
{
private CountDownLatch latch;
public LatchTimeOuter(CountDownLatch latch)
{
this.latch = latch;
}
public LatchTimeOuter(int count)
{
this(new CountDownLatch(count));
}
public LatchTimeOuter()
{
this(1);
}
public CountDownLatch getLatch()
{
return latch;
}
public long getCount()
{
return latch.getCount();
}
public void countDown()
{
latch.countDown();
}
public void countDown(int n)
{
for (int i = 0; i < n; i++)
{
countDown();
}
}
public boolean timedOut(long timeoutMillis) throws InterruptedException
{
return !latch.await(timeoutMillis, TimeUnit.MILLISECONDS);
}
}
}