Skip to content

Commit

Permalink
Verify that test classes are public.
Browse files Browse the repository at this point in the history
Test classes should be public in order to avoid problems with
reflection caused by the security manager. Public classes are always
accesible.
  • Loading branch information
stefanbirkner committed Apr 22, 2014
1 parent 63743d5 commit 1d97da7
Show file tree
Hide file tree
Showing 9 changed files with 152 additions and 6 deletions.
18 changes: 15 additions & 3 deletions src/main/java/org/junit/runners/ParentRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
Expand Down Expand Up @@ -38,6 +39,8 @@
import org.junit.runners.model.Statement;
import org.junit.runners.model.TestClass;
import org.junit.validator.AnnotationsValidator;
import org.junit.validator.PublicClassValidator;
import org.junit.validator.TestClassValidator;

/**
* Provides most of the functionality specific to a Runner that implements a
Expand All @@ -54,14 +57,15 @@
*/
public abstract class ParentRunner<T> extends Runner implements Filterable,
Sortable {
private static final List<TestClassValidator> VALIDATORS = Arrays.asList(
new AnnotationsValidator(), new PublicClassValidator());

private final Object fChildrenLock = new Object();
private final TestClass fTestClass;

// Guarded by fChildrenLock
private volatile Collection<T> fFilteredChildren = null;

private final AnnotationsValidator fAnnotationsValidator= new AnnotationsValidator();

private volatile RunnerScheduler fScheduler = new RunnerScheduler() {
public void schedule(Runnable childStatement) {
childStatement.run();
Expand Down Expand Up @@ -121,7 +125,15 @@ protected void collectInitializationErrors(List<Throwable> errors) {
validatePublicVoidNoArgMethods(BeforeClass.class, true, errors);
validatePublicVoidNoArgMethods(AfterClass.class, true, errors);
validateClassRules(errors);
errors.addAll(fAnnotationsValidator.validateTestClass(getTestClass()));
applyValidators(errors);
}

private void applyValidators(List<Throwable> errors) {
if (getTestClass().getJavaClass() != null) {
for (TestClassValidator each : VALIDATORS) {
errors.addAll(each.validateTestClass(getTestClass()));
}
}
}

/**
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/org/junit/runners/model/TestClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
Expand Down Expand Up @@ -248,6 +249,10 @@ public <T> List<T> getAnnotatedMethodValues(Object test,
return results;
}

public boolean isPublic() {
return Modifier.isPublic(fClass.getModifiers());
}

public boolean isANonStaticInnerClass() {
return fClass.isMemberClass() && !isStatic(fClass.getModifiers());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
*
* @since 4.12
*/
public final class AnnotationsValidator {
public final class AnnotationsValidator implements TestClassValidator {
private static final List<AnnotatableValidator<?>> VALIDATORS = Arrays.<AnnotatableValidator<?>>asList(
new ClassValidator(), new MethodValidator(), new FieldValidator());

Expand Down
33 changes: 33 additions & 0 deletions src/main/java/org/junit/validator/PublicClassValidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package org.junit.validator;

import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;

import java.util.List;

import org.junit.runners.model.TestClass;

/**
* Validates that a {@link TestClass} is public.
*
* @since 4.12
*/
public class PublicClassValidator implements TestClassValidator {
private static final List<Exception> NO_VALIDATION_ERRORS = emptyList();

/**
* Validate that the specified {@link TestClass} is public.
*
* @param testClass the {@link TestClass} that is validated.
* @return an empty list if the class is public or a list with a single
* exception otherwise.
*/
public List<Exception> validateTestClass(TestClass testClass) {
if (testClass.isPublic()) {
return NO_VALIDATION_ERRORS;
} else {
return singletonList(new Exception("The class "
+ testClass.getName() + " is not public."));
}
}
}
21 changes: 21 additions & 0 deletions src/main/java/org/junit/validator/TestClassValidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.junit.validator;

import java.util.List;

import org.junit.runners.model.TestClass;

/**
* Validates a single facet of a test class.
*
* @since 4.12
*/
public interface TestClassValidator {
/**
* Validate a single facet of a test class.
*
* @param testClass
* the {@link TestClass} that is validated.
* @return the validation errors found by the validator.
*/
public List<Exception> validateTestClass(TestClass testClass);
}
20 changes: 20 additions & 0 deletions src/test/java/org/junit/runners/model/TestClassTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -201,4 +201,24 @@ public void hasHashCodeWithoutJavaClass() {
testClass.hashCode();
// everything is fine if no exception is thrown.
}

public static class PublicClass {

}

@Test
public void identifiesPublicModifier() {
TestClass tc = new TestClass(PublicClass.class);
assertEquals("Wrong flag 'public',", true, tc.isPublic());
}

static class NonPublicClass {

}

@Test
public void identifiesNonPublicModifier() {
TestClass tc = new TestClass(NonPublicClass.class);
assertEquals("Wrong flag 'public',", false, tc.isPublic());
}
}
4 changes: 3 additions & 1 deletion src/test/java/org/junit/tests/AllTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
import org.junit.tests.validation.FailedConstructionTest;
import org.junit.tests.validation.InaccessibleBaseClassTest;
import org.junit.tests.validation.ValidationTest;
import org.junit.validator.PublicClassValidatorTest;

// These test files need to be cleaned. See
// https://sourceforge.net/pm/task.php?func=detailtask&project_task_id=136507&group_id=15278&group_project_id=51407
Expand Down Expand Up @@ -204,7 +205,8 @@
FrameworkMethodTest.class,
FailOnTimeoutTest.class,
JUnitCoreTest.class,
TestWithParametersTest.class
TestWithParametersTest.class,
PublicClassValidatorTest.class
})
public class AllTests {
public static Test suite() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public boolean matchesSafely(List<?> item) {
}

private static class Exclude extends Filter {
private String methodName;
private final String methodName;

public Exclude(String methodName) {
this.methodName = methodName;
Expand Down Expand Up @@ -127,6 +127,18 @@ public void failWithHelpfulMessageForNonStaticClassRule() {
"The @ClassRule 'temporaryFolder' must be static.");
}

static class NonPublicTestClass {
public NonPublicTestClass() {
}
}

@Test
public void cannotBeCreatedWithNonPublicTestClass() {
assertClassHasFailureMessage(
NonPublicTestClass.class,
"The class org.junit.tests.running.classes.ParentRunnerTest$NonPublicTestClass is not public.");
}

private void assertClassHasFailureMessage(Class<?> klass, String message) {
JUnitCore junitCore = new JUnitCore();
Request request = Request.aClass(klass);
Expand Down
41 changes: 41 additions & 0 deletions src/test/java/org/junit/validator/PublicClassValidatorTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package org.junit.validator;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;

import java.util.Collections;
import java.util.List;

import org.junit.Test;
import org.junit.runners.model.TestClass;

public class PublicClassValidatorTest {
private final PublicClassValidator validator = new PublicClassValidator();

public static class PublicClass {

}

@Test
public void acceptsPublicClass() {
TestClass testClass = new TestClass(PublicClass.class);
List<Exception> validationErrors = validator
.validateTestClass(testClass);
assertThat(validationErrors,
is(equalTo(Collections.<Exception> emptyList())));
}

static class NonPublicClass {

}

@Test
public void rejectsNonPublicClass() {
TestClass testClass = new TestClass(NonPublicClass.class);
List<Exception> validationErrors = validator
.validateTestClass(testClass);
assertThat("Wrong number of errors.", validationErrors.size(),
is(equalTo(1)));
}
}

0 comments on commit 1d97da7

Please sign in to comment.