/** * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ package net.sourceforge.pmd.testframework; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import org.junit.Test; 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.notification.Failure; import org.junit.runner.notification.RunNotifier; import org.junit.runners.BlockJUnit4ClassRunner; import org.junit.runners.model.InitializationError; import org.junit.runners.model.TestClass; import net.sourceforge.pmd.Rule; /** * A test runner for rule tests. It reports * the successful executed tests and allows to selectively execute single test * cases (it is {@link Filterable}). * * <p>In order to use it, you'll need to subclass {@link SimpleAggregatorTst} and * annotate your test class with RunWith:</p> * * <pre> * {@code @}RunWith(PMDTestRunner.class) * public class MyRuleSetTest extends SimpleAggregatorTst { * ... * } * </pre> */ public class PMDTestRunner extends Runner implements Filterable { private final Description desc; private final Class<? extends SimpleAggregatorTst> klass; private final List<TestDescriptor> allTests = new ArrayList<>(); private BlockJUnit4ClassRunner chainedRunner; /** * Creates a new {@link PMDTestRunner} for the given test class. * * @param klass * the test class that is under test * @throws InitializationError * any error */ public PMDTestRunner(final Class<? extends SimpleAggregatorTst> klass) throws InitializationError { this.klass = klass; desc = Description.createSuiteDescription(klass); configureRuleTests(); configureUnitTests(); } private void configureRuleTests() throws InitializationError { Description root = Description.createSuiteDescription("Rule Tests"); try { SimpleAggregatorTst test = createTestClass(); test.setUp(); List<Rule> rules = new ArrayList<>(test.getRules()); Collections.sort(rules, new Comparator<Rule>() { @Override public int compare(Rule o1, Rule o2) { return o1.getName().compareTo(o2.getName()); } }); for (Rule r : rules) { Description ruleDescription = Description.createSuiteDescription(r.getName()); root.addChild(ruleDescription); TestDescriptor[] ruleTests = test.extractTestsFromXml(r); for (TestDescriptor t : ruleTests) { Description d = createTestDescription(t); ruleDescription.addChild(d); allTests.add(t); } } if (!root.getChildren().isEmpty()) { desc.addChild(root); } } catch (Exception e) { throw new InitializationError(e); } } private SimpleAggregatorTst createTestClass() { try { return klass.getConstructor().newInstance(); } catch (Exception e) { throw new RuntimeException(e); } } private void configureUnitTests() throws InitializationError { TestClass tclass = new TestClass(klass); if (!tclass.getAnnotatedMethods(Test.class).isEmpty()) { Description unitTests = Description.createSuiteDescription("Unit tests"); chainedRunner = new BlockJUnit4ClassRunner(klass); for (Description d : chainedRunner.getDescription().getChildren()) { unitTests.addChild(d); } desc.addChild(unitTests); } } @Override public Description getDescription() { return desc; } @Override public void run(RunNotifier notifier) { SimpleAggregatorTst test = createTestClass(); boolean regressionTestMode = TestDescriptor.inRegressionTestMode(); for (TestDescriptor t : allTests) { Description d = createTestDescription(t); notifier.fireTestStarted(d); try { if (!regressionTestMode || t.isRegressionTest()) { test.runTest(t); } else { notifier.fireTestIgnored(d); } } catch (Throwable e) { notifier.fireTestFailure(new Failure(d, e)); } finally { notifier.fireTestFinished(d); } } if (chainedRunner != null) { chainedRunner.run(notifier); } } private Description createTestDescription(TestDescriptor t) { String d = t.getDescription().replaceAll("\n|\r", " "); return Description.createTestDescription(klass, t.getRule().getName() + "::" + t.getNumberInDocument() + " " + d); } @Override public void filter(Filter filter) throws NoTestsRemainException { Iterator<TestDescriptor> it = allTests.iterator(); while (it.hasNext()) { TestDescriptor t = it.next(); Description testDesc = createTestDescription(t); if (filter.shouldRun(testDesc)) { try { filter.apply(t); } catch (NoTestsRemainException e) { it.remove(); } } else { it.remove(); } } boolean chainIsEmpty = false; try { if (chainedRunner != null) { chainedRunner.filter(filter); } else { chainIsEmpty = true; } } catch (NoTestsRemainException e) { chainIsEmpty = true; } if (allTests.isEmpty() && chainIsEmpty) { throw new NoTestsRemainException(); } } }