From 06bbb4ece2edf62abe125f0392289a0cf52baac4 Mon Sep 17 00:00:00 2001 From: Peter Wippermann Date: Thu, 7 Jul 2016 15:38:43 +0200 Subject: [PATCH 01/13] Converted useful object methods to class methods for later reuse --- .../java/org/junit/runners/Parameterized.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/junit/runners/Parameterized.java b/src/main/java/org/junit/runners/Parameterized.java index cc7c804f4daa..e509f2130e35 100644 --- a/src/main/java/org/junit/runners/Parameterized.java +++ b/src/main/java/org/junit/runners/Parameterized.java @@ -256,10 +256,10 @@ private RunnersFactory(Class klass) { } private List createRunners() throws Throwable { - Parameters parameters = getParametersMethod().getAnnotation( + Parameters parameters = getParametersMethod(testClass).getAnnotation( Parameters.class); return Collections.unmodifiableList(createRunnersForParameters( - allParameters(), parameters.name(), + allParameters(testClass), parameters.name(), getParametersRunnerFactory())); } @@ -285,18 +285,18 @@ private TestWithParameters createTestWithNotNormalizedParameters( } @SuppressWarnings("unchecked") - private Iterable allParameters() throws Throwable { - Object parameters = getParametersMethod().invokeExplosively(null); + public static Iterable allParameters(TestClass testClass) throws Throwable { + Object parameters = getParametersMethod(testClass).invokeExplosively(null); if (parameters instanceof Iterable) { return (Iterable) parameters; } else if (parameters instanceof Object[]) { return Arrays.asList((Object[]) parameters); } else { - throw parametersMethodReturnedWrongType(); + throw parametersMethodReturnedWrongType(testClass); } } - private FrameworkMethod getParametersMethod() throws Exception { + public static FrameworkMethod getParametersMethod(TestClass testClass) throws Exception { List methods = testClass .getAnnotatedMethods(Parameters.class); for (FrameworkMethod each : methods) { @@ -322,7 +322,7 @@ private List createRunnersForParameters( } return runners; } catch (ClassCastException e) { - throw parametersMethodReturnedWrongType(); + throw parametersMethodReturnedWrongType(this.testClass); } } @@ -338,9 +338,9 @@ private List createTestsForParameters( return children; } - private Exception parametersMethodReturnedWrongType() throws Exception { + private static Exception parametersMethodReturnedWrongType(TestClass testClass) throws Exception { String className = testClass.getName(); - String methodName = getParametersMethod().getName(); + String methodName = getParametersMethod(testClass).getName(); String message = MessageFormat.format( "{0}.{1}() must return an Iterable of arrays.", className, methodName); From 1f04d9de50a8c536d5b9437fb033f0efa54e7159 Mon Sep 17 00:00:00 2001 From: Peter Wippermann Date: Fri, 8 Jul 2016 20:12:01 +0200 Subject: [PATCH 02/13] extracted new helper methods; moved helper methods from nested RunnerFactory class to top-level Parameterized class to be accessible from other classes. --- .../java/org/junit/runners/Parameterized.java | 97 +++++++++++-------- 1 file changed, 54 insertions(+), 43 deletions(-) diff --git a/src/main/java/org/junit/runners/Parameterized.java b/src/main/java/org/junit/runners/Parameterized.java index e509f2130e35..e655f19847c8 100644 --- a/src/main/java/org/junit/runners/Parameterized.java +++ b/src/main/java/org/junit/runners/Parameterized.java @@ -256,10 +256,10 @@ private RunnersFactory(Class klass) { } private List createRunners() throws Throwable { - Parameters parameters = getParametersMethod(testClass).getAnnotation( + Parameters parameters = Parameterized.getParametersMethod(testClass).getAnnotation( Parameters.class); return Collections.unmodifiableList(createRunnersForParameters( - allParameters(testClass), parameters.name(), + Parameterized.allParameters(testClass), parameters.name(), getParametersRunnerFactory())); } @@ -278,37 +278,11 @@ private ParametersRunnerFactory getParametersRunnerFactory() private TestWithParameters createTestWithNotNormalizedParameters( String pattern, int index, Object parametersOrSingleParameter) { - Object[] parameters = (parametersOrSingleParameter instanceof Object[]) ? (Object[]) parametersOrSingleParameter - : new Object[] { parametersOrSingleParameter }; + Object[] parameters = Parameterized.normalizeParameter(parametersOrSingleParameter); return createTestWithParameters(testClass, pattern, index, parameters); } - @SuppressWarnings("unchecked") - public static Iterable allParameters(TestClass testClass) throws Throwable { - Object parameters = getParametersMethod(testClass).invokeExplosively(null); - if (parameters instanceof Iterable) { - return (Iterable) parameters; - } else if (parameters instanceof Object[]) { - return Arrays.asList((Object[]) parameters); - } else { - throw parametersMethodReturnedWrongType(testClass); - } - } - - public static FrameworkMethod getParametersMethod(TestClass testClass) throws Exception { - List methods = testClass - .getAnnotatedMethods(Parameters.class); - for (FrameworkMethod each : methods) { - if (each.isStatic() && each.isPublic()) { - return each; - } - } - - throw new Exception("No public static parameters method on class " - + testClass.getName()); - } - private List createRunnersForParameters( Iterable allParameters, String namePattern, ParametersRunnerFactory runnerFactory) throws Exception { @@ -322,7 +296,7 @@ private List createRunnersForParameters( } return runners; } catch (ClassCastException e) { - throw parametersMethodReturnedWrongType(this.testClass); + throw Parameterized.parametersMethodReturnedWrongType(this.testClass); } } @@ -338,23 +312,60 @@ private List createTestsForParameters( return children; } - private static Exception parametersMethodReturnedWrongType(TestClass testClass) throws Exception { - String className = testClass.getName(); - String methodName = getParametersMethod(testClass).getName(); - String message = MessageFormat.format( - "{0}.{1}() must return an Iterable of arrays.", className, - methodName); - return new Exception(message); - } - private TestWithParameters createTestWithParameters( TestClass testClass, String pattern, int index, Object[] parameters) { - String finalPattern = pattern.replaceAll("\\{index\\}", - Integer.toString(index)); - String name = MessageFormat.format(finalPattern, parameters); - return new TestWithParameters("[" + name + "]", testClass, + String testName = Parameterized.produceTestName(pattern, index, parameters); + return new TestWithParameters(testName, testClass, Arrays.asList(parameters)); } } + + @SuppressWarnings("unchecked") + public static Iterable allParameters(TestClass testClass) throws Throwable { + Object parameters = getParametersMethod(testClass).invokeExplosively(null); + if (parameters instanceof Iterable) { + return (Iterable) parameters; + } else if (parameters instanceof Object[]) { + return Arrays.asList((Object[]) parameters); + } else { + throw parametersMethodReturnedWrongType(testClass); + } + } + + public static FrameworkMethod getParametersMethod(TestClass testClass) throws Exception { + List methods = testClass + .getAnnotatedMethods(Parameters.class); + for (FrameworkMethod each : methods) { + if (each.isStatic() && each.isPublic()) { + return each; + } + } + + throw new Exception("No public static parameters method on class " + + testClass.getName()); + } + + private static Exception parametersMethodReturnedWrongType(TestClass testClass) throws Exception { + String className = testClass.getName(); + String methodName = getParametersMethod(testClass).getName(); + String message = MessageFormat.format( + "{0}.{1}() must return an Iterable of arrays.", className, + methodName); + return new Exception(message); + } + + public static Object[] normalizeParameter(Object parametersOrSingleParameter) { + return (parametersOrSingleParameter instanceof Object[]) ? (Object[]) parametersOrSingleParameter + : new Object[] { parametersOrSingleParameter }; + } + + public static String produceTestName(String pattern, int index, + Object[] parameters) { + String finalPattern = pattern.replaceAll("\\{index\\}", + Integer.toString(index)); + String name = MessageFormat.format(finalPattern, parameters); + String testName = "[" + name + "]"; + return testName; + } } From d4e9cf4682a0f21b74fc5f265fe0d6c5758c3046 Mon Sep 17 00:00:00 2001 From: Peter Wippermann Date: Fri, 8 Jul 2016 20:34:18 +0200 Subject: [PATCH 03/13] extracted getNamePatternForParameters() --- .../java/org/junit/runners/Parameterized.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/junit/runners/Parameterized.java b/src/main/java/org/junit/runners/Parameterized.java index e655f19847c8..76b4f1e2bc56 100644 --- a/src/main/java/org/junit/runners/Parameterized.java +++ b/src/main/java/org/junit/runners/Parameterized.java @@ -256,10 +256,8 @@ private RunnersFactory(Class klass) { } private List createRunners() throws Throwable { - Parameters parameters = Parameterized.getParametersMethod(testClass).getAnnotation( - Parameters.class); return Collections.unmodifiableList(createRunnersForParameters( - Parameterized.allParameters(testClass), parameters.name(), + Parameterized.allParameters(testClass), Parameterized.getNamePatternForParameters(testClass), getParametersRunnerFactory())); } @@ -333,7 +331,7 @@ public static Iterable allParameters(TestClass testClass) throws Throwab } } - public static FrameworkMethod getParametersMethod(TestClass testClass) throws Exception { + private static FrameworkMethod getParametersMethod(TestClass testClass) throws Exception { List methods = testClass .getAnnotatedMethods(Parameters.class); for (FrameworkMethod each : methods) { @@ -364,8 +362,12 @@ public static String produceTestName(String pattern, int index, Object[] parameters) { String finalPattern = pattern.replaceAll("\\{index\\}", Integer.toString(index)); - String name = MessageFormat.format(finalPattern, parameters); - String testName = "[" + name + "]"; - return testName; + return "[" + MessageFormat.format(finalPattern, parameters) + "]"; + } + + public static String getNamePatternForParameters(TestClass testClass) throws Exception { + Parameters parameters = getParametersMethod(testClass).getAnnotation( + Parameters.class); + return parameters.name(); } } From d3d5f9c4f34003d0726b2bb7b7440cac15537faa Mon Sep 17 00:00:00 2001 From: Peter Wippermann Date: Fri, 8 Jul 2016 20:48:26 +0200 Subject: [PATCH 04/13] renamed getAnnotatedClasses() to getSuiteClasses() and made it public --- src/main/java/org/junit/runners/Suite.java | 260 ++++++++++----------- 1 file changed, 130 insertions(+), 130 deletions(-) diff --git a/src/main/java/org/junit/runners/Suite.java b/src/main/java/org/junit/runners/Suite.java index 6652bbcad980..4cb491d4f9ae 100644 --- a/src/main/java/org/junit/runners/Suite.java +++ b/src/main/java/org/junit/runners/Suite.java @@ -1,130 +1,130 @@ -package org.junit.runners; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.Collections; -import java.util.List; - -import org.junit.internal.builders.AllDefaultPossibilitiesBuilder; -import org.junit.runner.Description; -import org.junit.runner.Runner; -import org.junit.runner.notification.RunNotifier; -import org.junit.runners.model.InitializationError; -import org.junit.runners.model.RunnerBuilder; - -/** - * Using Suite as a runner allows you to manually - * build a suite containing tests from many classes. It is the JUnit 4 equivalent of the JUnit 3.8.x - * static {@link junit.framework.Test} suite() method. To use it, annotate a class - * with @RunWith(Suite.class) and @SuiteClasses({TestClass1.class, ...}). - * When you run this class, it will run all the tests in all the suite classes. - * - * @since 4.0 - */ -public class Suite extends ParentRunner { - /** - * Returns an empty suite. - */ - public static Runner emptySuite() { - try { - return new Suite((Class) null, new Class[0]); - } catch (InitializationError e) { - throw new RuntimeException("This shouldn't be possible"); - } - } - - /** - * The SuiteClasses annotation specifies the classes to be run when a class - * annotated with @RunWith(Suite.class) is run. - */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) - @Inherited - public @interface SuiteClasses { - /** - * @return the classes to be run - */ - Class[] value(); - } - - private static Class[] getAnnotatedClasses(Class klass) throws InitializationError { - SuiteClasses annotation = klass.getAnnotation(SuiteClasses.class); - if (annotation == null) { - throw new InitializationError(String.format("class '%s' must have a SuiteClasses annotation", klass.getName())); - } - return annotation.value(); - } - - private final List runners; - - /** - * Called reflectively on classes annotated with @RunWith(Suite.class) - * - * @param klass the root class - * @param builder builds runners for classes in the suite - */ - public Suite(Class klass, RunnerBuilder builder) throws InitializationError { - this(builder, klass, getAnnotatedClasses(klass)); - } - - /** - * Call this when there is no single root class (for example, multiple class names - * passed on the command line to {@link org.junit.runner.JUnitCore} - * - * @param builder builds runners for classes in the suite - * @param classes the classes in the suite - */ - public Suite(RunnerBuilder builder, Class[] classes) throws InitializationError { - this(null, builder.runners(null, classes)); - } - - /** - * Call this when the default builder is good enough. Left in for compatibility with JUnit 4.4. - * - * @param klass the root of the suite - * @param suiteClasses the classes in the suite - */ - protected Suite(Class klass, Class[] suiteClasses) throws InitializationError { - this(new AllDefaultPossibilitiesBuilder(true), klass, suiteClasses); - } - - /** - * Called by this class and subclasses once the classes making up the suite have been determined - * - * @param builder builds runners for classes in the suite - * @param klass the root of the suite - * @param suiteClasses the classes in the suite - */ - protected Suite(RunnerBuilder builder, Class klass, Class[] suiteClasses) throws InitializationError { - this(klass, builder.runners(klass, suiteClasses)); - } - - /** - * Called by this class and subclasses once the runners making up the suite have been determined - * - * @param klass root of the suite - * @param runners for each class in the suite, a {@link Runner} - */ - protected Suite(Class klass, List runners) throws InitializationError { - super(klass); - this.runners = Collections.unmodifiableList(runners); - } - - @Override - protected List getChildren() { - return runners; - } - - @Override - protected Description describeChild(Runner child) { - return child.getDescription(); - } - - @Override - protected void runChild(Runner runner, final RunNotifier notifier) { - runner.run(notifier); - } -} +package org.junit.runners; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.Collections; +import java.util.List; + +import org.junit.internal.builders.AllDefaultPossibilitiesBuilder; +import org.junit.runner.Description; +import org.junit.runner.Runner; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.RunnerBuilder; + +/** + * Using Suite as a runner allows you to manually + * build a suite containing tests from many classes. It is the JUnit 4 equivalent of the JUnit 3.8.x + * static {@link junit.framework.Test} suite() method. To use it, annotate a class + * with @RunWith(Suite.class) and @SuiteClasses({TestClass1.class, ...}). + * When you run this class, it will run all the tests in all the suite classes. + * + * @since 4.0 + */ +public class Suite extends ParentRunner { + /** + * Returns an empty suite. + */ + public static Runner emptySuite() { + try { + return new Suite((Class) null, new Class[0]); + } catch (InitializationError e) { + throw new RuntimeException("This shouldn't be possible"); + } + } + + /** + * The SuiteClasses annotation specifies the classes to be run when a class + * annotated with @RunWith(Suite.class) is run. + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + @Inherited + public @interface SuiteClasses { + /** + * @return the classes to be run + */ + Class[] value(); + } + + public static Class[] getSuiteClasses(Class klass) throws InitializationError { + SuiteClasses annotation = klass.getAnnotation(SuiteClasses.class); + if (annotation == null) { + throw new InitializationError(String.format("class '%s' must have a SuiteClasses annotation", klass.getName())); + } + return annotation.value(); + } + + private final List runners; + + /** + * Called reflectively on classes annotated with @RunWith(Suite.class) + * + * @param klass the root class + * @param builder builds runners for classes in the suite + */ + public Suite(Class klass, RunnerBuilder builder) throws InitializationError { + this(builder, klass, getSuiteClasses(klass)); + } + + /** + * Call this when there is no single root class (for example, multiple class names + * passed on the command line to {@link org.junit.runner.JUnitCore} + * + * @param builder builds runners for classes in the suite + * @param classes the classes in the suite + */ + public Suite(RunnerBuilder builder, Class[] classes) throws InitializationError { + this(null, builder.runners(null, classes)); + } + + /** + * Call this when the default builder is good enough. Left in for compatibility with JUnit 4.4. + * + * @param klass the root of the suite + * @param suiteClasses the classes in the suite + */ + protected Suite(Class klass, Class[] suiteClasses) throws InitializationError { + this(new AllDefaultPossibilitiesBuilder(true), klass, suiteClasses); + } + + /** + * Called by this class and subclasses once the classes making up the suite have been determined + * + * @param builder builds runners for classes in the suite + * @param klass the root of the suite + * @param suiteClasses the classes in the suite + */ + protected Suite(RunnerBuilder builder, Class klass, Class[] suiteClasses) throws InitializationError { + this(klass, builder.runners(klass, suiteClasses)); + } + + /** + * Called by this class and subclasses once the runners making up the suite have been determined + * + * @param klass root of the suite + * @param runners for each class in the suite, a {@link Runner} + */ + protected Suite(Class klass, List runners) throws InitializationError { + super(klass); + this.runners = Collections.unmodifiableList(runners); + } + + @Override + protected List getChildren() { + return runners; + } + + @Override + protected Description describeChild(Runner child) { + return child.getDescription(); + } + + @Override + protected void runChild(Runner runner, final RunNotifier notifier) { + runner.run(notifier); + } +} From 59bd4618928ecd282644b0a12692cf38d198e2a9 Mon Sep 17 00:00:00 2001 From: pwippermann Date: Mon, 18 Jul 2016 22:42:18 +0200 Subject: [PATCH 05/13] minor improvement of variable name --- src/main/java/org/junit/runners/Parameterized.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/junit/runners/Parameterized.java b/src/main/java/org/junit/runners/Parameterized.java index 76b4f1e2bc56..8d08e47559d5 100644 --- a/src/main/java/org/junit/runners/Parameterized.java +++ b/src/main/java/org/junit/runners/Parameterized.java @@ -366,8 +366,8 @@ public static String produceTestName(String pattern, int index, } public static String getNamePatternForParameters(TestClass testClass) throws Exception { - Parameters parameters = getParametersMethod(testClass).getAnnotation( + Parameters parametersAnnotation = getParametersMethod(testClass).getAnnotation( Parameters.class); - return parameters.name(); + return parametersAnnotation.name(); } } From 60ee6940816aa49356082b24114228532449b0c3 Mon Sep 17 00:00:00 2001 From: pwippermann Date: Mon, 18 Jul 2016 22:43:17 +0200 Subject: [PATCH 06/13] Extracted further methods as static for reuse in other Runners --- .../java/org/junit/runners/ParentRunner.java | 23 +- .../BlockJUnit4ClassRunnerWithParameters.java | 352 +++++++++--------- 2 files changed, 196 insertions(+), 179 deletions(-) mode change 100755 => 100644 src/main/java/org/junit/runners/ParentRunner.java diff --git a/src/main/java/org/junit/runners/ParentRunner.java b/src/main/java/org/junit/runners/ParentRunner.java old mode 100755 new mode 100644 index 72be394cc5a4..b1d75c73a3bd --- a/src/main/java/org/junit/runners/ParentRunner.java +++ b/src/main/java/org/junit/runners/ParentRunner.java @@ -189,9 +189,9 @@ private void validateClassRules(List errors) { protected Statement classBlock(final RunNotifier notifier) { Statement statement = childrenInvoker(notifier); if (!areAllChildrenIgnored()) { - statement = withBeforeClasses(statement); - statement = withAfterClasses(statement); - statement = withClassRules(statement); + statement = withBeforeClasses(statement, testClass); + statement = withAfterClasses(statement, testClass); + statement = withClassRules(statement, testClass, getDescription()); } return statement; } @@ -209,8 +209,9 @@ private boolean areAllChildrenIgnored() { * Returns a {@link Statement}: run all non-overridden {@code @BeforeClass} methods on this class * and superclasses before executing {@code statement}; if any throws an * Exception, stop execution and pass the exception on. + * @param testClass */ - protected Statement withBeforeClasses(Statement statement) { + public static Statement withBeforeClasses(Statement statement, TestClass testClass) { List befores = testClass .getAnnotatedMethods(BeforeClass.class); return befores.isEmpty() ? statement : @@ -223,8 +224,9 @@ protected Statement withBeforeClasses(Statement statement) { * always executed: exceptions thrown by previous steps are combined, if * necessary, with exceptions from AfterClass methods into a * {@link org.junit.runners.model.MultipleFailureException}. + * @param testClass */ - protected Statement withAfterClasses(Statement statement) { + public static Statement withAfterClasses(Statement statement, TestClass testClass) { List afters = testClass .getAnnotatedMethods(AfterClass.class); return afters.isEmpty() ? statement : @@ -237,20 +239,23 @@ protected Statement withAfterClasses(Statement statement) { * annotated with {@link ClassRule}. * * @param statement the base statement + * @param testClass + * @param description the description to pass to the {@link Rule}s * @return a RunRules statement if any class-level {@link Rule}s are * found, or the base statement */ - private Statement withClassRules(Statement statement) { - List classRules = classRules(); + public static Statement withClassRules(Statement statement, TestClass testClass, Description description) { + List classRules = classRules(testClass); return classRules.isEmpty() ? statement : - new RunRules(statement, classRules, getDescription()); + new RunRules(statement, classRules, description); } /** + * @param testClass * @return the {@code ClassRule}s that can transform the block that runs * each method in the tested class. */ - protected List classRules() { + protected static List classRules(TestClass testClass) { List result = testClass.getAnnotatedMethodValues(null, ClassRule.class, TestRule.class); result.addAll(testClass.getAnnotatedFieldValues(null, ClassRule.class, TestRule.class)); return result; diff --git a/src/main/java/org/junit/runners/parameterized/BlockJUnit4ClassRunnerWithParameters.java b/src/main/java/org/junit/runners/parameterized/BlockJUnit4ClassRunnerWithParameters.java index ffed4beaf6bb..a588677139b5 100644 --- a/src/main/java/org/junit/runners/parameterized/BlockJUnit4ClassRunnerWithParameters.java +++ b/src/main/java/org/junit/runners/parameterized/BlockJUnit4ClassRunnerWithParameters.java @@ -1,170 +1,182 @@ -package org.junit.runners.parameterized; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Field; -import java.util.List; - -import org.junit.runner.RunWith; -import org.junit.runner.notification.RunNotifier; -import org.junit.runners.BlockJUnit4ClassRunner; -import org.junit.runners.Parameterized.Parameter; -import org.junit.runners.model.FrameworkField; -import org.junit.runners.model.FrameworkMethod; -import org.junit.runners.model.InitializationError; -import org.junit.runners.model.Statement; - -/** - * A {@link BlockJUnit4ClassRunner} with parameters support. Parameters can be - * injected via constructor or into annotated fields. - */ -public class BlockJUnit4ClassRunnerWithParameters extends - BlockJUnit4ClassRunner { - private enum InjectionType { - CONSTRUCTOR, FIELD - } - - private final Object[] parameters; - - private final String name; - - public BlockJUnit4ClassRunnerWithParameters(TestWithParameters test) - throws InitializationError { - super(test.getTestClass().getJavaClass()); - parameters = test.getParameters().toArray( - new Object[test.getParameters().size()]); - name = test.getName(); - } - - @Override - public Object createTest() throws Exception { - InjectionType injectionType = getInjectionType(); - switch (injectionType) { - case CONSTRUCTOR: - return createTestUsingConstructorInjection(); - case FIELD: - return createTestUsingFieldInjection(); - default: - throw new IllegalStateException("The injection type " - + injectionType + " is not supported."); - } - } - - private Object createTestUsingConstructorInjection() throws Exception { - return getTestClass().getOnlyConstructor().newInstance(parameters); - } - - private Object createTestUsingFieldInjection() throws Exception { - List annotatedFieldsByParameter = getAnnotatedFieldsByParameter(); - if (annotatedFieldsByParameter.size() != parameters.length) { - throw new Exception( - "Wrong number of parameters and @Parameter fields." - + " @Parameter fields counted: " - + annotatedFieldsByParameter.size() - + ", available parameters: " + parameters.length - + "."); - } - Object testClassInstance = getTestClass().getJavaClass().newInstance(); - for (FrameworkField each : annotatedFieldsByParameter) { - Field field = each.getField(); - Parameter annotation = field.getAnnotation(Parameter.class); - int index = annotation.value(); - try { - field.set(testClassInstance, parameters[index]); - } catch (IllegalArgumentException iare) { - throw new Exception(getTestClass().getName() - + ": Trying to set " + field.getName() - + " with the value " + parameters[index] - + " that is not the right type (" - + parameters[index].getClass().getSimpleName() - + " instead of " + field.getType().getSimpleName() - + ").", iare); - } - } - return testClassInstance; - } - - @Override - protected String getName() { - return name; - } - - @Override - protected String testName(FrameworkMethod method) { - return method.getName() + getName(); - } - - @Override - protected void validateConstructor(List errors) { - validateOnlyOneConstructor(errors); - if (getInjectionType() != InjectionType.CONSTRUCTOR) { - validateZeroArgConstructor(errors); - } - } - - @Override - protected void validateFields(List errors) { - super.validateFields(errors); - if (getInjectionType() == InjectionType.FIELD) { - List annotatedFieldsByParameter = getAnnotatedFieldsByParameter(); - int[] usedIndices = new int[annotatedFieldsByParameter.size()]; - for (FrameworkField each : annotatedFieldsByParameter) { - int index = each.getField().getAnnotation(Parameter.class) - .value(); - if (index < 0 || index > annotatedFieldsByParameter.size() - 1) { - errors.add(new Exception("Invalid @Parameter value: " - + index + ". @Parameter fields counted: " - + annotatedFieldsByParameter.size() - + ". Please use an index between 0 and " - + (annotatedFieldsByParameter.size() - 1) + ".")); - } else { - usedIndices[index]++; - } - } - for (int index = 0; index < usedIndices.length; index++) { - int numberOfUse = usedIndices[index]; - if (numberOfUse == 0) { - errors.add(new Exception("@Parameter(" + index - + ") is never used.")); - } else if (numberOfUse > 1) { - errors.add(new Exception("@Parameter(" + index - + ") is used more than once (" + numberOfUse + ").")); - } - } - } - } - - @Override - protected Statement classBlock(RunNotifier notifier) { - return childrenInvoker(notifier); - } - - @Override - protected Annotation[] getRunnerAnnotations() { - Annotation[] allAnnotations = super.getRunnerAnnotations(); - Annotation[] annotationsWithoutRunWith = new Annotation[allAnnotations.length - 1]; - int i = 0; - for (Annotation annotation: allAnnotations) { - if (!annotation.annotationType().equals(RunWith.class)) { - annotationsWithoutRunWith[i] = annotation; - ++i; - } - } - return annotationsWithoutRunWith; - } - - private List getAnnotatedFieldsByParameter() { - return getTestClass().getAnnotatedFields(Parameter.class); - } - - private InjectionType getInjectionType() { - if (fieldsAreAnnotated()) { - return InjectionType.FIELD; - } else { - return InjectionType.CONSTRUCTOR; - } - } - - private boolean fieldsAreAnnotated() { - return !getAnnotatedFieldsByParameter().isEmpty(); - } -} +package org.junit.runners.parameterized; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.model.FrameworkField; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.Statement; +import org.junit.runners.model.TestClass; + +/** + * A {@link BlockJUnit4ClassRunner} with parameters support. Parameters can be + * injected via constructor or into annotated fields. + */ +public class BlockJUnit4ClassRunnerWithParameters + extends BlockJUnit4ClassRunner { + private enum InjectionType { + CONSTRUCTOR, FIELD + } + + private final Object[] parameters; + + private final String name; + + public BlockJUnit4ClassRunnerWithParameters(TestWithParameters test) + throws InitializationError { + super(test.getTestClass().getJavaClass()); + parameters = test.getParameters() + .toArray(new Object[test.getParameters().size()]); + name = test.getName(); + } + + @Override + public Object createTest() throws Exception { + return createInstanceOfParameterizedTest(getTestClass(), parameters); + } + + public static Object createInstanceOfParameterizedTest(TestClass testClass, Object[] parameters) + throws Exception { + InjectionType injectionType = getInjectionType(testClass); + switch (injectionType) { + case CONSTRUCTOR: + return createTestUsingConstructorInjection(testClass, parameters); + case FIELD: + return createTestUsingFieldInjection(testClass, parameters); + default: + throw new IllegalStateException("The injection type " + + injectionType + " is not supported."); + } + } + + private static Object createTestUsingConstructorInjection(TestClass testClass, Object[] parameters) throws Exception { + return testClass.getOnlyConstructor().newInstance(parameters); + } + + private static Object createTestUsingFieldInjection(TestClass testClass, Object[] parameters) throws Exception { + List annotatedFieldsByParameter = getAnnotatedFieldsByParameter( + testClass); + if (annotatedFieldsByParameter.size() != parameters.length) { + throw new Exception( + "Wrong number of parameters and @Parameter fields." + + " @Parameter fields counted: " + + annotatedFieldsByParameter.size() + + ", available parameters: " + parameters.length + + "."); + } + Object testClassInstance = testClass.getJavaClass().newInstance(); + for (FrameworkField each : annotatedFieldsByParameter) { + Field field = each.getField(); + Parameter annotation = field.getAnnotation(Parameter.class); + int index = annotation.value(); + try { + field.set(testClassInstance, parameters[index]); + } catch (IllegalArgumentException iare) { + throw new Exception(testClass.getName() + + ": Trying to set " + field.getName() + + " with the value " + parameters[index] + + " that is not the right type (" + + parameters[index].getClass().getSimpleName() + + " instead of " + field.getType().getSimpleName() + + ").", iare); + } + } + return testClassInstance; + } + + @Override + protected String getName() { + return name; + } + + @Override + protected String testName(FrameworkMethod method) { + return method.getName() + getName(); + } + + @Override + protected void validateConstructor(List errors) { + validateOnlyOneConstructor(errors); + if (getInjectionType(getTestClass()) != InjectionType.CONSTRUCTOR) { + validateZeroArgConstructor(errors); + } + } + + @Override + protected void validateFields(List errors) { + super.validateFields(errors); + if (getInjectionType(getTestClass()) == InjectionType.FIELD) { + List annotatedFieldsByParameter = getAnnotatedFieldsByParameter( + getTestClass()); + int[] usedIndices = new int[annotatedFieldsByParameter.size()]; + for (FrameworkField each : annotatedFieldsByParameter) { + int index = each.getField().getAnnotation(Parameter.class) + .value(); + if (index < 0 + || index > annotatedFieldsByParameter.size() - 1) { + errors.add(new Exception("Invalid @Parameter value: " + + index + ". @Parameter fields counted: " + + annotatedFieldsByParameter.size() + + ". Please use an index between 0 and " + + (annotatedFieldsByParameter.size() - 1) + ".")); + } else { + usedIndices[index]++; + } + } + for (int index = 0; index < usedIndices.length; index++) { + int numberOfUse = usedIndices[index]; + if (numberOfUse == 0) { + errors.add(new Exception( + "@Parameter(" + index + ") is never used.")); + } else if (numberOfUse > 1) { + errors.add(new Exception( + "@Parameter(" + index + ") is used more than once (" + + numberOfUse + ").")); + } + } + } + } + + @Override + protected Statement classBlock(RunNotifier notifier) { + return childrenInvoker(notifier); + } + + @Override + protected Annotation[] getRunnerAnnotations() { + Annotation[] allAnnotations = super.getRunnerAnnotations(); + Annotation[] annotationsWithoutRunWith = new Annotation[allAnnotations.length + - 1]; + int i = 0; + for (Annotation annotation : allAnnotations) { + if (!annotation.annotationType().equals(RunWith.class)) { + annotationsWithoutRunWith[i] = annotation; + ++i; + } + } + return annotationsWithoutRunWith; + } + + public static List getAnnotatedFieldsByParameter( + TestClass testClass) { + return testClass.getAnnotatedFields(Parameter.class); + } + + private static InjectionType getInjectionType(TestClass testClass) { + if (fieldsAreAnnotated(testClass)) { + return InjectionType.FIELD; + } else { + return InjectionType.CONSTRUCTOR; + } + } + + private static boolean fieldsAreAnnotated(TestClass testClass) { + return !getAnnotatedFieldsByParameter(testClass).isEmpty(); + } +} From 0e9d515b1011009bae2c54e10268895e40c99cf2 Mon Sep 17 00:00:00 2001 From: pwippermann Date: Tue, 19 Jul 2016 00:09:23 +0200 Subject: [PATCH 07/13] Methods for TestRules are now publicly available --- .../junit/runners/BlockJUnit4ClassRunner.java | 120 +++++++++++------- 1 file changed, 74 insertions(+), 46 deletions(-) diff --git a/src/main/java/org/junit/runners/BlockJUnit4ClassRunner.java b/src/main/java/org/junit/runners/BlockJUnit4ClassRunner.java index 822f8dc47bfa..0068fbe81051 100644 --- a/src/main/java/org/junit/runners/BlockJUnit4ClassRunner.java +++ b/src/main/java/org/junit/runners/BlockJUnit4ClassRunner.java @@ -30,6 +30,7 @@ import org.junit.runners.model.InitializationError; import org.junit.runners.model.MultipleFailureException; import org.junit.runners.model.Statement; +import org.junit.runners.model.TestClass; /** * Implements the JUnit 4 standard test case class model, as defined by the @@ -49,7 +50,7 @@ * JUnit4ClassRunner} was in an internal package, and is now deprecated. * *

- * In turn, in 2009 we introduced {@link Rule}s. In many cases where extending + * In turn, in 2009 we introduced {@link Rule}s. In many cases where extending * BlockJUnit4ClassRunner was necessary to add new behavior, {@link Rule}s can * be used, which makes the extension more reusable and composable. * @@ -62,9 +63,11 @@ public class BlockJUnit4ClassRunner extends ParentRunner { /** * Creates a BlockJUnit4ClassRunner to run {@code testClass} * - * @throws InitializationError if the test class is malformed. + * @throws InitializationError + * if the test class is malformed. */ - public BlockJUnit4ClassRunner(Class testClass) throws InitializationError { + public BlockJUnit4ClassRunner(Class testClass) + throws InitializationError { super(testClass); } @@ -73,7 +76,8 @@ public BlockJUnit4ClassRunner(Class testClass) throws InitializationError { // @Override - protected void runChild(final FrameworkMethod method, RunNotifier notifier) { + protected void runChild(final FrameworkMethod method, + RunNotifier notifier) { Description description = describeChild(method); if (isIgnored(method)) { notifier.fireTestIgnored(description); @@ -81,8 +85,7 @@ protected void runChild(final FrameworkMethod method, RunNotifier notifier) { Statement statement; try { statement = methodBlock(method); - } - catch (Throwable ex) { + } catch (Throwable ex) { statement = new Fail(ex); } runLeaf(statement, description, notifier); @@ -103,8 +106,9 @@ protected Description describeChild(FrameworkMethod method) { Description description = methodDescriptions.get(method); if (description == null) { - description = Description.createTestDescription(getTestClass().getJavaClass(), - testName(method), method.getAnnotations()); + description = Description.createTestDescription( + getTestClass().getJavaClass(), testName(method), + method.getAnnotations()); methodDescriptions.putIfAbsent(method, description); } @@ -174,9 +178,9 @@ protected void validateOnlyOneConstructor(List errors) { * parameters (do not override) */ protected void validateZeroArgConstructor(List errors) { - if (!getTestClass().isANonStaticInnerClass() - && hasOneConstructor() - && (getTestClass().getOnlyConstructor().getParameterTypes().length != 0)) { + if (!getTestClass().isANonStaticInnerClass() && hasOneConstructor() + && (getTestClass().getOnlyConstructor() + .getParameterTypes().length != 0)) { String gripe = "Test class should have exactly one public zero-argument constructor"; errors.add(new Exception(gripe)); } @@ -190,6 +194,7 @@ private boolean hasOneConstructor() { * Adds to {@code errors} for each method annotated with {@code @Test}, * {@code @Before}, or {@code @After} that is not a public, void instance * method with no arguments. + * * @deprecated */ @Deprecated @@ -230,7 +235,8 @@ protected Object createTest() throws Exception { /** * Returns a new fixture to run a particular test {@code method} against. - * Default implementation executes the no-argument {@link #createTest()} method. + * Default implementation executes the no-argument {@link #createTest()} + * method. * * @since 4.13 */ @@ -253,8 +259,9 @@ protected String testName(FrameworkMethod method) { * Here is an outline of the default implementation: * *

    - *
  • Invoke {@code method} on the result of {@link #createTest(org.junit.runners.model.FrameworkMethod)}, and - * throw any exceptions thrown by either operation. + *
  • Invoke {@code method} on the result of + * {@link #createTest(org.junit.runners.model.FrameworkMethod)}, and throw + * any exceptions thrown by either operation. *
  • HOWEVER, if {@code method}'s {@code @Test} annotation has the {@code * expecting} attribute, return normally only if the previous step threw an * exception of the correct type, and throw an exception otherwise. @@ -320,14 +327,16 @@ protected Statement methodInvoker(FrameworkMethod method, Object test) { protected Statement possiblyExpectingExceptions(FrameworkMethod method, Object test, Statement next) { Test annotation = method.getAnnotation(Test.class); - return expectsException(annotation) ? new ExpectException(next, - getExpectedException(annotation)) : next; + return expectsException(annotation) + ? new ExpectException(next, getExpectedException(annotation)) + : next; } /** * Returns a {@link Statement}: if {@code method}'s {@code @Test} annotation * has the {@code timeout} attribute, throw an exception if {@code next} * takes more than the specified number of milliseconds. + * * @deprecated */ @Deprecated @@ -338,8 +347,7 @@ protected Statement withPotentialTimeout(FrameworkMethod method, return next; } return FailOnTimeout.builder() - .withTimeout(timeout, TimeUnit.MILLISECONDS) - .build(next); + .withTimeout(timeout, TimeUnit.MILLISECONDS).build(next); } /** @@ -349,10 +357,10 @@ protected Statement withPotentialTimeout(FrameworkMethod method, */ protected Statement withBefores(FrameworkMethod method, Object target, Statement statement) { - List befores = getTestClass().getAnnotatedMethods( - Before.class); - return befores.isEmpty() ? statement : new RunBefores(statement, - befores, target); + List befores = getTestClass() + .getAnnotatedMethods(Before.class); + return befores.isEmpty() ? statement + : new RunBefores(statement, befores, target); } /** @@ -364,10 +372,10 @@ protected Statement withBefores(FrameworkMethod method, Object target, */ protected Statement withAfters(FrameworkMethod method, Object target, Statement statement) { - List afters = getTestClass().getAnnotatedMethods( - After.class); - return afters.isEmpty() ? statement : new RunAfters(statement, afters, - target); + List afters = getTestClass() + .getAnnotatedMethods(After.class); + return afters.isEmpty() ? statement + : new RunAfters(statement, afters, target); } private Statement withRules(FrameworkMethod method, Object target, @@ -375,13 +383,13 @@ private Statement withRules(FrameworkMethod method, Object target, List testRules = getTestRules(target); Statement result = statement; result = withMethodRules(method, testRules, target, result); - result = withTestRules(method, testRules, result); + result = withTestRules(testRules, describeChild(method), result); return result; } - private Statement withMethodRules(FrameworkMethod method, List testRules, - Object target, Statement result) { + private Statement withMethodRules(FrameworkMethod method, + List testRules, Object target, Statement result) { Statement withMethodRules = result; for (org.junit.rules.MethodRule each : getMethodRules(target)) { if (!(each instanceof TestRule && testRules.contains(each))) { @@ -396,45 +404,65 @@ private List getMethodRules(Object target) { } /** - * @param target the test case instance + * @param target + * the test case instance * @return a list of MethodRules that should be applied when executing this * test */ protected List rules(Object target) { - List rules = getTestClass().getAnnotatedMethodValues(target, + List rules = getTestClass().getAnnotatedMethodValues(target, Rule.class, MethodRule.class); - rules.addAll(getTestClass().getAnnotatedFieldValues(target, - Rule.class, MethodRule.class)); + rules.addAll(getTestClass().getAnnotatedFieldValues(target, Rule.class, + MethodRule.class)); return rules; } /** - * Returns a {@link Statement}: apply all non-static fields - * annotated with {@link Rule}. + * Returns a {@link Statement}: apply all non-static fields annotated with + * {@link Rule}. + * + * @param description + * The description passed to the {@link Rule} + * @param statement + * The base statement * - * @param statement The base statement - * @return a RunRules statement if any class-level {@link Rule}s are - * found, or the base statement + * @return a RunRules statement if any class-level {@link Rule}s are found, + * or the base statement */ - private Statement withTestRules(FrameworkMethod method, List testRules, - Statement statement) { - return testRules.isEmpty() ? statement : - new RunRules(statement, testRules, describeChild(method)); + public static Statement withTestRules(List testRules, + Description description, Statement statement) { + return testRules.isEmpty() ? statement + : new RunRules(statement, testRules, description); } /** - * @param target the test case instance + * @param target + * the test case instance * @return a list of TestRules that should be applied when executing this * test */ protected List getTestRules(Object target) { - List result = getTestClass().getAnnotatedMethodValues(target, + return getTestRules(target, getTestClass()); + } + + /** + * @param target + * the test case instance + * @param the + * {@link TestClass} where the {@link TestRule} annotations have + * been defined. + * @return a list of TestRules that should be applied when executing this + * test + */ + public static List getTestRules(Object target, + TestClass testClass) { + List result = testClass.getAnnotatedMethodValues(target, Rule.class, TestRule.class); - result.addAll(getTestClass().getAnnotatedFieldValues(target, - Rule.class, TestRule.class)); + result.addAll(testClass.getAnnotatedFieldValues(target, Rule.class, + TestRule.class)); return result; } From 6964310ad67e8701f55105929689aa6ae2cb3aaa Mon Sep 17 00:00:00 2001 From: pwippermann Date: Tue, 19 Jul 2016 21:22:05 +0200 Subject: [PATCH 08/13] Minor change to fix "@param name not found" error on Travis CI --- src/main/java/org/junit/runners/BlockJUnit4ClassRunner.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/junit/runners/BlockJUnit4ClassRunner.java b/src/main/java/org/junit/runners/BlockJUnit4ClassRunner.java index 0068fbe81051..08abd72b4cee 100644 --- a/src/main/java/org/junit/runners/BlockJUnit4ClassRunner.java +++ b/src/main/java/org/junit/runners/BlockJUnit4ClassRunner.java @@ -450,8 +450,8 @@ protected List getTestRules(Object target) { /** * @param target * the test case instance - * @param the - * {@link TestClass} where the {@link TestRule} annotations have + * @param testClass + * the {@link TestClass} where the {@link TestRule} annotations have * been defined. * @return a list of TestRules that should be applied when executing this * test From 0f4704a88049ec785dc5efa66b5545d86d3f0e03 Mon Sep 17 00:00:00 2001 From: pwippermann Date: Thu, 21 Jul 2016 17:38:28 +0200 Subject: [PATCH 09/13] methods related to annotation processing are now protected inst. methods --- .../java/org/junit/runners/ParentRunner.java | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/junit/runners/ParentRunner.java b/src/main/java/org/junit/runners/ParentRunner.java index b1d75c73a3bd..8a27f076ae01 100644 --- a/src/main/java/org/junit/runners/ParentRunner.java +++ b/src/main/java/org/junit/runners/ParentRunner.java @@ -189,9 +189,9 @@ private void validateClassRules(List errors) { protected Statement classBlock(final RunNotifier notifier) { Statement statement = childrenInvoker(notifier); if (!areAllChildrenIgnored()) { - statement = withBeforeClasses(statement, testClass); - statement = withAfterClasses(statement, testClass); - statement = withClassRules(statement, testClass, getDescription()); + statement = withBeforeClasses(statement); + statement = withAfterClasses(statement); + statement = withClassRules(statement, getDescription()); } return statement; } @@ -209,9 +209,8 @@ private boolean areAllChildrenIgnored() { * Returns a {@link Statement}: run all non-overridden {@code @BeforeClass} methods on this class * and superclasses before executing {@code statement}; if any throws an * Exception, stop execution and pass the exception on. - * @param testClass */ - public static Statement withBeforeClasses(Statement statement, TestClass testClass) { + protected Statement withBeforeClasses(Statement statement) { List befores = testClass .getAnnotatedMethods(BeforeClass.class); return befores.isEmpty() ? statement : @@ -224,9 +223,8 @@ public static Statement withBeforeClasses(Statement statement, TestClass testCla * always executed: exceptions thrown by previous steps are combined, if * necessary, with exceptions from AfterClass methods into a * {@link org.junit.runners.model.MultipleFailureException}. - * @param testClass */ - public static Statement withAfterClasses(Statement statement, TestClass testClass) { + protected Statement withAfterClasses(Statement statement) { List afters = testClass .getAnnotatedMethods(AfterClass.class); return afters.isEmpty() ? statement : @@ -239,23 +237,21 @@ public static Statement withAfterClasses(Statement statement, TestClass testClas * annotated with {@link ClassRule}. * * @param statement the base statement - * @param testClass * @param description the description to pass to the {@link Rule}s * @return a RunRules statement if any class-level {@link Rule}s are * found, or the base statement */ - public static Statement withClassRules(Statement statement, TestClass testClass, Description description) { - List classRules = classRules(testClass); + protected Statement withClassRules(Statement statement, Description description) { + List classRules = classRules(); return classRules.isEmpty() ? statement : new RunRules(statement, classRules, description); } /** - * @param testClass * @return the {@code ClassRule}s that can transform the block that runs * each method in the tested class. */ - protected static List classRules(TestClass testClass) { + protected List classRules() { List result = testClass.getAnnotatedMethodValues(null, ClassRule.class, TestRule.class); result.addAll(testClass.getAnnotatedFieldValues(null, ClassRule.class, TestRule.class)); return result; From ff3d035a74addb564a667c8d7ae82b106ab5efe5 Mon Sep 17 00:00:00 2001 From: pwippermann Date: Fri, 22 Jul 2016 09:40:42 +0200 Subject: [PATCH 10/13] Introduced ParameterizedTestClass to encapsulate annotation processing related to parameters there. --- .../java/org/junit/runners/Parameterized.java | 66 ++-------------- .../runners/model/ParameterizedTestClass.java | 76 +++++++++++++++++++ .../org/junit/runners/model/TestClass.java | 0 3 files changed, 83 insertions(+), 59 deletions(-) create mode 100644 src/main/java/org/junit/runners/model/ParameterizedTestClass.java mode change 100755 => 100644 src/main/java/org/junit/runners/model/TestClass.java diff --git a/src/main/java/org/junit/runners/Parameterized.java b/src/main/java/org/junit/runners/Parameterized.java index 8d08e47559d5..f0f99b836214 100644 --- a/src/main/java/org/junit/runners/Parameterized.java +++ b/src/main/java/org/junit/runners/Parameterized.java @@ -12,7 +12,7 @@ import java.util.List; import org.junit.runner.Runner; -import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.ParameterizedTestClass; import org.junit.runners.model.TestClass; import org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParametersFactory; import org.junit.runners.parameterized.ParametersRunnerFactory; @@ -244,7 +244,7 @@ public Parameterized(Class klass) throws Throwable { private static class RunnersFactory { private static final ParametersRunnerFactory DEFAULT_FACTORY = new BlockJUnit4ClassRunnerWithParametersFactory(); - private final TestClass testClass; + private final ParameterizedTestClass testClass; static List createRunnersForClass(Class klass) throws Throwable { @@ -252,12 +252,12 @@ static List createRunnersForClass(Class klass) } private RunnersFactory(Class klass) { - testClass = new TestClass(klass); + testClass = new ParameterizedTestClass(klass); } private List createRunners() throws Throwable { return Collections.unmodifiableList(createRunnersForParameters( - Parameterized.allParameters(testClass), Parameterized.getNamePatternForParameters(testClass), + testClass.allParameters(), testClass.getNamePatternForParameters(), getParametersRunnerFactory())); } @@ -276,7 +276,7 @@ private ParametersRunnerFactory getParametersRunnerFactory() private TestWithParameters createTestWithNotNormalizedParameters( String pattern, int index, Object parametersOrSingleParameter) { - Object[] parameters = Parameterized.normalizeParameter(parametersOrSingleParameter); + Object[] parameters = ParameterizedTestClass.normalizeParameter(parametersOrSingleParameter); return createTestWithParameters(testClass, pattern, index, parameters); } @@ -294,7 +294,7 @@ private List createRunnersForParameters( } return runners; } catch (ClassCastException e) { - throw Parameterized.parametersMethodReturnedWrongType(this.testClass); + throw testClass.parametersMethodReturnedWrongType(); } } @@ -313,61 +313,9 @@ private List createTestsForParameters( private TestWithParameters createTestWithParameters( TestClass testClass, String pattern, int index, Object[] parameters) { - String testName = Parameterized.produceTestName(pattern, index, parameters); + String testName = ParameterizedTestClass.produceTestName(pattern, index, parameters); return new TestWithParameters(testName, testClass, Arrays.asList(parameters)); } } - - @SuppressWarnings("unchecked") - public static Iterable allParameters(TestClass testClass) throws Throwable { - Object parameters = getParametersMethod(testClass).invokeExplosively(null); - if (parameters instanceof Iterable) { - return (Iterable) parameters; - } else if (parameters instanceof Object[]) { - return Arrays.asList((Object[]) parameters); - } else { - throw parametersMethodReturnedWrongType(testClass); - } - } - - private static FrameworkMethod getParametersMethod(TestClass testClass) throws Exception { - List methods = testClass - .getAnnotatedMethods(Parameters.class); - for (FrameworkMethod each : methods) { - if (each.isStatic() && each.isPublic()) { - return each; - } - } - - throw new Exception("No public static parameters method on class " - + testClass.getName()); - } - - private static Exception parametersMethodReturnedWrongType(TestClass testClass) throws Exception { - String className = testClass.getName(); - String methodName = getParametersMethod(testClass).getName(); - String message = MessageFormat.format( - "{0}.{1}() must return an Iterable of arrays.", className, - methodName); - return new Exception(message); - } - - public static Object[] normalizeParameter(Object parametersOrSingleParameter) { - return (parametersOrSingleParameter instanceof Object[]) ? (Object[]) parametersOrSingleParameter - : new Object[] { parametersOrSingleParameter }; - } - - public static String produceTestName(String pattern, int index, - Object[] parameters) { - String finalPattern = pattern.replaceAll("\\{index\\}", - Integer.toString(index)); - return "[" + MessageFormat.format(finalPattern, parameters) + "]"; - } - - public static String getNamePatternForParameters(TestClass testClass) throws Exception { - Parameters parametersAnnotation = getParametersMethod(testClass).getAnnotation( - Parameters.class); - return parametersAnnotation.name(); - } } diff --git a/src/main/java/org/junit/runners/model/ParameterizedTestClass.java b/src/main/java/org/junit/runners/model/ParameterizedTestClass.java new file mode 100644 index 000000000000..34aa482d8393 --- /dev/null +++ b/src/main/java/org/junit/runners/model/ParameterizedTestClass.java @@ -0,0 +1,76 @@ +/** + * + */ +package org.junit.runners.model; + +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.List; + +import org.junit.runners.Parameterized; + +/** + * @author Peter Wippermann + * + */ +public class ParameterizedTestClass extends TestClass { + + public ParameterizedTestClass(Class clazz) { + super(clazz); + } + + @SuppressWarnings("unchecked") + public Iterable allParameters() throws Throwable { + Object parameters = getParametersMethod().invokeExplosively(null); + if (parameters instanceof Iterable) { + return (Iterable) parameters; + } else if (parameters instanceof Object[]) { + return Arrays.asList((Object[]) parameters); + } else { + throw parametersMethodReturnedWrongType(); + } + } + + private FrameworkMethod getParametersMethod() throws Exception { + List methods = getAnnotatedMethods( + Parameterized.Parameters.class); + for (FrameworkMethod each : methods) { + if (each.isStatic() && each.isPublic()) { + return each; + } + } + + throw new Exception( + "No public static parameters method on class " + getName()); + } + + public Exception parametersMethodReturnedWrongType() throws Exception { + String className = getName(); + String methodName = getParametersMethod().getName(); + String message = MessageFormat.format( + "{0}.{1}() must return an Iterable of arrays.", className, + methodName); + return new Exception(message); + } + + public static Object[] normalizeParameter( + Object parametersOrSingleParameter) { + return (parametersOrSingleParameter instanceof Object[]) + ? (Object[]) parametersOrSingleParameter + : new Object[] { parametersOrSingleParameter }; + } + + public static String produceTestName(String pattern, int index, + Object[] parameters) { + String finalPattern = pattern.replaceAll("\\{index\\}", + Integer.toString(index)); + return "[" + MessageFormat.format(finalPattern, parameters) + "]"; + } + + public String getNamePatternForParameters() throws Exception { + Parameterized.Parameters parametersAnnotation = getParametersMethod() + .getAnnotation(Parameterized.Parameters.class); + return parametersAnnotation.name(); + } + +} diff --git a/src/main/java/org/junit/runners/model/TestClass.java b/src/main/java/org/junit/runners/model/TestClass.java old mode 100755 new mode 100644 From 43ab75edde82ea36a5a0ac70b5581740485c057b Mon Sep 17 00:00:00 2001 From: pwippermann Date: Fri, 22 Jul 2016 10:07:55 +0200 Subject: [PATCH 11/13] withClassStatements() combines @BeforeClass, @AfterClass and @ClassRule --- src/main/java/org/junit/runners/ParentRunner.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/junit/runners/ParentRunner.java b/src/main/java/org/junit/runners/ParentRunner.java index 8a27f076ae01..1529077989ea 100644 --- a/src/main/java/org/junit/runners/ParentRunner.java +++ b/src/main/java/org/junit/runners/ParentRunner.java @@ -189,13 +189,18 @@ private void validateClassRules(List errors) { protected Statement classBlock(final RunNotifier notifier) { Statement statement = childrenInvoker(notifier); if (!areAllChildrenIgnored()) { - statement = withBeforeClasses(statement); - statement = withAfterClasses(statement); - statement = withClassRules(statement, getDescription()); + statement = withClassStatements(statement); } return statement; } + protected Statement withClassStatements(Statement statement) { + statement = withBeforeClasses(statement); + statement = withAfterClasses(statement); + statement = withClassRules(statement, getDescription()); + return statement; + } + private boolean areAllChildrenIgnored() { for (T child : getFilteredChildren()) { if (!isIgnored(child)) { From dca431f61448f9300894dd4eb5772d728b25bd69 Mon Sep 17 00:00:00 2001 From: Peter Wippermann Date: Mon, 25 Jul 2016 18:00:41 +0200 Subject: [PATCH 12/13] converted line delimiters to unix --- src/main/java/org/junit/runners/Suite.java | 260 ++++++++++----------- 1 file changed, 130 insertions(+), 130 deletions(-) diff --git a/src/main/java/org/junit/runners/Suite.java b/src/main/java/org/junit/runners/Suite.java index 4cb491d4f9ae..a540e1ed524a 100644 --- a/src/main/java/org/junit/runners/Suite.java +++ b/src/main/java/org/junit/runners/Suite.java @@ -1,130 +1,130 @@ -package org.junit.runners; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.Collections; -import java.util.List; - -import org.junit.internal.builders.AllDefaultPossibilitiesBuilder; -import org.junit.runner.Description; -import org.junit.runner.Runner; -import org.junit.runner.notification.RunNotifier; -import org.junit.runners.model.InitializationError; -import org.junit.runners.model.RunnerBuilder; - -/** - * Using Suite as a runner allows you to manually - * build a suite containing tests from many classes. It is the JUnit 4 equivalent of the JUnit 3.8.x - * static {@link junit.framework.Test} suite() method. To use it, annotate a class - * with @RunWith(Suite.class) and @SuiteClasses({TestClass1.class, ...}). - * When you run this class, it will run all the tests in all the suite classes. - * - * @since 4.0 - */ -public class Suite extends ParentRunner { - /** - * Returns an empty suite. - */ - public static Runner emptySuite() { - try { - return new Suite((Class) null, new Class[0]); - } catch (InitializationError e) { - throw new RuntimeException("This shouldn't be possible"); - } - } - - /** - * The SuiteClasses annotation specifies the classes to be run when a class - * annotated with @RunWith(Suite.class) is run. - */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) - @Inherited - public @interface SuiteClasses { - /** - * @return the classes to be run - */ - Class[] value(); - } - - public static Class[] getSuiteClasses(Class klass) throws InitializationError { - SuiteClasses annotation = klass.getAnnotation(SuiteClasses.class); - if (annotation == null) { - throw new InitializationError(String.format("class '%s' must have a SuiteClasses annotation", klass.getName())); - } - return annotation.value(); - } - - private final List runners; - - /** - * Called reflectively on classes annotated with @RunWith(Suite.class) - * - * @param klass the root class - * @param builder builds runners for classes in the suite - */ - public Suite(Class klass, RunnerBuilder builder) throws InitializationError { - this(builder, klass, getSuiteClasses(klass)); - } - - /** - * Call this when there is no single root class (for example, multiple class names - * passed on the command line to {@link org.junit.runner.JUnitCore} - * - * @param builder builds runners for classes in the suite - * @param classes the classes in the suite - */ - public Suite(RunnerBuilder builder, Class[] classes) throws InitializationError { - this(null, builder.runners(null, classes)); - } - - /** - * Call this when the default builder is good enough. Left in for compatibility with JUnit 4.4. - * - * @param klass the root of the suite - * @param suiteClasses the classes in the suite - */ - protected Suite(Class klass, Class[] suiteClasses) throws InitializationError { - this(new AllDefaultPossibilitiesBuilder(true), klass, suiteClasses); - } - - /** - * Called by this class and subclasses once the classes making up the suite have been determined - * - * @param builder builds runners for classes in the suite - * @param klass the root of the suite - * @param suiteClasses the classes in the suite - */ - protected Suite(RunnerBuilder builder, Class klass, Class[] suiteClasses) throws InitializationError { - this(klass, builder.runners(klass, suiteClasses)); - } - - /** - * Called by this class and subclasses once the runners making up the suite have been determined - * - * @param klass root of the suite - * @param runners for each class in the suite, a {@link Runner} - */ - protected Suite(Class klass, List runners) throws InitializationError { - super(klass); - this.runners = Collections.unmodifiableList(runners); - } - - @Override - protected List getChildren() { - return runners; - } - - @Override - protected Description describeChild(Runner child) { - return child.getDescription(); - } - - @Override - protected void runChild(Runner runner, final RunNotifier notifier) { - runner.run(notifier); - } -} +package org.junit.runners; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.Collections; +import java.util.List; + +import org.junit.internal.builders.AllDefaultPossibilitiesBuilder; +import org.junit.runner.Description; +import org.junit.runner.Runner; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.RunnerBuilder; + +/** + * Using Suite as a runner allows you to manually + * build a suite containing tests from many classes. It is the JUnit 4 equivalent of the JUnit 3.8.x + * static {@link junit.framework.Test} suite() method. To use it, annotate a class + * with @RunWith(Suite.class) and @SuiteClasses({TestClass1.class, ...}). + * When you run this class, it will run all the tests in all the suite classes. + * + * @since 4.0 + */ +public class Suite extends ParentRunner { + /** + * Returns an empty suite. + */ + public static Runner emptySuite() { + try { + return new Suite((Class) null, new Class[0]); + } catch (InitializationError e) { + throw new RuntimeException("This shouldn't be possible"); + } + } + + /** + * The SuiteClasses annotation specifies the classes to be run when a class + * annotated with @RunWith(Suite.class) is run. + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + @Inherited + public @interface SuiteClasses { + /** + * @return the classes to be run + */ + Class[] value(); + } + + public static Class[] getSuiteClasses(Class klass) throws InitializationError { + SuiteClasses annotation = klass.getAnnotation(SuiteClasses.class); + if (annotation == null) { + throw new InitializationError(String.format("class '%s' must have a SuiteClasses annotation", klass.getName())); + } + return annotation.value(); + } + + private final List runners; + + /** + * Called reflectively on classes annotated with @RunWith(Suite.class) + * + * @param klass the root class + * @param builder builds runners for classes in the suite + */ + public Suite(Class klass, RunnerBuilder builder) throws InitializationError { + this(builder, klass, getSuiteClasses(klass)); + } + + /** + * Call this when there is no single root class (for example, multiple class names + * passed on the command line to {@link org.junit.runner.JUnitCore} + * + * @param builder builds runners for classes in the suite + * @param classes the classes in the suite + */ + public Suite(RunnerBuilder builder, Class[] classes) throws InitializationError { + this(null, builder.runners(null, classes)); + } + + /** + * Call this when the default builder is good enough. Left in for compatibility with JUnit 4.4. + * + * @param klass the root of the suite + * @param suiteClasses the classes in the suite + */ + protected Suite(Class klass, Class[] suiteClasses) throws InitializationError { + this(new AllDefaultPossibilitiesBuilder(true), klass, suiteClasses); + } + + /** + * Called by this class and subclasses once the classes making up the suite have been determined + * + * @param builder builds runners for classes in the suite + * @param klass the root of the suite + * @param suiteClasses the classes in the suite + */ + protected Suite(RunnerBuilder builder, Class klass, Class[] suiteClasses) throws InitializationError { + this(klass, builder.runners(klass, suiteClasses)); + } + + /** + * Called by this class and subclasses once the runners making up the suite have been determined + * + * @param klass root of the suite + * @param runners for each class in the suite, a {@link Runner} + */ + protected Suite(Class klass, List runners) throws InitializationError { + super(klass); + this.runners = Collections.unmodifiableList(runners); + } + + @Override + protected List getChildren() { + return runners; + } + + @Override + protected Description describeChild(Runner child) { + return child.getDescription(); + } + + @Override + protected void runChild(Runner runner, final RunNotifier notifier) { + runner.run(notifier); + } +} From 0c1d4d5eaa947b20de9ad2a4717afd2a6b7ee087 Mon Sep 17 00:00:00 2001 From: Peter Wippermann Date: Mon, 25 Jul 2016 18:03:23 +0200 Subject: [PATCH 13/13] converted line delimiters to unix --- .../BlockJUnit4ClassRunnerWithParameters.java | 364 +++++++++--------- 1 file changed, 182 insertions(+), 182 deletions(-) diff --git a/src/main/java/org/junit/runners/parameterized/BlockJUnit4ClassRunnerWithParameters.java b/src/main/java/org/junit/runners/parameterized/BlockJUnit4ClassRunnerWithParameters.java index a588677139b5..4e46844c6313 100644 --- a/src/main/java/org/junit/runners/parameterized/BlockJUnit4ClassRunnerWithParameters.java +++ b/src/main/java/org/junit/runners/parameterized/BlockJUnit4ClassRunnerWithParameters.java @@ -1,182 +1,182 @@ -package org.junit.runners.parameterized; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Field; -import java.util.List; - -import org.junit.runner.RunWith; -import org.junit.runner.notification.RunNotifier; -import org.junit.runners.BlockJUnit4ClassRunner; -import org.junit.runners.Parameterized.Parameter; -import org.junit.runners.model.FrameworkField; -import org.junit.runners.model.FrameworkMethod; -import org.junit.runners.model.InitializationError; -import org.junit.runners.model.Statement; -import org.junit.runners.model.TestClass; - -/** - * A {@link BlockJUnit4ClassRunner} with parameters support. Parameters can be - * injected via constructor or into annotated fields. - */ -public class BlockJUnit4ClassRunnerWithParameters - extends BlockJUnit4ClassRunner { - private enum InjectionType { - CONSTRUCTOR, FIELD - } - - private final Object[] parameters; - - private final String name; - - public BlockJUnit4ClassRunnerWithParameters(TestWithParameters test) - throws InitializationError { - super(test.getTestClass().getJavaClass()); - parameters = test.getParameters() - .toArray(new Object[test.getParameters().size()]); - name = test.getName(); - } - - @Override - public Object createTest() throws Exception { - return createInstanceOfParameterizedTest(getTestClass(), parameters); - } - - public static Object createInstanceOfParameterizedTest(TestClass testClass, Object[] parameters) - throws Exception { - InjectionType injectionType = getInjectionType(testClass); - switch (injectionType) { - case CONSTRUCTOR: - return createTestUsingConstructorInjection(testClass, parameters); - case FIELD: - return createTestUsingFieldInjection(testClass, parameters); - default: - throw new IllegalStateException("The injection type " - + injectionType + " is not supported."); - } - } - - private static Object createTestUsingConstructorInjection(TestClass testClass, Object[] parameters) throws Exception { - return testClass.getOnlyConstructor().newInstance(parameters); - } - - private static Object createTestUsingFieldInjection(TestClass testClass, Object[] parameters) throws Exception { - List annotatedFieldsByParameter = getAnnotatedFieldsByParameter( - testClass); - if (annotatedFieldsByParameter.size() != parameters.length) { - throw new Exception( - "Wrong number of parameters and @Parameter fields." - + " @Parameter fields counted: " - + annotatedFieldsByParameter.size() - + ", available parameters: " + parameters.length - + "."); - } - Object testClassInstance = testClass.getJavaClass().newInstance(); - for (FrameworkField each : annotatedFieldsByParameter) { - Field field = each.getField(); - Parameter annotation = field.getAnnotation(Parameter.class); - int index = annotation.value(); - try { - field.set(testClassInstance, parameters[index]); - } catch (IllegalArgumentException iare) { - throw new Exception(testClass.getName() - + ": Trying to set " + field.getName() - + " with the value " + parameters[index] - + " that is not the right type (" - + parameters[index].getClass().getSimpleName() - + " instead of " + field.getType().getSimpleName() - + ").", iare); - } - } - return testClassInstance; - } - - @Override - protected String getName() { - return name; - } - - @Override - protected String testName(FrameworkMethod method) { - return method.getName() + getName(); - } - - @Override - protected void validateConstructor(List errors) { - validateOnlyOneConstructor(errors); - if (getInjectionType(getTestClass()) != InjectionType.CONSTRUCTOR) { - validateZeroArgConstructor(errors); - } - } - - @Override - protected void validateFields(List errors) { - super.validateFields(errors); - if (getInjectionType(getTestClass()) == InjectionType.FIELD) { - List annotatedFieldsByParameter = getAnnotatedFieldsByParameter( - getTestClass()); - int[] usedIndices = new int[annotatedFieldsByParameter.size()]; - for (FrameworkField each : annotatedFieldsByParameter) { - int index = each.getField().getAnnotation(Parameter.class) - .value(); - if (index < 0 - || index > annotatedFieldsByParameter.size() - 1) { - errors.add(new Exception("Invalid @Parameter value: " - + index + ". @Parameter fields counted: " - + annotatedFieldsByParameter.size() - + ". Please use an index between 0 and " - + (annotatedFieldsByParameter.size() - 1) + ".")); - } else { - usedIndices[index]++; - } - } - for (int index = 0; index < usedIndices.length; index++) { - int numberOfUse = usedIndices[index]; - if (numberOfUse == 0) { - errors.add(new Exception( - "@Parameter(" + index + ") is never used.")); - } else if (numberOfUse > 1) { - errors.add(new Exception( - "@Parameter(" + index + ") is used more than once (" - + numberOfUse + ").")); - } - } - } - } - - @Override - protected Statement classBlock(RunNotifier notifier) { - return childrenInvoker(notifier); - } - - @Override - protected Annotation[] getRunnerAnnotations() { - Annotation[] allAnnotations = super.getRunnerAnnotations(); - Annotation[] annotationsWithoutRunWith = new Annotation[allAnnotations.length - - 1]; - int i = 0; - for (Annotation annotation : allAnnotations) { - if (!annotation.annotationType().equals(RunWith.class)) { - annotationsWithoutRunWith[i] = annotation; - ++i; - } - } - return annotationsWithoutRunWith; - } - - public static List getAnnotatedFieldsByParameter( - TestClass testClass) { - return testClass.getAnnotatedFields(Parameter.class); - } - - private static InjectionType getInjectionType(TestClass testClass) { - if (fieldsAreAnnotated(testClass)) { - return InjectionType.FIELD; - } else { - return InjectionType.CONSTRUCTOR; - } - } - - private static boolean fieldsAreAnnotated(TestClass testClass) { - return !getAnnotatedFieldsByParameter(testClass).isEmpty(); - } -} +package org.junit.runners.parameterized; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.model.FrameworkField; +import org.junit.runners.model.FrameworkMethod; +import org.junit.runners.model.InitializationError; +import org.junit.runners.model.Statement; +import org.junit.runners.model.TestClass; + +/** + * A {@link BlockJUnit4ClassRunner} with parameters support. Parameters can be + * injected via constructor or into annotated fields. + */ +public class BlockJUnit4ClassRunnerWithParameters + extends BlockJUnit4ClassRunner { + private enum InjectionType { + CONSTRUCTOR, FIELD + } + + private final Object[] parameters; + + private final String name; + + public BlockJUnit4ClassRunnerWithParameters(TestWithParameters test) + throws InitializationError { + super(test.getTestClass().getJavaClass()); + parameters = test.getParameters() + .toArray(new Object[test.getParameters().size()]); + name = test.getName(); + } + + @Override + public Object createTest() throws Exception { + return createInstanceOfParameterizedTest(getTestClass(), parameters); + } + + public static Object createInstanceOfParameterizedTest(TestClass testClass, Object[] parameters) + throws Exception { + InjectionType injectionType = getInjectionType(testClass); + switch (injectionType) { + case CONSTRUCTOR: + return createTestUsingConstructorInjection(testClass, parameters); + case FIELD: + return createTestUsingFieldInjection(testClass, parameters); + default: + throw new IllegalStateException("The injection type " + + injectionType + " is not supported."); + } + } + + private static Object createTestUsingConstructorInjection(TestClass testClass, Object[] parameters) throws Exception { + return testClass.getOnlyConstructor().newInstance(parameters); + } + + private static Object createTestUsingFieldInjection(TestClass testClass, Object[] parameters) throws Exception { + List annotatedFieldsByParameter = getAnnotatedFieldsByParameter( + testClass); + if (annotatedFieldsByParameter.size() != parameters.length) { + throw new Exception( + "Wrong number of parameters and @Parameter fields." + + " @Parameter fields counted: " + + annotatedFieldsByParameter.size() + + ", available parameters: " + parameters.length + + "."); + } + Object testClassInstance = testClass.getJavaClass().newInstance(); + for (FrameworkField each : annotatedFieldsByParameter) { + Field field = each.getField(); + Parameter annotation = field.getAnnotation(Parameter.class); + int index = annotation.value(); + try { + field.set(testClassInstance, parameters[index]); + } catch (IllegalArgumentException iare) { + throw new Exception(testClass.getName() + + ": Trying to set " + field.getName() + + " with the value " + parameters[index] + + " that is not the right type (" + + parameters[index].getClass().getSimpleName() + + " instead of " + field.getType().getSimpleName() + + ").", iare); + } + } + return testClassInstance; + } + + @Override + protected String getName() { + return name; + } + + @Override + protected String testName(FrameworkMethod method) { + return method.getName() + getName(); + } + + @Override + protected void validateConstructor(List errors) { + validateOnlyOneConstructor(errors); + if (getInjectionType(getTestClass()) != InjectionType.CONSTRUCTOR) { + validateZeroArgConstructor(errors); + } + } + + @Override + protected void validateFields(List errors) { + super.validateFields(errors); + if (getInjectionType(getTestClass()) == InjectionType.FIELD) { + List annotatedFieldsByParameter = getAnnotatedFieldsByParameter( + getTestClass()); + int[] usedIndices = new int[annotatedFieldsByParameter.size()]; + for (FrameworkField each : annotatedFieldsByParameter) { + int index = each.getField().getAnnotation(Parameter.class) + .value(); + if (index < 0 + || index > annotatedFieldsByParameter.size() - 1) { + errors.add(new Exception("Invalid @Parameter value: " + + index + ". @Parameter fields counted: " + + annotatedFieldsByParameter.size() + + ". Please use an index between 0 and " + + (annotatedFieldsByParameter.size() - 1) + ".")); + } else { + usedIndices[index]++; + } + } + for (int index = 0; index < usedIndices.length; index++) { + int numberOfUse = usedIndices[index]; + if (numberOfUse == 0) { + errors.add(new Exception( + "@Parameter(" + index + ") is never used.")); + } else if (numberOfUse > 1) { + errors.add(new Exception( + "@Parameter(" + index + ") is used more than once (" + + numberOfUse + ").")); + } + } + } + } + + @Override + protected Statement classBlock(RunNotifier notifier) { + return childrenInvoker(notifier); + } + + @Override + protected Annotation[] getRunnerAnnotations() { + Annotation[] allAnnotations = super.getRunnerAnnotations(); + Annotation[] annotationsWithoutRunWith = new Annotation[allAnnotations.length + - 1]; + int i = 0; + for (Annotation annotation : allAnnotations) { + if (!annotation.annotationType().equals(RunWith.class)) { + annotationsWithoutRunWith[i] = annotation; + ++i; + } + } + return annotationsWithoutRunWith; + } + + public static List getAnnotatedFieldsByParameter( + TestClass testClass) { + return testClass.getAnnotatedFields(Parameter.class); + } + + private static InjectionType getInjectionType(TestClass testClass) { + if (fieldsAreAnnotated(testClass)) { + return InjectionType.FIELD; + } else { + return InjectionType.CONSTRUCTOR; + } + } + + private static boolean fieldsAreAnnotated(TestClass testClass) { + return !getAnnotatedFieldsByParameter(testClass).isEmpty(); + } +}