/*
* $Id$
*
* Copyright (C) 2003-2013 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.test.framework;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import junit.framework.JUnit4TestAdapter;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.apache.log4j.Logger;
import org.jnode.plugin.ConfigurationElement;
import org.jnode.plugin.Extension;
import org.jnode.plugin.ExtensionPoint;
import org.jnode.plugin.ExtensionPointListener;
import org.jnode.plugin.PluginDescriptor;
/**
* @author Fabien DUMINY (fduminy at jnode.org)
*/
public final class TestManager implements ExtensionPointListener {
public static final String ALL_CATEGORY = "all";
public static final List<String> DEFAULT_CATEGORY =
Collections.unmodifiableList(Arrays.asList(new String[]{ALL_CATEGORY, "default"}));
private static TestManager instance = null;
/**
* My logger
*/
private static final Logger log = Logger.getLogger(TestManager.class);
/**
* The org.jnode.tests extension point
*/
private final ExtensionPoint typesEP;
/**
* The Test classes
*/
private final List<Class<? extends Test>> tests =
new ArrayList<Class<? extends Test>>();
/**
* The TestSuite classes
*/
private final List<Class<? extends TestSuite>> suites =
new ArrayList<Class<? extends TestSuite>>();
/**
* The Categories for the Tests and TestSuites
*/
private final Map<Class<? extends Test>, List<String>> categories =
new HashMap<Class<? extends Test>, List<String>>();
private final Set<String> categoriesNames = new TreeSet<String>();
public static TestManager getInstance() {
if (TestManager.instance == null)
throw new RuntimeException("TestManager not yet created");
return TestManager.instance;
}
/**
* Create a new instance
*/
TestManager(ExtensionPoint typesEP) {
if (instance != null)
throw new RuntimeException("TestManager already created");
TestManager.instance = this;
this.typesEP = typesEP;
if (typesEP == null) {
throw new IllegalArgumentException("The types extension-point cannot be null");
}
//log.debug("before addListener");
//typesEP.addListener(this);
//log.debug("after addListener");
refreshTests();
log.debug("end of Cstor");
}
public List<Class<? extends Test>> getTests() {
refreshTests();
List<Class<? extends Test>> result = new ArrayList<Class<? extends Test>>(tests);
result.addAll(suites);
return result;
}
public List<String> getCategories(Class<? extends Test> test) {
refreshTests();
return Collections.unmodifiableList(categories.get(test));
}
public Set<String> getCategories() {
refreshTests();
return Collections.unmodifiableSet(categoriesNames);
}
/**
* Get a TestSuite with all known tests.
*/
public TestSuite getTestSuite() {
refreshTests();
return getTestSuite(Collections.singletonList(ALL_CATEGORY));
}
/**
* Get a TestSuite with all tests that have one of the given categories.
*/
public synchronized TestSuite getTestSuite(List<String> wantedCategories) {
refreshTests();
TestSuite suite = new TestSuite();
// add Tests
for (Class<? extends Test> testClass : tests) {
if (!matchCategory(wantedCategories, categories.get(testClass))) continue;
try {
Test test = (Test) testClass.newInstance();
suite.addTest(test);
log.debug("added Test " + testClass.getName());
} catch (InstantiationException e1) {
e1.printStackTrace();
} catch (IllegalAccessException e1) {
e1.printStackTrace();
}
}
// add TestSuites
for (Class<? extends TestSuite> suiteClass : suites) {
if (!matchCategory(wantedCategories, categories.get(suiteClass))) continue;
suite.addTest(new JUnit4TestAdapter(suiteClass));
log.debug("added TestSuite " + suiteClass.getName());
}
return suite;
}
private static boolean matchCategory(List<String> wantedCategories,
List<String> testCategories) {
for (String testCategory : testCategories) {
if (wantedCategories.contains(testCategory)) {
log.debug("testCategory:" + testCategory + " contains");
return true;
}
log.debug("testCategory:" + testCategory + " NOT contains");
}
log.debug("testCategory FAILS");
return false;
}
/**
* Refresh all known tests.
*/
private synchronized void refreshTests() {
log.debug("<<< BEGIN refreshTests >>>");
suites.clear();
tests.clear();
categories.clear();
categoriesNames.clear();
for (Extension ext : typesEP.getExtensions()) {
final PluginDescriptor desc = ext.getDeclaringPluginDescriptor();
log.debug("plugin " + desc.getName() + " classloader=" + desc.getPluginClassLoader());
final ConfigurationElement[] elements = ext.getConfigurationElements();
for (ConfigurationElement e : elements) {
if ("test".equals(e.getName())) {
addTest(tests, e, desc);
} else if ("suite".equals(e.getName())) {
addTest(suites, e, desc);
}
}
}
log.debug("<<< END refreshTests >>>");
}
private <T extends Test> void addTest(List<Class<? extends T>> list,
ConfigurationElement e, PluginDescriptor desc) {
String className = e.getAttribute("class");
Class<T> clazz = (Class<T>) loadClass(className, desc);
if (clazz != null) {
list.add(clazz);
log.debug("adding class " + className);
String testCategories = e.getAttribute("category");
log.debug("testCategories=" + testCategories);
List<String> categs;
if ((testCategories == null) || (testCategories.trim().length() == 0)) {
log.debug("DEFAULT_CATEGORY");
categs = DEFAULT_CATEGORY;
} else {
categs = new ArrayList<String>();
StringTokenizer st = new StringTokenizer(testCategories.trim(), ",", false);
log.debug(st.countTokens() + " tokens");
while (st.hasMoreTokens()) {
String token = st.nextToken().trim();
log.debug("token=" + token);
categs.add(token);
}
categs.add(ALL_CATEGORY);
log.debug("ALL_CATEGORY");
}
categories.put(clazz, categs);
categoriesNames.addAll(categs);
}
}
private static Class loadClass(String className, PluginDescriptor desc) {
log.debug("searching class " + className);
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class clazz = loadClass(className, loader);
log.debug("ContextClassLoader:" + ((clazz == null) ? "not found" : "FOUND"));
loader = desc.getPluginClassLoader();
if (clazz == null) {
clazz = loadClass(className, loader);
log.debug("PluginClassLoader:" + ((clazz == null) ? "not found" : "FOUND"));
}
return clazz;
}
private static Class loadClass(String className, ClassLoader loader) {
try {
log.debug("loadClass: method 1");
return Class.forName(className, true, loader);
} catch (ClassNotFoundException e) {
try {
log.debug("loadClass: method 2");
return loader.loadClass(className);
} catch (ClassNotFoundException e1) {
log.debug("loadClass: all methods failed");
return null;
}
}
}
/**
* @see org.jnode.plugin.ExtensionPointListener#extensionAdded(org.jnode.plugin.ExtensionPoint,
* org.jnode.plugin.Extension)
*/
public void extensionAdded(ExtensionPoint point, Extension extension) {
//log.debug("extensionAdded");
//log.debug("extensionAdded : before refreshTests");
//refreshTests();
//log.debug("extensionAdded : after refreshTests");
}
/**
* @see org.jnode.plugin.ExtensionPointListener#extensionRemoved(org.jnode.plugin.ExtensionPoint,
* org.jnode.plugin.Extension)
*/
public void extensionRemoved(ExtensionPoint point, Extension extension) {
//log.debug("extensionRemoved : before refreshTests");
//refreshTests;
//log.debug("extensionRemoved : after refreshTests");
}
}