You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by sk...@apache.org on 2006/03/03 02:28:57 UTC
svn commit: r382604 - in
/jakarta/commons/proper/logging/contrib/simon/jcl2/src/core/test: ./ org/
org/apache/ org/apache/commons/ org/apache/commons/logging/
Author: skitching
Date: Thu Mar 2 17:28:55 2006
New Revision: 382604
URL: http://svn.apache.org/viewcvs?rev=382604&view=rev
Log:
Initial unit testing framework.
Added:
jakarta/commons/proper/logging/contrib/simon/jcl2/src/core/test/
jakarta/commons/proper/logging/contrib/simon/jcl2/src/core/test/org/
jakarta/commons/proper/logging/contrib/simon/jcl2/src/core/test/org/apache/
jakarta/commons/proper/logging/contrib/simon/jcl2/src/core/test/org/apache/commons/
jakarta/commons/proper/logging/contrib/simon/jcl2/src/core/test/org/apache/commons/logging/
jakarta/commons/proper/logging/contrib/simon/jcl2/src/core/test/org/apache/commons/logging/LogConfigurationExceptionTestCase.java (with props)
jakarta/commons/proper/logging/contrib/simon/jcl2/src/core/test/org/apache/commons/logging/PathableClassLoader.java (with props)
jakarta/commons/proper/logging/contrib/simon/jcl2/src/core/test/org/apache/commons/logging/PathableTestSuite.java (with props)
Added: jakarta/commons/proper/logging/contrib/simon/jcl2/src/core/test/org/apache/commons/logging/LogConfigurationExceptionTestCase.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/logging/contrib/simon/jcl2/src/core/test/org/apache/commons/logging/LogConfigurationExceptionTestCase.java?rev=382604&view=auto
==============================================================================
--- jakarta/commons/proper/logging/contrib/simon/jcl2/src/core/test/org/apache/commons/logging/LogConfigurationExceptionTestCase.java (added)
+++ jakarta/commons/proper/logging/contrib/simon/jcl2/src/core/test/org/apache/commons/logging/LogConfigurationExceptionTestCase.java Thu Mar 2 17:28:55 2006
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2004,2005 The Apache Software Foundation.
+ *
+ * Licensed 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.commons.logging;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests the LogConfigurationException class.
+ */
+public class LogConfigurationExceptionTestCase extends TestCase
+{
+ public void testDefaultConstructor()
+ {
+ LogConfigurationException ex = new LogConfigurationException();
+ assertNull("message is null", ex.getMessage());
+ assertNull("cause is null", ex.getCause());
+
+ // We can't verify what toString returns, as that will vary by
+ // JVM. However this does test that toString doesn't throw an
+ // exception or return null.
+ String str = ex.toString();
+ assertNotNull("toString is not null", str);
+ }
+
+ public void testConstructorMessageOnly()
+ {
+ LogConfigurationException ex = new LogConfigurationException("msg");
+ assertEquals("msg has expected value", "msg", ex.getMessage());
+ assertNull("cause is null", ex.getCause());
+
+ // We can't verify what toString returns, as that will vary by
+ // JVM. However this does test that toString doesn't throw an
+ // exception or return null.
+ String str = ex.toString();
+ assertNotNull(str);
+ }
+
+ public void testConstructorCauseOnly()
+ {
+ // try with no message in cause
+ Exception cause1 = new Exception();
+ LogConfigurationException ex = new LogConfigurationException(cause1);
+
+ // The cause message automatically generated by Sun when none is
+ // provided looks something like "java.lang.Exception (Caused by ...)"
+ assertNotNull("message is null", ex.getMessage());
+ assertSame("cause is set", cause1, ex.getCause());
+
+ // try with message in cause
+ Exception cause2 = new Exception("ccddee");
+ LogConfigurationException ex2 = new LogConfigurationException(cause2);
+
+ // The cause message should be included somewhere in the
+ // LogConfigurationException msg
+ assertTrue("msg has expected value", ex2.getMessage().contains("ccddee"));
+ assertSame("cause is set", cause2, ex2.getCause());
+ }
+
+ public void testConstructorMessageAndCause()
+ {
+ String causeMsg = "ccddee";
+ Exception cause = new Exception(causeMsg);
+ String exceptionMsg = "ffgghh";
+ LogConfigurationException ex = new LogConfigurationException(exceptionMsg, cause);
+
+ assertTrue("msg has expected value", ex.getMessage().startsWith(exceptionMsg));
+ assertSame("cause is set", cause, ex.getCause());
+ }
+}
Propchange: jakarta/commons/proper/logging/contrib/simon/jcl2/src/core/test/org/apache/commons/logging/LogConfigurationExceptionTestCase.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jakarta/commons/proper/logging/contrib/simon/jcl2/src/core/test/org/apache/commons/logging/LogConfigurationExceptionTestCase.java
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Added: jakarta/commons/proper/logging/contrib/simon/jcl2/src/core/test/org/apache/commons/logging/PathableClassLoader.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/logging/contrib/simon/jcl2/src/core/test/org/apache/commons/logging/PathableClassLoader.java?rev=382604&view=auto
==============================================================================
--- jakarta/commons/proper/logging/contrib/simon/jcl2/src/core/test/org/apache/commons/logging/PathableClassLoader.java (added)
+++ jakarta/commons/proper/logging/contrib/simon/jcl2/src/core/test/org/apache/commons/logging/PathableClassLoader.java Thu Mar 2 17:28:55 2006
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed 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.commons.logging;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+// TODO: use HashTable instead of HashMap for java1.1 support
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * A ClassLoader which sees only specified classes, and which can be
+ * set to do parent-first or child-first path lookup.
+ * <p>
+ * Note that this classloader is not "industrial strength"; users
+ * looking for such a class may wish to look at the Tomcat sourcecode
+ * instead. In particular, this class may not be threadsafe.
+ * <p>
+ * Note that the ClassLoader.getResources method isn't overloaded here.
+ * It would be nice to ensure that when child-first lookup is set the
+ * resources from the child are returned earlier in the list than the
+ * resources from the parent. However overriding this method isn't possible
+ * as the java 1.4 version of ClassLoader declares this method final
+ * (though the java 1.5 version has removed the final qualifier). As the
+ * ClassLoader javadoc doesn't specify the order in which resources
+ * are returned, it's valid to return the resources in any order (just
+ * untidy) so the inherited implementation is technically ok.
+ */
+
+public class PathableClassLoader extends URLClassLoader {
+
+ private static final URL[] NO_URLS = new URL[0];
+
+ /**
+ * A map of package-prefix to ClassLoader. Any class which is in
+ * this map is looked up via the specified classloader instead of
+ * the classpath associated with this classloader or its parents.
+ * <p>
+ * This is necessary in order for the rest of the world to communicate
+ * with classes loaded via a custom classloader. As an example, junit
+ * testcases which are loaded via a custom classloader needs to see
+ * the same junit classes as the code invoking the testcase, otherwise
+ * they can't pass result objects back.
+ * <p>
+ * Normally, only a classloader created with a null parent needs to
+ * have any lookasides defined.
+ */
+ private HashMap lookasides = null;
+
+ /**
+ * See setParentFirst.
+ */
+ private boolean parentFirst = true;
+
+ /**
+ * Constructor.
+ */
+ public PathableClassLoader(ClassLoader parent) {
+ super(NO_URLS, parent);
+ }
+
+ /**
+ * Allow caller to explicitly add paths. Generally this not a good idea;
+ * use addLogicalLib instead, then define the location for that logical
+ * library in the build.xml file.
+ */
+ public void addURL(URL url) {
+ super.addURL(url);
+ }
+
+ /**
+ * Specify whether this classloader should ask the parent classloader
+ * to resolve a class first, before trying to resolve it via its own
+ * classpath.
+ * <p>
+ * Checking with the parent first is the normal approach for java, but
+ * components within containers such as servlet engines can use
+ * child-first lookup instead, to allow the components to override libs
+ * which are visible in shared classloaders provided by the container.
+ * <p>
+ * This value defaults to true.
+ */
+ public void setParentFirst(boolean state) {
+ parentFirst = state;
+ }
+
+ /**
+ * For classes with the specified prefix, get them from the system
+ * classpath <i>which is active at the point this method is called</i>.
+ * <p>
+ * This method is just a shortcut for
+ * <pre>
+ * useExplicitLoader(prefix, ClassLoader.getSystemClassLoader());
+ * </pre>
+ */
+ public void useSystemLoader(String prefix) {
+ useExplicitLoader(prefix, ClassLoader.getSystemClassLoader());
+
+ }
+
+ /**
+ * Specify a classloader to use for specific java packages.
+ */
+ public void useExplicitLoader(String prefix, ClassLoader loader) {
+ if (lookasides == null) {
+ lookasides = new HashMap();
+ }
+ lookasides.put(prefix, loader);
+ }
+
+ /**
+ * Specify a collection of logical libraries. See addLogicalLib.
+ */
+ public void addLogicalLib(String[] logicalLibs) {
+ for(int i=0; i<logicalLibs.length; ++i) {
+ addLogicalLib(logicalLibs[i]);
+ }
+ }
+
+ /**
+ * Specify a logical library to be included in the classpath used to
+ * locate classes.
+ * <p>
+ * The specified lib name is used as a key into the system properties;
+ * there is expected to be a system property defined with that name
+ * whose value is a url that indicates where that logical library can
+ * be found. Typically this is the name of a jar file, or a directory
+ * containing class files.
+ * <p>
+ * Using logical library names allows the calling code to specify its
+ * desired classpath without knowing the exact location of the necessary
+ * classes.
+ */
+ public void addLogicalLib(String logicalLib) {
+ String filename = System.getProperty(logicalLib);
+ if (filename == null) {
+ throw new UnknownError(
+ "Logical lib [" + logicalLib + "] is not defined"
+ + " as a System property.");
+ }
+
+ try {
+ URL url = new File(filename).toURL();
+ addURL(url);
+ } catch(java.net.MalformedURLException e) {
+ throw new UnknownError(
+ "Invalid file [" + filename + "] for logical lib [" + logicalLib + "]");
+ }
+ }
+
+ /**
+ * Override ClassLoader method.
+ * <p>
+ * For each explicitly mapped package prefix, if the name matches the
+ * prefix associated with that entry then attempt to load the class via
+ * that entries' classloader.
+ */
+ protected Class loadClass(String name, boolean resolve)
+ throws ClassNotFoundException {
+ // just for performance, check java and javax
+ if (name.startsWith("java.") || name.startsWith("javax.")) {
+ return super.loadClass(name, resolve);
+ }
+
+ if (lookasides != null) {
+ for(Iterator i = lookasides.entrySet().iterator(); i.hasNext(); ) {
+ Map.Entry entry = (Map.Entry) i.next();
+ String prefix = (String) entry.getKey();
+ if (name.startsWith(prefix) == true) {
+ ClassLoader loader = (ClassLoader) entry.getValue();
+ Class clazz = Class.forName(name, resolve, loader);
+ return clazz;
+ }
+ }
+ }
+
+ if (parentFirst) {
+ return super.loadClass(name, resolve);
+ } else {
+ // Implement child-first.
+ //
+ // It appears that the findClass method doesn't check whether the
+ // class has already been loaded. This seems odd to me, but without
+ // first checking via findLoadedClass we can get java.lang.LinkageError
+ // with message "duplicate class definition" which isn't good.
+
+ try {
+ Class clazz = findLoadedClass(name);
+ if (clazz == null) {
+ clazz = super.findClass(name);
+ }
+ if (resolve) {
+ resolveClass(clazz);
+ }
+ return clazz;
+ } catch(ClassNotFoundException e) {
+ return super.loadClass(name, resolve);
+ }
+ }
+ }
+
+ /**
+ * Same as parent class method except that when parentFirst is false
+ * the resource is looked for in the local classpath before the parent
+ * loader is consulted.
+ */
+ public URL getResource(String name) {
+ if (parentFirst) {
+ return super.getResource(name);
+ } else {
+ URL local = super.findResource(name);
+ if (local != null) {
+ return local;
+ }
+ return super.getResource(name);
+ }
+ }
+
+ /**
+ * Same as parent class method except that when parentFirst is false
+ * the resource is looked for in the local classpath before the parent
+ * loader is consulted.
+ */
+ public InputStream getResourceAsStream(String name) {
+ if (parentFirst) {
+ return super.getResourceAsStream(name);
+ } else {
+ URL local = super.findResource(name);
+ if (local != null) {
+ try {
+ return local.openStream();
+ } catch(IOException e) {
+ // TODO: check if this is right or whether we should
+ // fall back to trying parent. The javadoc doesn't say...
+ return null;
+ }
+ }
+ return super.getResourceAsStream(name);
+ }
+ }
+}
Propchange: jakarta/commons/proper/logging/contrib/simon/jcl2/src/core/test/org/apache/commons/logging/PathableClassLoader.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jakarta/commons/proper/logging/contrib/simon/jcl2/src/core/test/org/apache/commons/logging/PathableClassLoader.java
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Added: jakarta/commons/proper/logging/contrib/simon/jcl2/src/core/test/org/apache/commons/logging/PathableTestSuite.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/logging/contrib/simon/jcl2/src/core/test/org/apache/commons/logging/PathableTestSuite.java?rev=382604&view=auto
==============================================================================
--- jakarta/commons/proper/logging/contrib/simon/jcl2/src/core/test/org/apache/commons/logging/PathableTestSuite.java (added)
+++ jakarta/commons/proper/logging/contrib/simon/jcl2/src/core/test/org/apache/commons/logging/PathableTestSuite.java Thu Mar 2 17:28:55 2006
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed 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.commons.logging;
+
+import java.util.Properties;
+
+import junit.framework.Test;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+
+/**
+ * Custom TestSuite class that can be used to control the context classloader
+ * in operation when a test runs.
+ * <p>
+ * For tests that need to control exactly what the classloader hierarchy is
+ * like when the test is run, something like the following is recommended:
+ * <pre>
+ * class SomeTestCase extends TestCase {
+ * public static Test suite() throws Exception {
+ * PathableClassLoader parent = new PathableClassLoader(null);
+ * parent.useSystemLoader("junit.");
+ *
+ * PathableClassLoader child = new PathableClassLoader(parent);
+ * child.addLogicalLib("testclasses");
+ * child.addLogicalLib("log4j12");
+ * child.addLogicalLib("commons-logging");
+ *
+ * Class testClass = child.loadClass(SomeTestCase.class.getName());
+ * ClassLoader contextClassLoader = child;
+ *
+ * PathableTestSuite suite = new PathableTestSuite(testClass, child);
+ * return suite;
+ * }
+ *
+ * // test methods go here
+ * }
+ * </pre>
+ * Note that if the suite method throws an exception then this will be handled
+ * reasonable gracefully by junit; it will report that the suite method for
+ * a test case failed with exception yyy.
+ * <p>
+ * The use of PathableClassLoader is not required to use this class, but it
+ * is expected that using the two classes together is common practice.
+ * <p>
+ * This class will run each test methods within the specified TestCase using
+ * the specified context classloader and system classloader. If different
+ * tests within the same class require different context classloaders,
+ * then the context classloader passed to the constructor should be the
+ * "lowest" one available, and tests that need the context set to some parent
+ * of this "lowest" classloader can call
+ * <pre>
+ * // NB: pseudo-code only
+ * setContextClassLoader(getContextClassLoader().getParent());
+ * </pre>
+ * This class ensures that any context classloader changes applied by a test
+ * is undone after the test is run, so tests don't need to worry about
+ * restoring the context classloader on exit. This class also ensures that
+ * the system properties are restored to their original settings after each
+ * test, so tests that manipulate those don't need to worry about resetting them.
+ * <p>
+ * This class does not provide facilities for manipulating system properties;
+ * tests that need specific system properties can simply set them in the
+ * fixture or at the start of a test method.
+ * <p>
+ * <b>Important!</b> When the test case is run, "this.getClass()" refers of
+ * course to the Class object passed to the constructor of this class - which
+ * is different from the class whose suite() method was executed to determine
+ * the classpath. This means that the suite method cannot communicate with
+ * the test cases simply by setting static variables (for example to make the
+ * custom classloaders available to the test methods or setUp/tearDown fixtures).
+ * If this is really necessary then it is possible to use reflection to invoke
+ * static methods on the class object passed to the constructor of this class.
+ * <p>
+ * <h2>Limitations</h2>
+ * <p>
+ * This class cannot control the system classloader (ie what method
+ * ClassLoader.getSystemClassLoader returns) because Java provides no
+ * mechanism for setting the system classloader. In this case, the only
+ * option is to invoke the unit test in a separate JVM with the appropriate
+ * settings.
+ * <p>
+ * The effect of using this approach in a system that uses junit's
+ * "reloading classloader" behaviour is unknown. This junit feature is
+ * intended for junit GUI apps where a test may be run multiple times
+ * within the same JVM - and in particular, when the .class file may
+ * be modified between runs of the test. How junit achieves this is
+ * actually rather weird (the whole junit code is rather weird in fact)
+ * and it is not clear whether this approach will work as expected in
+ * such situations.
+ */
+public class PathableTestSuite extends TestSuite {
+
+ /**
+ * The classloader that should be set as the context classloader
+ * before each test in the suite is run.
+ */
+ private ClassLoader contextLoader;
+
+ /**
+ * Constructor.
+ *
+ * @param testClass is the TestCase that is to be run, as loaded by
+ * the appropriate ClassLoader.
+ *
+ * @param contextClassLoader is the loader that should be returned by
+ * calls to Thread.currentThread.getContextClassLoader from test methods
+ * (or any method called by test methods).
+ */
+ public PathableTestSuite(Class testClass, ClassLoader contextClassLoader) {
+ super(testClass);
+ contextLoader = contextClassLoader;
+ }
+
+ /**
+ * This method is invoked once for each Test in the current TestSuite.
+ * Note that a Test may itself be a TestSuite object (ie a collection
+ * of tests).
+ * <p>
+ * The context classloader and system properties are saved before each
+ * test, and restored after the test completes to better isolate tests.
+ */
+ public void runTest(Test test, TestResult result) {
+ ClassLoader origContext = Thread.currentThread().getContextClassLoader();
+ Properties oldSysProps = (Properties) System.getProperties().clone();
+ try {
+ Thread.currentThread().setContextClassLoader(contextLoader);
+ test.run(result);
+ } finally {
+ System.setProperties(oldSysProps);
+ Thread.currentThread().setContextClassLoader(origContext);
+ }
+ }
+}
Propchange: jakarta/commons/proper/logging/contrib/simon/jcl2/src/core/test/org/apache/commons/logging/PathableTestSuite.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jakarta/commons/proper/logging/contrib/simon/jcl2/src/core/test/org/apache/commons/logging/PathableTestSuite.java
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org