Making test error/failure reporting more useful

This commit is contained in:
Joakim Erdfelt 2015-06-29 10:04:24 -07:00
parent f222763ac9
commit bc71a57afa
1 changed files with 232 additions and 83 deletions

View File

@ -18,33 +18,45 @@
package org.eclipse.jetty.webapp;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Path;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Collections;
import java.util.List;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.resource.PathResource;
import org.eclipse.jetty.util.resource.Resource;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
public class WebAppClassLoaderTest
{
@Rule
public ExpectedException expectedException = ExpectedException.none();
private Path testWebappDir;
private WebAppContext _context;
private WebAppClassLoader _loader;
@Before
public void init() throws Exception
{
Resource webapp = Resource.newResource("./src/test/webapp");
this.testWebappDir = MavenTestingUtils.getProjectDir("src/test/webapp").toPath();
Resource webapp = new PathResource(testWebappDir);
_context = new WebAppContext();
_context.setBaseResource(webapp);
@ -55,16 +67,39 @@ public class WebAppClassLoaderTest
_loader.addClassPath(webapp.addPath("WEB-INF/classes"));
_loader.setName("test");
}
public void assertCanLoadClass(String clazz) throws ClassNotFoundException
{
assertThat("Can Load Class ["+clazz+"]", _loader.loadClass(clazz), notNullValue());
}
public void assertCanLoadResource(String res) throws ClassNotFoundException
{
assertThat("Can Load Resource ["+res+"]", _loader.getResource(res), notNullValue());
}
public void assertCantLoadClass(String clazz)
{
try
{
assertThat("Can't Load Class ["+clazz+"]", _loader.loadClass(clazz), nullValue());
}
catch (ClassNotFoundException e)
{
// Valid path
}
}
@Test
public void testParentLoad() throws Exception
{
_context.setParentLoaderPriority(true);
assertTrue(canLoadClass("org.acme.webapp.ClassInJarA"));
assertTrue(canLoadClass("org.acme.webapp.ClassInJarB"));
assertTrue(canLoadClass("org.acme.other.ClassInClassesC"));
assertCanLoadClass("org.acme.webapp.ClassInJarA");
assertCanLoadClass("org.acme.webapp.ClassInJarB");
assertCanLoadClass("org.acme.other.ClassInClassesC");
assertTrue(cantLoadClass("org.eclipse.jetty.webapp.Configuration"));
assertCantLoadClass("org.eclipse.jetty.webapp.Configuration");
Class<?> clazzA = _loader.loadClass("org.acme.webapp.ClassInJarA");
assertTrue(clazzA.getField("FROM_PARENT")!=null);
@ -74,22 +109,15 @@ public class WebAppClassLoaderTest
public void testWebAppLoad() throws Exception
{
_context.setParentLoaderPriority(false);
assertTrue(canLoadClass("org.acme.webapp.ClassInJarA"));
assertTrue(canLoadClass("org.acme.webapp.ClassInJarB"));
assertTrue(canLoadClass("org.acme.other.ClassInClassesC"));
assertCanLoadClass("org.acme.webapp.ClassInJarA");
assertCanLoadClass("org.acme.webapp.ClassInJarB");
assertCanLoadClass("org.acme.other.ClassInClassesC");
assertTrue(cantLoadClass("org.eclipse.jetty.webapp.Configuration"));
assertCantLoadClass("org.eclipse.jetty.webapp.Configuration");
Class<?> clazzA = _loader.loadClass("org.acme.webapp.ClassInJarA");
try
{
clazzA.getField("FROM_PARENT");
assertTrue(false);
}
catch(NoSuchFieldException e)
{
assertTrue(true);
}
expectedException.expect(NoSuchFieldException.class);
clazzA.getField("FROM_PARENT");
}
@Test
@ -123,20 +151,20 @@ public class WebAppClassLoaderTest
});
_context.setParentLoaderPriority(false);
assertTrue(canLoadClass("org.acme.webapp.ClassInJarA"));
assertTrue(canLoadClass("org.acme.webapp.ClassInJarB"));
assertTrue(canLoadClass("org.acme.other.ClassInClassesC"));
assertTrue(canLoadClass("java.lang.String"));
assertTrue(cantLoadClass("org.eclipse.jetty.webapp.Configuration"));
Iterator<Object> iter = results.iterator();
assertEquals(_loader,iter.next());
assertEquals("org.acme.webapp.ClassInJarA",iter.next());
assertEquals(_loader,iter.next());
assertEquals("org.acme.webapp.ClassInJarB",iter.next());
assertEquals(_loader,iter.next());
assertEquals("org.acme.other.ClassInClassesC",iter.next());
assertFalse(iter.hasNext());
assertCanLoadClass("org.acme.webapp.ClassInJarA");
assertCanLoadClass("org.acme.webapp.ClassInJarB");
assertCanLoadClass("org.acme.other.ClassInClassesC");
assertCanLoadClass("java.lang.String");
assertCantLoadClass("org.eclipse.jetty.webapp.Configuration");
assertThat("Classname Results", results, contains(
_loader,
"org.acme.webapp.ClassInJarA",
_loader,
"org.acme.webapp.ClassInJarB",
_loader,
"org.acme.other.ClassInClassesC"));
}
@Test
@ -151,8 +179,7 @@ public class WebAppClassLoaderTest
}
});
assertTrue(canLoadClass("org.acme.webapp.ClassInJarA"));
assertCanLoadClass("org.acme.webapp.ClassInJarA");
}
@Test
@ -164,12 +191,12 @@ public class WebAppClassLoaderTest
System.arraycopy(oldSC,0,newSC,1,oldSC.length);
_context.setServerClasses(newSC);
assertTrue(canLoadClass("org.acme.webapp.ClassInJarA"));
assertTrue(canLoadClass("org.acme.webapp.ClassInJarB"));
assertTrue(canLoadClass("org.acme.other.ClassInClassesC"));
assertCanLoadClass("org.acme.webapp.ClassInJarA");
assertCanLoadClass("org.acme.webapp.ClassInJarB");
assertCanLoadClass("org.acme.other.ClassInClassesC");
assertTrue(canLoadClass("org.eclipse.jetty.webapp.Configuration"));
assertTrue(cantLoadClass("org.eclipse.jetty.webapp.JarScanner"));
assertCanLoadClass("org.eclipse.jetty.webapp.Configuration");
assertCantLoadClass("org.eclipse.jetty.webapp.JarScanner");
}
@Test
@ -187,11 +214,11 @@ public class WebAppClassLoaderTest
System.arraycopy(oldSysC,0,newSysC,1,oldSysC.length);
_context.setSystemClasses(newSysC);
assertTrue(canLoadClass("org.acme.webapp.ClassInJarA"));
assertTrue(canLoadClass("org.acme.webapp.ClassInJarB"));
assertTrue(canLoadClass("org.acme.other.ClassInClassesC"));
assertTrue(cantLoadClass("org.eclipse.jetty.webapp.Configuration"));
assertTrue(cantLoadClass("org.eclipse.jetty.webapp.JarScanner"));
assertCanLoadClass("org.acme.webapp.ClassInJarA");
assertCanLoadClass("org.acme.webapp.ClassInJarB");
assertCanLoadClass("org.acme.other.ClassInClassesC");
assertCantLoadClass("org.eclipse.jetty.webapp.Configuration");
assertCantLoadClass("org.eclipse.jetty.webapp.JarScanner");
oldSysC=_context.getSystemClasses();
newSysC=new String[oldSysC.length+1];
@ -199,7 +226,7 @@ public class WebAppClassLoaderTest
System.arraycopy(oldSysC,0,newSysC,1,oldSysC.length);
_context.setSystemClasses(newSysC);
assertNotNull(_loader.getResource("org/acme/webapp/ClassInJarA.class"));
assertCanLoadResource("org/acme/webapp/ClassInJarA.class");
_context.setSystemClasses(oldSysC);
oldServC=_context.getServerClasses();
@ -207,27 +234,53 @@ public class WebAppClassLoaderTest
newServC[0]="org.acme.webapp.ClassInJarA";
System.arraycopy(oldServC,0,newServC,1,oldServC.length);
_context.setServerClasses(newServC);
assertNotNull(_loader.getResource("org/acme/webapp/ClassInJarA.class"));
assertCanLoadResource("org/acme/webapp/ClassInJarA.class");
}
@Test
public void testResources() throws Exception
{
List<URL> expected = new ArrayList<>();
List<URL> resources;
// Expected Locations
URL webappWebInfLibAcme = new URI("jar:" + testWebappDir.resolve("WEB-INF/lib/acme.jar").toUri().toASCIIString() + "!/org/acme/resource.txt").toURL();
URL webappWebInfClasses = testWebappDir.resolve("WEB-INF/classes/org/acme/resource.txt").toUri().toURL();
URL targetTestClasses = MavenTestingUtils.getTargetDir().toPath().resolve("test-classes/org/acme/resource.txt").toUri().toURL();
_context.setParentLoaderPriority(false);
resources =toList( _loader.getResources("org/acme/resource.txt"));
assertEquals(3,resources.size());
assertEquals(0,resources.get(0).toString().indexOf("jar:file:"));
assertEquals(-1,resources.get(1).toString().indexOf("test-classes"));
assertEquals(0,resources.get(2).toString().indexOf("file:"));
// dump(_context);
resources =Collections.list(_loader.getResources("org/acme/resource.txt"));
expected.clear();
expected.add(webappWebInfLibAcme);
expected.add(webappWebInfClasses);
expected.add(targetTestClasses);
assertOrdered("Resources Found (Parent Loader Priority == false)",expected,resources);
// dump(resources);
// assertEquals(3,resources.size());
// assertEquals(0,resources.get(0).toString().indexOf("jar:file:"));
// assertEquals(-1,resources.get(1).toString().indexOf("test-classes"));
// assertEquals(0,resources.get(2).toString().indexOf("file:"));
_context.setParentLoaderPriority(true);
resources =toList( _loader.getResources("org/acme/resource.txt"));
assertEquals(3,resources.size());
assertEquals(0,resources.get(0).toString().indexOf("file:"));
assertEquals(0,resources.get(1).toString().indexOf("jar:file:"));
assertEquals(-1,resources.get(2).toString().indexOf("test-classes"));
// dump(_context);
resources =Collections.list(_loader.getResources("org/acme/resource.txt"));
expected.clear();
expected.add(targetTestClasses);
expected.add(webappWebInfLibAcme);
expected.add(webappWebInfClasses);
assertOrdered("Resources Found (Parent Loader Priority == true)",expected,resources);
// dump(resources);
// assertEquals(3,resources.size());
// assertEquals(0,resources.get(0).toString().indexOf("file:"));
// assertEquals(0,resources.get(1).toString().indexOf("jar:file:"));
// assertEquals(-1,resources.get(2).toString().indexOf("test-classes"));
String[] oldServC=_context.getServerClasses();
String[] newServC=new String[oldServC.length+1];
@ -236,10 +289,19 @@ public class WebAppClassLoaderTest
_context.setServerClasses(newServC);
_context.setParentLoaderPriority(true);
resources =toList( _loader.getResources("org/acme/resource.txt"));
assertEquals(2,resources.size());
assertEquals(0,resources.get(0).toString().indexOf("jar:file:"));
assertEquals(0,resources.get(1).toString().indexOf("file:"));
// dump(_context);
resources =Collections.list(_loader.getResources("org/acme/resource.txt"));
expected.clear();
expected.add(webappWebInfLibAcme);
expected.add(webappWebInfClasses);
assertOrdered("Resources Found (Parent Loader Priority == true) (with serverClasses filtering)",expected,resources);
// dump(resources);
// assertEquals(2,resources.size());
// assertEquals(0,resources.get(0).toString().indexOf("jar:file:"));
// assertEquals(0,resources.get(1).toString().indexOf("file:"));
_context.setServerClasses(oldServC);
String[] oldSysC=_context.getSystemClasses();
@ -249,33 +311,120 @@ public class WebAppClassLoaderTest
_context.setSystemClasses(newSysC);
_context.setParentLoaderPriority(true);
resources =toList( _loader.getResources("org/acme/resource.txt"));
assertEquals(1,resources.size());
assertEquals(0,resources.get(0).toString().indexOf("file:"));
// dump(_context);
resources =Collections.list(_loader.getResources("org/acme/resource.txt"));
expected.clear();
expected.add(targetTestClasses);
assertOrdered("Resources Found (Parent Loader Priority == true) (with systemClasses filtering)",expected,resources);
// dump(resources);
// assertEquals(1,resources.size());
// assertEquals(0,resources.get(0).toString().indexOf("file:"));
}
private List<URL> toList(Enumeration<URL> e)
private void dump(WebAppContext wac)
{
List<URL> list = new ArrayList<URL>();
while (e!=null && e.hasMoreElements())
list.add(e.nextElement());
return list;
System.err.println("--Dump WebAppContext - "+wac);
System.err.printf(" context.getClass().getClassLoader() = %s%n",wac.getClass().getClassLoader());
dumpClassLoaderHierarchy(" ", wac.getClass().getClassLoader());
System.err.printf(" context.getClassLoader() = %s%n",wac.getClassLoader());
dumpClassLoaderHierarchy(" ", wac.getClassLoader());
}
private boolean canLoadClass(String clazz) throws ClassNotFoundException
private void dumpClassLoaderHierarchy(String indent, ClassLoader classLoader)
{
return _loader.loadClass(clazz)!=null;
}
private boolean cantLoadClass(String clazz)
{
try
if (classLoader != null)
{
return _loader.loadClass(clazz)==null;
if(classLoader instanceof URLClassLoader)
{
URLClassLoader urlCL = (URLClassLoader)classLoader;
URL urls[] = urlCL.getURLs();
for (URL url : urls)
{
System.err.printf("%s url[] = %s%n",indent,url);
}
}
ClassLoader parent = classLoader.getParent();
if (parent != null)
{
System.err.printf("%s .parent = %s%n",indent,parent);
dumpClassLoaderHierarchy(indent + " ",parent);
}
}
catch(ClassNotFoundException e)
}
private void dump(List<URL> resources)
{
System.err.println("--Dump--");
for(URL url: resources)
{
return true;
System.err.printf(" \"%s\"%n",url);
}
}
/**
* Developer Friendly list order assertion, with clear error messages indicating the full state of the expected and actual lists, along with highlighting of the problem areas.
* @param msg the message in case of error
* @param expectedList the expected list
* @param actualList the actual list
*/
public static void assertOrdered(String msg, List<?> expectedList, List<?> actualList)
{
// same size?
boolean mismatch = expectedList.size() != actualList.size();
// test content
List<Integer> badEntries = new ArrayList<>();
int min = Math.min(expectedList.size(),actualList.size());
int max = Math.max(expectedList.size(),actualList.size());
for (int i = 0; i < min; i++)
{
if (!expectedList.get(i).equals(actualList.get(i)))
{
badEntries.add(i);
}
}
for (int i = min; i < max; i++)
{
badEntries.add(i);
}
if (mismatch || badEntries.size() > 0)
{
// build up detailed error message
StringWriter message = new StringWriter();
PrintWriter err = new PrintWriter(message);
err.printf("%s: Assert Contains (Ordered)",msg);
if (mismatch)
{
err.print(" [size mismatch]");
}
if (badEntries.size() >= 0)
{
err.printf(" [%d entries not matched]",badEntries.size());
}
err.println();
err.printf("Actual Entries (size: %d)%n",actualList.size());
for (int i = 0; i < actualList.size(); i++)
{
Object actualObj = actualList.get(i);
char indicator = badEntries.contains(i)?'>':' ';
err.printf("%s[%d] %s%n",indicator,i,actualObj==null?"<null>":actualObj.toString());
}
err.printf("Expected Entries (size: %d)%n",expectedList.size());
for (int i = 0; i < expectedList.size(); i++)
{
Object expectedObj = expectedList.get(i).toString();
char indicator = badEntries.contains(i)?'>':' ';
err.printf("%s[%d] %s%n",indicator,i,expectedObj==null?"<null>":expectedObj.toString());
}
err.flush();
Assert.fail(message.toString());
}
}
}