From 3e0cd956bb3050220f092618ffc034be43ab9438 Mon Sep 17 00:00:00 2001 From: Ilya Lim Date: Fri, 23 Mar 2018 11:25:09 +0700 Subject: [PATCH 1/5] Preparation for global rules implementation --- .../internal/requests/GlobalRuleRequest.java | 32 +++++++++++++++ .../runner/JUnitCommandLineParseResult.java | 41 ++++++++++++++++++- src/main/java/org/junit/runner/Request.java | 6 +++ .../manipulation/GlobalRuleRunnable.java | 9 ++++ .../runner/manipulation/GlobalRuleRunner.java | 20 +++++++++ .../java/org/junit/runners/ParentRunner.java | 21 ++++++---- 6 files changed, 121 insertions(+), 8 deletions(-) create mode 100644 src/main/java/org/junit/internal/requests/GlobalRuleRequest.java create mode 100644 src/main/java/org/junit/runner/manipulation/GlobalRuleRunnable.java create mode 100644 src/main/java/org/junit/runner/manipulation/GlobalRuleRunner.java diff --git a/src/main/java/org/junit/internal/requests/GlobalRuleRequest.java b/src/main/java/org/junit/internal/requests/GlobalRuleRequest.java new file mode 100644 index 000000000000..ec14872b54a7 --- /dev/null +++ b/src/main/java/org/junit/internal/requests/GlobalRuleRequest.java @@ -0,0 +1,32 @@ +package org.junit.internal.requests; + +import org.junit.runner.Request; +import org.junit.runner.Runner; +import org.junit.runner.manipulation.GlobalRuleRunner; + +/** + * A {@link Request} that adds global rules to the {@link Runner}. + */ +public final class GlobalRuleRequest extends Request { + private final Request request; + private final GlobalRuleRunner ruleRunner; + + /** + * Creates a Request with global rules + * + * @param request a {@link Request} describing your Tests + * @param ruleRunner {@link GlobalRuleRunner} to apply to the Tests described in + * request + */ + public GlobalRuleRequest(Request request, GlobalRuleRunner ruleRunner) { + this.request = request; + this.ruleRunner = ruleRunner; + } + + @Override + public Runner getRunner() { + Runner runner = request.getRunner(); + ruleRunner.apply(runner); + return runner; + } +} \ No newline at end of file diff --git a/src/main/java/org/junit/runner/JUnitCommandLineParseResult.java b/src/main/java/org/junit/runner/JUnitCommandLineParseResult.java index 338340789ee8..2b5f26e23591 100644 --- a/src/main/java/org/junit/runner/JUnitCommandLineParseResult.java +++ b/src/main/java/org/junit/runner/JUnitCommandLineParseResult.java @@ -5,14 +5,17 @@ import java.util.List; import org.junit.internal.Classes; +import org.junit.rules.TestRule; import org.junit.runner.FilterFactory.FilterNotCreatedException; import org.junit.runner.manipulation.Filter; +import org.junit.runner.manipulation.GlobalRuleRunner; import org.junit.runners.model.InitializationError; class JUnitCommandLineParseResult { private final List filterSpecs = new ArrayList(); private final List> classes = new ArrayList>(); private final List parserErrors = new ArrayList(); + private final List> globalRules = new ArrayList>(); /** * Do not use. Testing purposes only. @@ -33,6 +36,13 @@ public List> getClasses() { return Collections.unmodifiableList(classes); } + /** + * Returns global rules classes parsed from command line. + */ + public List> getGlobalRules() { + return Collections.unmodifiableList(globalRules); + } + /** * Parses the arguments. * @@ -73,6 +83,27 @@ String[] parseOptions(String... args) { } filterSpecs.add(filterSpec); + } else if (arg.equals("--global-rule")) { + ++i; + + if (i < args.length) { + try { + Class clazz = Classes.getClass(args[i]); + + if (TestRule.class.isAssignableFrom(clazz)) { + globalRules.add(clazz); + } else { + parserErrors.add(new CommandLineParserError(args[i] + " does not implement TestRule")); + break; + } + } catch (ClassNotFoundException e) { + parserErrors.add(new CommandLineParserError(args[i] + " is not a valid value for global rule")); + break; + } + } else { + parserErrors.add(new CommandLineParserError(arg + " value not specified")); + break; + } } else { parserErrors.add(new CommandLineParserError("JUnit knows nothing about the " + arg + " option")); } @@ -115,12 +146,20 @@ public Request createRequest(Computer computer) { if (parserErrors.isEmpty()) { Request request = Request.classes( computer, classes.toArray(new Class[classes.size()])); - return applyFilterSpecs(request); + return applyFilterSpecs(applyGlobalRules(request)); } else { return errorReport(new InitializationError(parserErrors)); } } + private Request applyGlobalRules(Request request) { + if (!globalRules.isEmpty()) { + return request.withGlobalRules(new GlobalRuleRunner(globalRules)); + } else { + return request; + } + } + private Request applyFilterSpecs(Request request) { try { for (String filterSpec : filterSpecs) { diff --git a/src/main/java/org/junit/runner/Request.java b/src/main/java/org/junit/runner/Request.java index 264489217f74..cbef0c1fec4c 100644 --- a/src/main/java/org/junit/runner/Request.java +++ b/src/main/java/org/junit/runner/Request.java @@ -5,9 +5,11 @@ import org.junit.internal.builders.AllDefaultPossibilitiesBuilder; import org.junit.internal.requests.ClassRequest; import org.junit.internal.requests.FilterRequest; +import org.junit.internal.requests.GlobalRuleRequest; import org.junit.internal.requests.SortingRequest; import org.junit.internal.runners.ErrorReportingRunner; import org.junit.runner.manipulation.Filter; +import org.junit.runner.manipulation.GlobalRuleRunner; import org.junit.runners.model.InitializationError; /** @@ -169,4 +171,8 @@ public Request filterWith(Description desiredDescription) { public Request sortWith(Comparator comparator) { return new SortingRequest(this, comparator); } + + public Request withGlobalRules(GlobalRuleRunner ruleRunner) { + return new GlobalRuleRequest(this, ruleRunner); + } } diff --git a/src/main/java/org/junit/runner/manipulation/GlobalRuleRunnable.java b/src/main/java/org/junit/runner/manipulation/GlobalRuleRunnable.java new file mode 100644 index 000000000000..46e7c3d7cae0 --- /dev/null +++ b/src/main/java/org/junit/runner/manipulation/GlobalRuleRunnable.java @@ -0,0 +1,9 @@ +package org.junit.runner.manipulation; + +import java.util.List; + +public interface GlobalRuleRunnable { + + void setGlobalRules(List> rules); + +} diff --git a/src/main/java/org/junit/runner/manipulation/GlobalRuleRunner.java b/src/main/java/org/junit/runner/manipulation/GlobalRuleRunner.java new file mode 100644 index 000000000000..91b0f47722c4 --- /dev/null +++ b/src/main/java/org/junit/runner/manipulation/GlobalRuleRunner.java @@ -0,0 +1,20 @@ +package org.junit.runner.manipulation; + +import java.util.List; + +public class GlobalRuleRunner { + + private final List> rules; + + public GlobalRuleRunner(List> rules) { + this.rules = rules; + } + + public void apply(Object object) { + if (object instanceof GlobalRuleRunnable) { + GlobalRuleRunnable runnable = (GlobalRuleRunnable) object; + runnable.setGlobalRules(rules); + } + } + +} diff --git a/src/main/java/org/junit/runners/ParentRunner.java b/src/main/java/org/junit/runners/ParentRunner.java index 4949c242e5b2..e1dec9a50ca0 100644 --- a/src/main/java/org/junit/runners/ParentRunner.java +++ b/src/main/java/org/junit/runners/ParentRunner.java @@ -29,11 +29,7 @@ import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runner.Runner; -import org.junit.runner.manipulation.Filter; -import org.junit.runner.manipulation.Filterable; -import org.junit.runner.manipulation.NoTestsRemainException; -import org.junit.runner.manipulation.Sortable; -import org.junit.runner.manipulation.Sorter; +import org.junit.runner.manipulation.*; import org.junit.runner.notification.RunNotifier; import org.junit.runner.notification.StoppedByUserException; import org.junit.runners.model.FrameworkMember; @@ -61,7 +57,7 @@ * @since 4.5 */ public abstract class ParentRunner extends Runner implements Filterable, - Sortable { + Sortable, GlobalRuleRunnable { private static final List VALIDATORS = Arrays.asList( new AnnotationsValidator()); @@ -71,6 +67,8 @@ public abstract class ParentRunner extends Runner implements Filterable, // Guarded by childrenLock private volatile Collection filteredChildren = null; + private List globalRules = new ArrayList(); + private volatile RunnerScheduler scheduler = new RunnerScheduler() { public void schedule(Runnable childStatement) { childStatement.run(); @@ -403,7 +401,7 @@ public void run(final RunNotifier notifier) { } // - // Implementation of Filterable and Sortable + // Implementation of Filterable, Sortable and GlobalRuleRunnable // public void filter(Filter filter) throws NoTestsRemainException { @@ -445,6 +443,15 @@ public void sort(Sorter sorter) { } } + public void setGlobalRules(List> rules) { + if (!globalRules.isEmpty()) { + globalRules.clear(); + } + for (Class clazz : rules) { + + } + } + // // Private implementation // From 46e5beb85e904a26cff76633a670e95e31559666 Mon Sep 17 00:00:00 2001 From: Ilya Lim Date: Fri, 23 Mar 2018 11:44:36 +0700 Subject: [PATCH 2/5] Global rules instantiation and invocation --- .../internal/requests/GlobalRuleRequest.java | 11 ++++-- .../manipulation/GlobalRuleRunnable.java | 2 +- .../java/org/junit/runners/ParentRunner.java | 36 +++++++++++++++---- 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/junit/internal/requests/GlobalRuleRequest.java b/src/main/java/org/junit/internal/requests/GlobalRuleRequest.java index ec14872b54a7..10aa1f0f318e 100644 --- a/src/main/java/org/junit/internal/requests/GlobalRuleRequest.java +++ b/src/main/java/org/junit/internal/requests/GlobalRuleRequest.java @@ -1,5 +1,6 @@ package org.junit.internal.requests; +import org.junit.internal.runners.ErrorReportingRunner; import org.junit.runner.Request; import org.junit.runner.Runner; import org.junit.runner.manipulation.GlobalRuleRunner; @@ -25,8 +26,12 @@ public GlobalRuleRequest(Request request, GlobalRuleRunner ruleRunner) { @Override public Runner getRunner() { - Runner runner = request.getRunner(); - ruleRunner.apply(runner); - return runner; + try { + Runner runner = request.getRunner(); + ruleRunner.apply(runner); + return runner; + } catch (Exception e) { + return new ErrorReportingRunner(GlobalRuleRunner.class, e); + } } } \ No newline at end of file diff --git a/src/main/java/org/junit/runner/manipulation/GlobalRuleRunnable.java b/src/main/java/org/junit/runner/manipulation/GlobalRuleRunnable.java index 46e7c3d7cae0..97a619b15c68 100644 --- a/src/main/java/org/junit/runner/manipulation/GlobalRuleRunnable.java +++ b/src/main/java/org/junit/runner/manipulation/GlobalRuleRunnable.java @@ -4,6 +4,6 @@ public interface GlobalRuleRunnable { - void setGlobalRules(List> rules); + void setGlobalRules(List> rules) throws Exception; } diff --git a/src/main/java/org/junit/runners/ParentRunner.java b/src/main/java/org/junit/runners/ParentRunner.java index e1dec9a50ca0..01a59bb1cf04 100644 --- a/src/main/java/org/junit/runners/ParentRunner.java +++ b/src/main/java/org/junit/runners/ParentRunner.java @@ -5,6 +5,7 @@ import static org.junit.internal.runners.rules.RuleMemberValidator.CLASS_RULE_VALIDATOR; import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; @@ -16,11 +17,7 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Ignore; -import org.junit.Rule; +import org.junit.*; import org.junit.internal.AssumptionViolatedException; import org.junit.internal.runners.model.EachTestNotifier; import org.junit.internal.runners.statements.RunAfters; @@ -190,6 +187,7 @@ private void validateClassRules(List errors) { * construct a statement that will: *
    *
  1. Apply all {@code ClassRule}s on the test-class and superclasses.
  2. + *
  3. Apply all {@code GlobalRule}s on the test-class and superclasses.
  4. *
  5. Run all non-overridden {@code @BeforeClass} methods on the test-class * and superclasses; if any throws an Exception, stop execution and pass the * exception on.
  6. @@ -210,6 +208,7 @@ protected Statement classBlock(final RunNotifier notifier) { statement = withBeforeClasses(statement); statement = withAfterClasses(statement); statement = withClassRules(statement); + statement = withGlobalRules(statement); } return statement; } @@ -264,6 +263,20 @@ private Statement withClassRules(Statement statement) { new RunRules(statement, classRules, getDescription()); } + /** + * Returns a {@link Statement}: apply all + * classes assignable to {@link TestRule} provided + * by command line argument {@code --global-rule}. + * + * @param statement the base statement + * @return a RunRules statement if any global-level {@link TestRule}s are + * provided, or the base statement + */ + private Statement withGlobalRules(Statement statement) { + return globalRules.isEmpty() ? statement : + new RunRules(statement, globalRules, getDescription()); + } + /** * @return the {@code ClassRule}s that can transform the block that runs * each method in the tested class. @@ -443,12 +456,21 @@ public void sort(Sorter sorter) { } } - public void setGlobalRules(List> rules) { + public void setGlobalRules(List> rules) throws Exception { if (!globalRules.isEmpty()) { globalRules.clear(); } for (Class clazz : rules) { - + Constructor[] constructors = clazz.getConstructors(); + if (constructors.length > 1) { + throw new IllegalArgumentException("Global TestRule can only have one constructor"); + } + Assert.assertEquals(1, constructors.length); + Constructor constructor = clazz.getConstructors()[0]; + if (constructor.getParameterTypes().length > 0) { + throw new IllegalArgumentException("Global TestRule constructor cannot have parameters"); + } + globalRules.add(TestRule.class.cast(constructor.newInstance())); } } From ed751e83ccfb7468268ebde82d8e5dbc078329a8 Mon Sep 17 00:00:00 2001 From: Ilya Lim Date: Fri, 23 Mar 2018 12:04:58 +0700 Subject: [PATCH 3/5] Documentations --- src/main/java/org/junit/runner/Request.java | 8 ++++++++ .../runner/manipulation/GlobalRuleRunnable.java | 13 +++++++++++++ .../junit/runner/manipulation/GlobalRuleRunner.java | 12 +++++++++++- src/main/java/org/junit/runners/ParentRunner.java | 6 +++--- 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/junit/runner/Request.java b/src/main/java/org/junit/runner/Request.java index cbef0c1fec4c..474e8a814fb3 100644 --- a/src/main/java/org/junit/runner/Request.java +++ b/src/main/java/org/junit/runner/Request.java @@ -8,6 +8,7 @@ import org.junit.internal.requests.GlobalRuleRequest; import org.junit.internal.requests.SortingRequest; import org.junit.internal.runners.ErrorReportingRunner; +import org.junit.rules.TestRule; import org.junit.runner.manipulation.Filter; import org.junit.runner.manipulation.GlobalRuleRunner; import org.junit.runners.model.InitializationError; @@ -172,6 +173,13 @@ public Request sortWith(Comparator comparator) { return new SortingRequest(this, comparator); } + /** + * Returns a Request with global {@link TestRule}s that are applied + * to every test being run with this Request + * + * @param ruleRunner The {@link GlobalRuleRunner} to apply to this Request + * @return a Request with applied global {@link TestRule}s + */ public Request withGlobalRules(GlobalRuleRunner ruleRunner) { return new GlobalRuleRequest(this, ruleRunner); } diff --git a/src/main/java/org/junit/runner/manipulation/GlobalRuleRunnable.java b/src/main/java/org/junit/runner/manipulation/GlobalRuleRunnable.java index 97a619b15c68..01e9ca376206 100644 --- a/src/main/java/org/junit/runner/manipulation/GlobalRuleRunnable.java +++ b/src/main/java/org/junit/runner/manipulation/GlobalRuleRunnable.java @@ -2,8 +2,21 @@ import java.util.List; +/** + * Runners that allow global {@link org.junit.rules.TestRule} should implement this interface. + * Implement {@link #setGlobalRules(List)}} to receive the list of classes assignable to + * {@link org.junit.rules.TestRule} to apply on every test. + * + * @since 4.13 + */ public interface GlobalRuleRunnable { + /** + * Instantiate and apply global {@link org.junit.rules.TestRule}s to every test. + * + * @param rules the list of classes assignable to {@link org.junit.rules.TestRule} to instantiate + * @throws Exception if any class does not meet {@link org.junit.runner.Runner}'s requirements + */ void setGlobalRules(List> rules) throws Exception; } diff --git a/src/main/java/org/junit/runner/manipulation/GlobalRuleRunner.java b/src/main/java/org/junit/runner/manipulation/GlobalRuleRunner.java index 91b0f47722c4..7f660865e7dc 100644 --- a/src/main/java/org/junit/runner/manipulation/GlobalRuleRunner.java +++ b/src/main/java/org/junit/runner/manipulation/GlobalRuleRunner.java @@ -2,6 +2,13 @@ import java.util.List; +/** + * A GlobalRuleRunner passes global {@link org.junit.rules.TestRule} to the {@link org.junit.runner.Runner}. + * In general you will not need to use a GlobalRuleRunner directly. Instead, + * use {@link org.junit.runner.Request#withGlobalRules(GlobalRuleRunner)}. + * + * @since 4.13 + */ public class GlobalRuleRunner { private final List> rules; @@ -10,7 +17,10 @@ public GlobalRuleRunner(List> rules) { this.rules = rules; } - public void apply(Object object) { + /** + * Passes the list of classes assignable from {@link org.junit.rules.TestRule} to the runner + */ + public void apply(Object object) throws Exception { if (object instanceof GlobalRuleRunnable) { GlobalRuleRunnable runnable = (GlobalRuleRunnable) object; runnable.setGlobalRules(rules); diff --git a/src/main/java/org/junit/runners/ParentRunner.java b/src/main/java/org/junit/runners/ParentRunner.java index 01a59bb1cf04..30ed24ccac31 100644 --- a/src/main/java/org/junit/runners/ParentRunner.java +++ b/src/main/java/org/junit/runners/ParentRunner.java @@ -48,8 +48,8 @@ * must implement finding the children of the node, describing each child, and * running each child. ParentRunner will filter and sort children, handle * {@code @BeforeClass} and {@code @AfterClass} methods, - * handle annotated {@link ClassRule}s, create a composite - * {@link Description}, and run children sequentially. + * handle annotated {@link ClassRule}s, handle provided global {@link TestRule}s, + * create a composite {@link Description}, and run children sequentially. * * @since 4.5 */ @@ -187,7 +187,7 @@ private void validateClassRules(List errors) { * construct a statement that will: *
      *
    1. Apply all {@code ClassRule}s on the test-class and superclasses.
    2. - *
    3. Apply all {@code GlobalRule}s on the test-class and superclasses.
    4. + *
    5. Apply all global {@code TestRule}s on the test-class and superclasses.
    6. *
    7. Run all non-overridden {@code @BeforeClass} methods on the test-class * and superclasses; if any throws an Exception, stop execution and pass the * exception on.
    8. From 126b4e92e92f864f5da7c1a5b673cfbaa49bc4d3 Mon Sep 17 00:00:00 2001 From: Ilya Lim Date: Fri, 23 Mar 2018 12:46:19 +0700 Subject: [PATCH 4/5] Tests added --- .../java/org/junit/rules/AllRulesTests.java | 1 + .../java/org/junit/rules/GlobalRulesTest.java | 61 +++++++++++++++++++ .../JUnitCommandLineParseResultTest.java | 58 ++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 src/test/java/org/junit/rules/GlobalRulesTest.java diff --git a/src/test/java/org/junit/rules/AllRulesTests.java b/src/test/java/org/junit/rules/AllRulesTests.java index 8b63e1510db3..efe356fd4195 100644 --- a/src/test/java/org/junit/rules/AllRulesTests.java +++ b/src/test/java/org/junit/rules/AllRulesTests.java @@ -8,6 +8,7 @@ @SuiteClasses({ BlockJUnit4ClassRunnerOverrideTest.class, ClassRulesTest.class, + GlobalRulesTest.class, DisableOnDebugTest.class, ErrorCollectorTest.class, ExpectedExceptionTest.class, diff --git a/src/test/java/org/junit/rules/GlobalRulesTest.java b/src/test/java/org/junit/rules/GlobalRulesTest.java new file mode 100644 index 000000000000..6688f06188a0 --- /dev/null +++ b/src/test/java/org/junit/rules/GlobalRulesTest.java @@ -0,0 +1,61 @@ +package org.junit.rules; + +import org.junit.Test; +import org.junit.runner.Description; +import org.junit.runner.JUnitCore; +import org.junit.runner.Request; +import org.junit.runner.manipulation.GlobalRuleRunner; +import org.junit.runners.model.Statement; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +/** + * Tests to exercise global-level rules. + */ +public class GlobalRulesTest { + private static int count = 0; + + public static class Counter implements TestRule { + public Statement apply(Statement base, Description description) { + count++; + return base; + } + } + + public static class ExampleTest { + @Test + public void firstTest() { + assertEquals(1, count); + } + + @Test + public void secondTest() { + assertEquals(1, count); + } + } + + public static class SecondExampleTest { + @Test + public void firstTest() { + assertEquals(2, count); + } + + @Test + public void secondTest() { + assertEquals(2, count); + } + } + + @Test + public void rulesAreAppliedOnEveryTest() { + count = 0; + List> rules = new ArrayList>(); + rules.add(Counter.class); + new JUnitCore().run(Request.classes(ExampleTest.class, SecondExampleTest.class) + .withGlobalRules(new GlobalRuleRunner(rules))); + assertEquals(2, count); + } +} diff --git a/src/test/java/org/junit/runner/JUnitCommandLineParseResultTest.java b/src/test/java/org/junit/runner/JUnitCommandLineParseResultTest.java index ffd1f1c9552f..e76d532b3770 100644 --- a/src/test/java/org/junit/runner/JUnitCommandLineParseResultTest.java +++ b/src/test/java/org/junit/runner/JUnitCommandLineParseResultTest.java @@ -11,7 +11,9 @@ import org.junit.Test; import org.junit.experimental.categories.IncludeCategories; import org.junit.rules.ExpectedException; +import org.junit.rules.TestRule; import org.junit.runner.manipulation.Filter; +import org.junit.runners.model.Statement; public class JUnitCommandLineParseResultTest { @Rule @@ -47,6 +49,16 @@ public void shouldCreateFailureUponBaldFilterOptionNotFollowedByValue() { assertThat(description.toString(), containsString("initializationError")); } + @Test + public void shouldCreateFailureUponBaldGlobalRuleOptionNotFollowedByValue() { + jUnitCommandLineParseResult.parseOptions("--global-rule"); + + Runner runner = jUnitCommandLineParseResult.createRequest(new Computer()).getRunner(); + Description description = runner.getDescription().getChildren().get(0); + + assertThat(description.toString(), containsString("initializationError")); + } + @Test public void shouldParseFilterArgInWhichValueIsASeparateArg() throws Exception { String value= IncludeCategories.class.getName() + "=" + DummyCategory0.class.getName(); @@ -116,6 +128,19 @@ public void shouldAddToClasses() { assertThat(testClass.getName(), is(DummyTest.class.getName())); } + @Test + public void shouldAddToGlobalRules() { + jUnitCommandLineParseResult.parseOptions(new String[]{ + "--global-rule", + DummyRule.class.getName() + }); + + List> classes = jUnitCommandLineParseResult.getGlobalRules(); + Class ruleClass = classes.get(0); + + assertThat(ruleClass.getName(), is(DummyRule.class.getName())); + } + @Test public void shouldCreateFailureUponUnknownTestClass() throws Exception { String unknownTestClass = "UnknownTestClass"; @@ -129,6 +154,33 @@ public void shouldCreateFailureUponUnknownTestClass() throws Exception { assertThat(description.toString(), containsString("initializationError")); } + @Test + public void shouldCreateFailureUponUnknownGlobalRule() throws Exception { + String unknownRuleClass = "UnknownRuleClass"; + jUnitCommandLineParseResult.parseOptions(new String[]{ + "--global-rule", + unknownRuleClass + }); + + Runner runner = jUnitCommandLineParseResult.createRequest(new Computer()).getRunner(); + Description description = runner.getDescription().getChildren().get(0); + + assertThat(description.toString(), containsString("initializationError")); + } + + @Test + public void shouldCreateFailureUponNonAssignableToTestRuleGlobalRule() throws Exception { + jUnitCommandLineParseResult.parseOptions(new String[]{ + "--global-rule", + DummyTest.class.getName() + }); + + Runner runner = jUnitCommandLineParseResult.createRequest(new Computer()).getRunner(); + Description description = runner.getDescription().getChildren().get(0); + + assertThat(description.toString(), containsString("initializationError")); + } + public static class FilterFactoryStub implements FilterFactory { public Filter createFilter(FilterFactoryParams params) throws FilterNotCreatedException { throw new FilterNotCreatedException(new Exception("stub")); @@ -143,4 +195,10 @@ public static class DummyTest { public void dummyTest() { } } + + public static class DummyRule implements TestRule { + public Statement apply(Statement base, Description description) { + return base; + } + } } From bd94ebbe82b0e01b4fb72d59f8c30fc9d7aa78c7 Mon Sep 17 00:00:00 2001 From: Ilya Lim Date: Fri, 23 Mar 2018 13:24:36 +0700 Subject: [PATCH 5/5] Refactor runner to apply to all children, fix test --- .../manipulation/GlobalRuleRunnable.java | 8 ++--- .../runner/manipulation/GlobalRuleRunner.java | 9 ++++-- .../java/org/junit/runners/ParentRunner.java | 31 +++++++++++-------- .../java/org/junit/rules/GlobalRulesTest.java | 11 +++---- 4 files changed, 32 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/junit/runner/manipulation/GlobalRuleRunnable.java b/src/main/java/org/junit/runner/manipulation/GlobalRuleRunnable.java index 01e9ca376206..c32219d6f3d3 100644 --- a/src/main/java/org/junit/runner/manipulation/GlobalRuleRunnable.java +++ b/src/main/java/org/junit/runner/manipulation/GlobalRuleRunnable.java @@ -1,10 +1,8 @@ package org.junit.runner.manipulation; -import java.util.List; - /** * Runners that allow global {@link org.junit.rules.TestRule} should implement this interface. - * Implement {@link #setGlobalRules(List)}} to receive the list of classes assignable to + * Implement {@link #setGlobalRules(GlobalRuleRunner)}} to receive the list of classes assignable to * {@link org.junit.rules.TestRule} to apply on every test. * * @since 4.13 @@ -14,9 +12,9 @@ public interface GlobalRuleRunnable { /** * Instantiate and apply global {@link org.junit.rules.TestRule}s to every test. * - * @param rules the list of classes assignable to {@link org.junit.rules.TestRule} to instantiate + * @param ruleRunner the {@link GlobalRuleRunner} to apply * @throws Exception if any class does not meet {@link org.junit.runner.Runner}'s requirements */ - void setGlobalRules(List> rules) throws Exception; + void setGlobalRules(GlobalRuleRunner ruleRunner) throws Exception; } diff --git a/src/main/java/org/junit/runner/manipulation/GlobalRuleRunner.java b/src/main/java/org/junit/runner/manipulation/GlobalRuleRunner.java index 7f660865e7dc..d833c7ca72a3 100644 --- a/src/main/java/org/junit/runner/manipulation/GlobalRuleRunner.java +++ b/src/main/java/org/junit/runner/manipulation/GlobalRuleRunner.java @@ -3,7 +3,7 @@ import java.util.List; /** - * A GlobalRuleRunner passes global {@link org.junit.rules.TestRule} to the {@link org.junit.runner.Runner}. + * A GlobalRuleRunner passes global {@link org.junit.rules.TestRule}s to the {@link org.junit.runner.Runner}. * In general you will not need to use a GlobalRuleRunner directly. Instead, * use {@link org.junit.runner.Request#withGlobalRules(GlobalRuleRunner)}. * @@ -18,13 +18,16 @@ public GlobalRuleRunner(List> rules) { } /** - * Passes the list of classes assignable from {@link org.junit.rules.TestRule} to the runner + * Applies provided global {@link org.junit.rules.TestRule}s to the runner */ public void apply(Object object) throws Exception { if (object instanceof GlobalRuleRunnable) { GlobalRuleRunnable runnable = (GlobalRuleRunnable) object; - runnable.setGlobalRules(rules); + runnable.setGlobalRules(this); } } + public List> getRules() { + return rules; + } } diff --git a/src/main/java/org/junit/runners/ParentRunner.java b/src/main/java/org/junit/runners/ParentRunner.java index 30ed24ccac31..b77b928c6315 100644 --- a/src/main/java/org/junit/runners/ParentRunner.java +++ b/src/main/java/org/junit/runners/ParentRunner.java @@ -456,21 +456,26 @@ public void sort(Sorter sorter) { } } - public void setGlobalRules(List> rules) throws Exception { - if (!globalRules.isEmpty()) { - globalRules.clear(); - } - for (Class clazz : rules) { - Constructor[] constructors = clazz.getConstructors(); - if (constructors.length > 1) { - throw new IllegalArgumentException("Global TestRule can only have one constructor"); + public void setGlobalRules(GlobalRuleRunner rules) throws Exception { + childrenLock.lock(); + try { + for (T each : getFilteredChildren()) { + rules.apply(each); } - Assert.assertEquals(1, constructors.length); - Constructor constructor = clazz.getConstructors()[0]; - if (constructor.getParameterTypes().length > 0) { - throw new IllegalArgumentException("Global TestRule constructor cannot have parameters"); + for (Class clazz : rules.getRules()) { + Constructor[] constructors = clazz.getConstructors(); + if (constructors.length > 1) { + throw new IllegalArgumentException("Global TestRule can only have one constructor"); + } + Assert.assertEquals(1, constructors.length); + Constructor constructor = clazz.getConstructors()[0]; + if (constructor.getParameterTypes().length > 0) { + throw new IllegalArgumentException("Global TestRule constructor cannot have parameters"); + } + globalRules.add(TestRule.class.cast(constructor.newInstance())); } - globalRules.add(TestRule.class.cast(constructor.newInstance())); + } finally { + childrenLock.unlock(); } } diff --git a/src/test/java/org/junit/rules/GlobalRulesTest.java b/src/test/java/org/junit/rules/GlobalRulesTest.java index 6688f06188a0..46de1bc9a131 100644 --- a/src/test/java/org/junit/rules/GlobalRulesTest.java +++ b/src/test/java/org/junit/rules/GlobalRulesTest.java @@ -28,34 +28,33 @@ public Statement apply(Statement base, Description description) { public static class ExampleTest { @Test public void firstTest() { - assertEquals(1, count); + assertEquals(2, count); } @Test public void secondTest() { - assertEquals(1, count); + assertEquals(2, count); } } public static class SecondExampleTest { @Test public void firstTest() { - assertEquals(2, count); + assertEquals(3, count); } @Test public void secondTest() { - assertEquals(2, count); + assertEquals(3, count); } } @Test public void rulesAreAppliedOnEveryTest() { - count = 0; List> rules = new ArrayList>(); rules.add(Counter.class); new JUnitCore().run(Request.classes(ExampleTest.class, SecondExampleTest.class) .withGlobalRules(new GlobalRuleRunner(rules))); - assertEquals(2, count); + assertEquals(3, count); } }