/*******************************************************************************
* Copyright (c) 2006, 2011 Wind River Systems, Inc. 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:
* Markus Schorn - initial API and implementation
* Andrew Ferguson (Symbian)
*******************************************************************************/
package org.eclipse.cdt.core.testplugin.util;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import junit.framework.AssertionFailedError;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestFailure;
import junit.framework.TestResult;
import junit.framework.TestSuite;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ElementChangedEvent;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.IElementChangedListener;
import org.eclipse.cdt.core.testplugin.ResourceHelper;
import org.eclipse.cdt.core.testplugin.TestScannerProvider;
import org.eclipse.cdt.internal.core.CCoreInternals;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPASTNameBase;
import org.eclipse.cdt.internal.core.pdom.CModelListener;
import org.eclipse.cdt.internal.core.pdom.PDOMManager;
import org.eclipse.cdt.internal.core.pdom.indexer.AbstractPDOMIndexer;
import org.eclipse.core.resources.IResourceStatus;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.ILogListener;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
public class BaseTestCase extends TestCase {
private boolean fExpectFailure= false;
private int fBugnumber= 0;
private int fExpectedLoggedNonOK= 0;
public BaseTestCase() {
super();
}
public BaseTestCase(String name) {
super(name);
}
public static NullProgressMonitor npm() {
return new NullProgressMonitor();
}
@Override
protected void setUp() throws Exception {
CPPASTNameBase.sAllowRecursionBindings= false;
CPPASTNameBase.sAllowNameComputation= false;
CModelListener.sSuppressUpdateOfLastRecentlyUsed= true;
AbstractPDOMIndexer.noFilesUpFront= true;
}
@Override
protected void tearDown() throws Exception {
ResourceHelper.cleanUp();
TestScannerProvider.clear();
}
protected static TestSuite suite(Class clazz) {
return suite(clazz, null);
}
protected static TestSuite suite(Class clazz, String failingTestPrefix) {
TestSuite suite= new TestSuite(clazz);
Test failing= getFailingTests(clazz, failingTestPrefix);
if (failing != null) {
suite.addTest(failing);
}
return suite;
}
private static Test getFailingTests(Class clazz, String prefix) {
TestSuite suite= new TestSuite("Failing Tests");
HashSet names= new HashSet();
Class superClass= clazz;
while (Test.class.isAssignableFrom(superClass) && !TestCase.class.equals(superClass)) {
Method[] methods= superClass.getDeclaredMethods();
for (Method method : methods) {
addFailingMethod(suite, method, names, clazz, prefix);
}
superClass= superClass.getSuperclass();
}
if (suite.countTestCases() == 0) {
return null;
}
return suite;
}
private static void addFailingMethod(TestSuite suite, Method m, Set names, Class clazz, String prefix) {
String name= m.getName();
if (!names.add(name)) {
return;
}
if (name.startsWith("test") || (prefix != null && !name.startsWith(prefix))) {
return;
}
if (name.equals("tearDown") || name.equals("setUp") || name.equals("runBare")) {
return;
}
if (Modifier.isPublic(m.getModifiers())) {
Class[] parameters= m.getParameterTypes();
Class returnType= m.getReturnType();
if (parameters.length == 0 && returnType.equals(Void.TYPE)) {
Test test= TestSuite.createTest(clazz, name);
((BaseTestCase) test).setExpectFailure(0);
suite.addTest(test);
}
}
}
@Override
public void runBare() throws Throwable {
final List<IStatus> statusLog= Collections.synchronizedList(new ArrayList());
ILogListener logListener= new ILogListener() {
public void logging(IStatus status, String plugin) {
if (!status.isOK() && status.getSeverity() != IStatus.INFO) {
switch (status.getCode()) {
case IResourceStatus.NOT_FOUND_LOCAL:
case IResourceStatus.NO_LOCATION_LOCAL:
case IResourceStatus.FAILED_READ_LOCAL:
case IResourceStatus.RESOURCE_NOT_LOCAL:
// logged by the resources plugin.
return;
}
statusLog.add(status);
}
}
};
final CCorePlugin corePlugin = CCorePlugin.getDefault();
if (corePlugin != null) { // if we don't run a JUnit Plugin Test
corePlugin.getLog().addLogListener(logListener);
}
Throwable testThrowable= null;
try {
try {
super.runBare();
} catch (Throwable e) {
testThrowable=e;
}
if (statusLog.size() != fExpectedLoggedNonOK) {
StringBuffer msg= new StringBuffer("Expected number (" + fExpectedLoggedNonOK + ") of ");
msg.append("non-OK status objects in log differs from actual (" + statusLog.size() + ").\n");
Throwable cause= null;
if (!statusLog.isEmpty()) {
for (IStatus status : statusLog) {
IStatus[] ss= {status};
ss= status instanceof MultiStatus ? ((MultiStatus) status).getChildren() : ss;
for (IStatus s : ss) {
msg.append("\t" + s.getMessage() + " ");
Throwable t= s.getException();
cause= cause != null ? cause : t;
if (t != null) {
msg.append(t.getMessage() != null ? t.getMessage() : t.getClass().getCanonicalName());
}
msg.append("\n");
}
}
}
cause= cause != null ? cause : testThrowable;
AssertionFailedError afe= new AssertionFailedError(msg.toString());
afe.initCause(cause);
throw afe;
}
} finally {
if (corePlugin != null) {
corePlugin.getLog().removeLogListener(logListener);
}
}
if (testThrowable != null)
throw testThrowable;
}
@Override
public void run(TestResult result) {
if (!fExpectFailure || "true".equals(System.getProperty("SHOW_EXPECTED_FAILURES"))) {
super.run(result);
return;
}
result.startTest(this);
TestResult r = new TestResult();
super.run(r);
if (r.failureCount() == 1) {
TestFailure failure= r.failures().nextElement();
String msg= failure.exceptionMessage();
if (msg != null && msg.startsWith("Method \"" + getName() + "\"")) {
result.addFailure(this, new AssertionFailedError(msg));
}
} else if (r.errorCount() == 0 && r.failureCount() == 0) {
String err = "Unexpected success of " + getName();
if (fBugnumber > 0) {
err += ", bug #" + fBugnumber;
}
result.addFailure(this, new AssertionFailedError(err));
}
result.endTest(this);
}
public void setExpectFailure(int bugnumber) {
fExpectFailure= true;
fBugnumber= bugnumber;
}
/**
* The last value passed to this method in the body of a testXXX method
* will be used to determine whether or not the presence of non-OK status objects
* in the log should fail the test. If the logged number of non-OK status objects
* differs from the last value passed, the test is failed. If this method is not called
* at all, the expected number defaults to zero.
* @param value
*/
public void setExpectedNumberOfLoggedNonOKStatusObjects(int count) {
fExpectedLoggedNonOK= count;
}
/**
* Some test steps need synchronizing against a CModel event. This class
* is a very basic means of doing that.
*/
static protected class ModelJoiner implements IElementChangedListener {
private boolean[] changed= new boolean[1];
public ModelJoiner() {
CoreModel.getDefault().addElementChangedListener(this);
}
public void clear() {
synchronized (changed) {
changed[0]= false;
changed.notifyAll();
}
}
public void join() throws CoreException {
try {
synchronized(changed) {
while (!changed[0]) {
changed.wait();
}
}
} catch (InterruptedException e) {
throw new CoreException(CCorePlugin.createStatus("Interrupted", e));
}
}
public void dispose() {
CoreModel.getDefault().removeElementChangedListener(this);
}
public void elementChanged(ElementChangedEvent event) {
// Only respond to post change events
if (event.getType() != ElementChangedEvent.POST_CHANGE)
return;
synchronized (changed) {
changed[0]= true;
changed.notifyAll();
}
}
}
public static void waitForIndexer(ICProject project) throws InterruptedException {
final PDOMManager indexManager = CCoreInternals.getPDOMManager();
assertTrue(indexManager.joinIndexer(10000, npm()));
long waitms= 1;
while (waitms < 2000 && !indexManager.isProjectRegistered(project)) {
Thread.sleep(waitms);
waitms *= 2;
}
assertTrue(indexManager.joinIndexer(10000, npm()));
}
}