Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug: ParentRunner lost test Class from a separate class loader #1252

Merged
merged 3 commits into from
Sep 17, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/main/java/org/junit/runner/Description.java
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,17 @@ public static Description createSuiteDescription(Class<?> testClass) {
return new Description(testClass, testClass.getName(), testClass.getAnnotations());
}

/**
* Create a <code>Description</code> named after <code>testClass</code>
*
* @param testClass A not null {@link Class} containing tests
* @param annotations meta-data about the test, for downstream interpreters
* @return a <code>Description</code> of <code>testClass</code>
*/
public static Description createSuiteDescription(Class<?> testClass, Annotation... annotations) {
return new Description(testClass, testClass.getName(), annotations);
}

/**
* Describes a Runner which runs no tests
*/
Expand Down
12 changes: 10 additions & 2 deletions src/main/java/org/junit/runners/ParentRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -346,8 +346,16 @@ protected Annotation[] getRunnerAnnotations() {

@Override
public Description getDescription() {
Description description = Description.createSuiteDescription(getName(),
getRunnerAnnotations());
Class<?> clazz = getTestClass().getJavaClass();
Description description;
// if subclass overrides `getName()` then we should use it
// to maintain backwards compatibility with JUnit 4.12
if (clazz == null || !clazz.getName().equals(getName())) {
description = Description.createSuiteDescription(getName(), getRunnerAnnotations());
} else {
description = Description.createSuiteDescription(clazz, getRunnerAnnotations());
}

for (T child : getFilteredChildren()) {
description.addChild(describeChild(child));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
import org.junit.tests.running.classes.parent.ParentRunnerClassLoaderTest;

@RunWith(Suite.class)
@SuiteClasses({
Expand All @@ -13,6 +14,7 @@
ParameterizedTestTest.class,
ParentRunnerFilteringTest.class,
ParentRunnerTest.class,
ParentRunnerClassLoaderTest.class,
RunWithTest.class,
SuiteTest.class,
UseSuiteAsASuperclassTest.class
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package org.junit.tests.running.classes.parent;


import org.junit.Test;
import org.junit.runner.Description;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.ParentRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;

import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;

import static org.junit.Assert.assertEquals;

public class ParentRunnerClassLoaderTest {
@Test
public void testClassRuleAccessToClassInAnotherClassLoader() throws Exception {
Class<?> testClassWithOwnClassLoader = wrapToClassLoader(TestWithClassRule.class);

runTestWithParentRunner(testClassWithOwnClassLoader);

Field fieldWithReference = testClassWithOwnClassLoader.getDeclaredField("applyTestClass");
Class<?> usedClass = (Class<?>) fieldWithReference.get(null);

assertEquals("JUnitRunner can be located in own classLoader, so, " +
"Class.forName org.junit.runner.Description.getTestClass can not see " +
"in current classloader by execute Class.forName",
testClassWithOwnClassLoader, usedClass
);
}

@Test
public void testDescriptionContainCorrectTestClass() throws Exception {
Class<?> testClassWithOwnClassLoader = wrapToClassLoader(TestWithClassRule.class);
ParentRunner<?> runner = new BlockJUnit4ClassRunner(testClassWithOwnClassLoader);

Description description = runner.getDescription();
assertEquals("ParentRunner accept already instantiate Class<?> with tests, if we lost it instance, and will " +
"use Class.forName we can not find test class again, because tests can be " +
"located in different ClassLoader",
description.getTestClass(), testClassWithOwnClassLoader
);
}

@Test
public void testBackwardCompatibilityWithOverrideGetName() throws Exception {
final Class<TestWithClassRule> originalTestClass = TestWithClassRule.class;
final Class<?> waitClass = ParentRunnerClassLoaderTest.class;

ParentRunner<FrameworkMethod> subParentRunner = new BlockJUnit4ClassRunner(originalTestClass) {
@Override
protected String getName() {
return waitClass.getName();
}
};

Description description = subParentRunner.getDescription();
Class<?> result = description.getTestClass();

assertEquals("Subclass of ParentRunner can override getName method and specify another test class for run, " +
"we should maintain backwards compatibility with JUnit 4.12",
waitClass, result
);
}

private void runTestWithParentRunner(Class<?> testClass) throws InitializationError {
ParentRunner<?> runner = new BlockJUnit4ClassRunner(testClass);
runner.run(new RunNotifier());
}

private Class<?> wrapToClassLoader(Class<?> sourceClass) throws ClassNotFoundException {
URL classpath = sourceClass.getProtectionDomain().getCodeSource().getLocation();
VisibleClassLoader loader = new VisibleClassLoader(new URL[]{classpath}, this.getClass().getClassLoader());
Class<?> testClassWithOwnClassLoader = loader.findClass(sourceClass.getName());

assert testClassWithOwnClassLoader != sourceClass;

return testClassWithOwnClassLoader;
}


private static class VisibleClassLoader extends URLClassLoader {
public VisibleClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}

@Override // just making public
public Class<?> findClass(String name) throws ClassNotFoundException {
return super.findClass(name);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.junit.tests.running.classes.parent;

import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

import java.lang.reflect.Field;

/**
* Test class for validate run tests that was load in own ClassLoader
*/
public class TestWithClassRule {
public static Class<?> applyTestClass;

@ClassRule
public static TestRule rule = new CustomRule();

@Test
public void testClassRuleExecuted() throws Exception {
Assert.assertNotNull("Description should contain reference to TestClass", applyTestClass);
}

public static final class CustomRule implements TestRule {

public Statement apply(final Statement base, final Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
Class<?> testClass = description.getTestClass();
if(testClass != null) {
Field field = testClass.getDeclaredField("applyTestClass");
field.set(null, description.getTestClass());
}
base.evaluate();
}
};
}
}
}