Here's a patch for JUnit's busted classloader: junit-patch.jar. And below is XmlTest.java, a regression test that breaks (with a java.lang.LinkageError) with the old classloader and the swingui/awtui TestRunners, but passes with the patch.
ReloadableTestClassLoader - a properly delegating custom ClassLoader that currently relies on a System property value for its classpath: junit.test.path. The fix can be ported to junit.runner.TestCaseClassLoader or it can be effected through a new ClassLoader like this one.
ReloadingTestSuiteLoader - modified to return this custom ClassLoader. If I can get this fix applied to the real JUnit, this class wouldn't necessarily need to be modified (it's only like 5 lines) because we could fix the original TestCaseClassLoader instead of writing a new one. Whatever; minor detail.
XmlTest.java:
import junit.framework.TestCase;
import javax.xml.parsers.*;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
public class XmlTest extends TestCase {
Document doc = null;
public void testXml() throws ParserConfigurationException {
doc = DocumentBuilderFactory.newInstance()
.newDocumentBuilder()
.newDocument();
// System.out.println("Hello! I changed again");
Element root = doc.createElement("xml-test");
assertNotNull(root);
}
}
This is a simple test that reproduces a LinkageError with the JUnit buggy ClassLoader under the following conditions:
Use Sun JDK 1.4.x
Edit JUnit's excluded.properties so that you comment out the exclusion of org.w3c.dom.* -- this is necessary to reveal the bug and is the easiest test I could think of. Make sure your edited excluded.properties is first in the classpath or updated into junit.jar at junit/runner/excluded.properties (BTW, obviously the new ReloadableTestClassLoader doesn't use or rely on excluded.properties at all).
Put the DOM interfaces (in Xerces these are in xml-apis.jar) and an XML parser impl, such as Xerces, in your classpath to reproduce the bug. Why? Since you will have unexcluded org.w3c.dom.* in excluded.properties, JUnit will attempt to load those interfaces rather than delegate to the system loader. But they aren't in the scope of JUnit's buggy classloader if they aren't in the JVM's system classpath, so you have to add them. I.e., the 1.4.x JVM loads these in its boot loader, whose classpath JUnit's buggy loader doesn't know about.
Example of command line to use to reproduce the bug (using junit.jar from JUnit 3.8.1, but any version should work): java -cp .;junit.jar;xml-apis.jar;xerces.jar junit.swingui.TestRunner XmlTest
You should see this in the runner's error display window:
java.lang.LinkageError: Class org/w3c/dom/Document violates loader constraints
at java.lang.ClassLoader.defineClass0(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:502)
at java.lang.ClassLoader.defineClass(ClassLoader.java:431)
at junit.runner.TestCaseClassLoader.loadClass(TestCaseClassLoader.java:104)
at java.lang.ClassLoader.loadClass(ClassLoader.java:255)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:315)
at XmlTest.testXml(XmlTest.java:16)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at junit.framework.TestCase.runTest(TestCase.java:154)
at junit.framework.TestCase.runBare(TestCase.java:127)
at junit.framework.TestResult$1.protect(TestResult.java:106)
at junit.framework.TestResult.runProtected(TestResult.java:124)
at junit.framework.TestResult.run(TestResult.java:109)
at junit.framework.TestCase.run(TestCase.java:118)
at junit.framework.TestSuite.runTest(TestSuite.java:208)
at junit.framework.TestSuite.run(TestSuite.java:203)
at junit.swingui.TestRunner$16.run(TestRunner.java:623)
Now run the same test with the fixed ClassLoader:
shadow classes in junit.jar by listing junit-patch.jar first in the classpath
set junit.test.path property to include the path to XmlTest.class
run XmlTest with the Swing or AWT test runner: java -cp junit-patch.jar;junit.jar -Djunit.test.path=. junit.swingui.TestRunner XmlTest