diff --git a/pmd-test/pom.xml b/pmd-test/pom.xml
index 8e1af3a5da..7484282065 100644
--- a/pmd-test/pom.xml
+++ b/pmd-test/pom.xml
@@ -27,5 +27,12 @@
org.apache.ant
ant-testutil
+
+
+ org.mockito
+ mockito-core
+ 1.10.19
+ test
+
diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/test/lang/DummyLanguageModule.java b/pmd-test/src/main/java/net/sourceforge/pmd/test/lang/DummyLanguageModule.java
new file mode 100644
index 0000000000..3a0fcaa8dc
--- /dev/null
+++ b/pmd-test/src/main/java/net/sourceforge/pmd/test/lang/DummyLanguageModule.java
@@ -0,0 +1,119 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+package net.sourceforge.pmd.test.lang;
+
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import net.sourceforge.pmd.Rule;
+import net.sourceforge.pmd.RuleContext;
+import net.sourceforge.pmd.RuleViolation;
+import net.sourceforge.pmd.lang.AbstractLanguageVersionHandler;
+import net.sourceforge.pmd.lang.AbstractParser;
+import net.sourceforge.pmd.lang.BaseLanguageModule;
+import net.sourceforge.pmd.lang.Parser;
+import net.sourceforge.pmd.lang.ParserOptions;
+import net.sourceforge.pmd.lang.TokenManager;
+import net.sourceforge.pmd.lang.ast.Node;
+import net.sourceforge.pmd.lang.ast.ParseException;
+import net.sourceforge.pmd.lang.rule.AbstractRuleChainVisitor;
+import net.sourceforge.pmd.lang.rule.AbstractRuleViolationFactory;
+import net.sourceforge.pmd.lang.rule.ParametricRuleViolation;
+import net.sourceforge.pmd.test.lang.ast.DummyNode;
+
+
+/**
+ * Dummy language used for testing PMD.
+ */
+public class DummyLanguageModule extends BaseLanguageModule {
+
+ public static final String NAME = "Dummy";
+ public static final String TERSE_NAME = "dummy";
+
+ public DummyLanguageModule() {
+ super(NAME, null, TERSE_NAME, DummyRuleChainVisitor.class, "dummy");
+ addVersion("1.0", new Handler(), false);
+ addVersion("1.1", new Handler(), false);
+ addVersion("1.2", new Handler(), false);
+ addVersion("1.3", new Handler(), false);
+ addVersion("1.4", new Handler(), false);
+ addVersion("1.5", new Handler(), false);
+ addVersion("1.6", new Handler(), false);
+ addVersion("1.7", new Handler(), true);
+ addVersion("1.8", new Handler(), false);
+ }
+
+ public static class DummyRuleChainVisitor extends AbstractRuleChainVisitor {
+ @Override
+ protected void visit(Rule rule, Node node, RuleContext ctx) {
+ rule.apply(Arrays.asList(node), ctx);
+ }
+ @Override
+ protected void indexNodes(List nodes, RuleContext ctx) {
+ for (Node n : nodes) {
+ indexNode(n);
+ List childs = new ArrayList();
+ for (int i = 0; i < n.jjtGetNumChildren(); i++) {
+ childs.add(n.jjtGetChild(i));
+ }
+ indexNodes(childs, ctx);
+ }
+ }
+ }
+
+ public static class Handler extends AbstractLanguageVersionHandler {
+ @Override
+ public RuleViolationFactory getRuleViolationFactory() {
+ return new RuleViolationFactory();
+ }
+
+ @Override
+ public Parser getParser(ParserOptions parserOptions) {
+ return new AbstractParser(parserOptions) {
+ @Override
+ public Node parse(String fileName, Reader source) throws ParseException {
+ DummyNode node = new DummyNode(1);
+ node.testingOnly__setBeginLine(1);
+ node.testingOnly__setBeginColumn(1);
+ node.setImage("Foo");
+ return node;
+ }
+ @Override
+ public Map getSuppressMap() {
+ return Collections.emptyMap();
+ }
+ @Override
+ public boolean canParse() {
+ return true;
+ }
+ @Override
+ protected TokenManager createTokenManager(Reader source) {
+ return null;
+ }
+ };
+ }
+ }
+
+ public static class RuleViolationFactory extends AbstractRuleViolationFactory {
+ @Override
+ protected RuleViolation createRuleViolation(Rule rule, RuleContext ruleContext, Node node, String message) {
+ return createRuleViolation(rule, ruleContext, node, message, 0, 0);
+ }
+ @Override
+ protected RuleViolation createRuleViolation(Rule rule, RuleContext ruleContext, Node node, String message,
+ int beginLine, int endLine) {
+ ParametricRuleViolation rv = new ParametricRuleViolation(rule, ruleContext, node, message) {
+ {
+ this.packageName = "foo"; // just for testing variable expansion
+ }
+ };
+ rv.setLines(beginLine, endLine);
+ return rv;
+ }
+ }
+}
diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/test/lang/ast/DummyNode.java b/pmd-test/src/main/java/net/sourceforge/pmd/test/lang/ast/DummyNode.java
new file mode 100644
index 0000000000..5760c799c6
--- /dev/null
+++ b/pmd-test/src/main/java/net/sourceforge/pmd/test/lang/ast/DummyNode.java
@@ -0,0 +1,17 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+package net.sourceforge.pmd.test.lang.ast;
+
+import net.sourceforge.pmd.lang.ast.AbstractNode;
+
+public class DummyNode extends AbstractNode {
+ public DummyNode(int id) {
+ super(id);
+ }
+
+ @Override
+ public String toString() {
+ return "dummyNode";
+ }
+}
diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java
index 5a83052746..b3a066cfb2 100644
--- a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java
+++ b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java
@@ -216,7 +216,9 @@ public abstract class RuleTst {
ctx.setIgnoreExceptions(false);
RuleSet rules = new RuleSet();
rules.addRule(rule);
+ rules.start(ctx);
p.getSourceCodeProcessor().processSourceCode(new StringReader(code), new RuleSets(rules), ctx);
+ rules.end(ctx);
} catch (Exception e) {
throw new RuntimeException(e);
}
diff --git a/pmd-test/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language b/pmd-test/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language
new file mode 100644
index 0000000000..825247a1cc
--- /dev/null
+++ b/pmd-test/src/main/resources/META-INF/services/net.sourceforge.pmd.lang.Language
@@ -0,0 +1 @@
+net.sourceforge.pmd.test.lang.DummyLanguageModule
diff --git a/pmd-test/src/main/resources/rulesets/dummy/basic.xml b/pmd-test/src/main/resources/rulesets/dummy/basic.xml
new file mode 100644
index 0000000000..0a141832a2
--- /dev/null
+++ b/pmd-test/src/main/resources/rulesets/dummy/basic.xml
@@ -0,0 +1,36 @@
+
+
+
+
+ Ruleset used by test RuleSetReferenceIdTest
+
+
+
+
+Just for test
+
+ 3
+
+
+
+
+
+
+
+
+ Test
+ 3
+
+
+
+
+
+
+ "
+
\ No newline at end of file
diff --git a/pmd-test/src/test/java/net/sourceforge/pmd/testframework/RuleTstTest.java b/pmd-test/src/test/java/net/sourceforge/pmd/testframework/RuleTstTest.java
new file mode 100644
index 0000000000..0b28df31fa
--- /dev/null
+++ b/pmd-test/src/test/java/net/sourceforge/pmd/testframework/RuleTstTest.java
@@ -0,0 +1,47 @@
+/**
+ * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
+ */
+package net.sourceforge.pmd.testframework;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyList;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import org.junit.Test;
+
+import net.sourceforge.pmd.Report;
+import net.sourceforge.pmd.Rule;
+import net.sourceforge.pmd.RuleContext;
+import net.sourceforge.pmd.lang.LanguageRegistry;
+import net.sourceforge.pmd.lang.LanguageVersion;
+
+public class RuleTstTest {
+
+ @Test
+ public void shouldCallStartAndEnd() {
+ RuleTst ruleTester = new RuleTst() {};
+ LanguageVersion languageVersion = LanguageRegistry.findLanguageByTerseName("dummy").getDefaultVersion();
+ Report report = new Report();
+ Rule rule = mock(Rule.class);
+ when(rule.getLanguage()).thenReturn(languageVersion.getLanguage());
+ when(rule.getName()).thenReturn("test rule");
+
+ ruleTester.runTestFromString("the code", rule, report, languageVersion, false);
+
+ verify(rule).start(any(RuleContext.class));
+ verify(rule).end(any(RuleContext.class));
+ verify(rule, times(4)).getLanguage();
+ verify(rule).usesDFA();
+ verify(rule).usesTypeResolution();
+ verify(rule, times(2)).usesRuleChain();
+ verify(rule).getMinimumLanguageVersion();
+ verify(rule).getMaximumLanguageVersion();
+ verify(rule).apply(anyList(), any(RuleContext.class));
+ verify(rule).getName();
+ verifyNoMoreInteractions(rule);
+ }
+}
diff --git a/src/site/markdown/overview/changelog.md b/src/site/markdown/overview/changelog.md
index 1af211c370..0a049dab29 100644
--- a/src/site/markdown/overview/changelog.md
+++ b/src/site/markdown/overview/changelog.md
@@ -19,6 +19,7 @@
* [#1501](https://sourceforge.net/p/pmd/bugs/1501/): \[java] \[apex] CyclomaticComplexity rule causes OOM when class reporting is disabled
* General
* [#1499](https://sourceforge.net/p/pmd/bugs/1499/): \[core] CPD test break PMD 5.5.1 build on Windows
+ * [#1506](https://sourceforge.net/p/pmd/bugs/1506/): \[core] When runing any RuleTst, start/end methods not called
* [#1508](https://sourceforge.net/p/pmd/bugs/1508/): \[core] \[java] PMD is leaking file handles
**API Changes:**