1
2
3
4 package net.sourceforge.pmd.testframework;
5
6 import static org.junit.Assert.assertEquals;
7 import static org.junit.Assert.fail;
8
9 import java.io.IOException;
10 import java.io.InputStream;
11 import java.io.StringReader;
12 import java.util.Map;
13 import java.util.Properties;
14
15 import javax.xml.parsers.DocumentBuilder;
16 import javax.xml.parsers.DocumentBuilderFactory;
17 import javax.xml.parsers.FactoryConfigurationError;
18 import javax.xml.parsers.ParserConfigurationException;
19
20 import net.sourceforge.pmd.PMD;
21 import net.sourceforge.pmd.PMDException;
22 import net.sourceforge.pmd.PropertyDescriptor;
23 import net.sourceforge.pmd.Report;
24 import net.sourceforge.pmd.Rule;
25 import net.sourceforge.pmd.RuleContext;
26 import net.sourceforge.pmd.RuleSet;
27 import net.sourceforge.pmd.RuleSetFactory;
28 import net.sourceforge.pmd.RuleSetNotFoundException;
29 import net.sourceforge.pmd.RuleSets;
30 import net.sourceforge.pmd.lang.Language;
31 import net.sourceforge.pmd.lang.LanguageVersion;
32
33 import org.w3c.dom.Document;
34 import org.w3c.dom.Element;
35 import org.w3c.dom.Node;
36 import org.w3c.dom.NodeList;
37 import org.xml.sax.SAXException;
38
39
40
41 public abstract class RuleTst {
42 public static final LanguageVersion DEFAULT_LANGUAGE_VERSION = LanguageVersion.JAVA_15;
43 public static final Language DEFAULT_LANGUAGE = DEFAULT_LANGUAGE_VERSION.getLanguage();
44
45
46
47
48 public Rule findRule(String ruleSet, String ruleName) {
49 try {
50 Rule rule = new RuleSetFactory().createRuleSets(ruleSet).getRuleByName(ruleName);
51 if (rule == null) {
52 fail("Rule " + ruleName + " not found in ruleset " + ruleSet);
53 }
54 rule.setRuleSetName(ruleSet);
55 return rule;
56 } catch (RuleSetNotFoundException e) {
57 e.printStackTrace();
58 fail("Couldn't find ruleset " + ruleSet);
59 return null;
60 }
61 }
62
63
64
65
66
67 @SuppressWarnings("unchecked")
68 public void runTest(TestDescriptor test) {
69 Rule rule = test.getRule();
70
71 if (test.getReinitializeRule()) {
72 rule = findRule(rule.getRuleSetName(), rule.getName());
73 }
74
75 Map<PropertyDescriptor<?>, Object> oldProperties = rule.getPropertiesByPropertyDescriptor();
76 try {
77 int res;
78 try {
79
80 if (test.getProperties() != null) {
81 for (Map.Entry<Object, Object> entry : test.getProperties().entrySet()) {
82 String propertyName = (String)entry.getKey();
83 String strValue = (String)entry.getValue();
84 PropertyDescriptor propertyDescriptor = rule.getPropertyDescriptor(propertyName);
85 if (propertyDescriptor == null) {
86 throw new IllegalArgumentException("No such property '" + propertyName + "' on Rule " + rule.getName());
87 }
88 Object value = propertyDescriptor.valueFrom(strValue);
89 rule.setProperty(propertyDescriptor, value);
90 }
91 }
92
93 res = processUsingStringReader(test.getCode(), rule, test.getLanguageVersion()).size();
94 } catch (Throwable t) {
95 t.printStackTrace();
96 throw new RuntimeException('"' + test.getDescription() + "\" failed", t);
97 }
98 assertEquals('"' + test.getDescription() + "\" resulted in wrong number of failures,",
99 test.getNumberOfProblemsExpected(), res);
100 } finally {
101
102
103
104 for (Map.Entry entry: oldProperties.entrySet()) {
105 rule.setProperty((PropertyDescriptor)entry.getKey(), entry.getValue());
106 }
107 }
108 }
109
110 private Report processUsingStringReader(String code, Rule rule,
111 LanguageVersion languageVersion) throws PMDException {
112 Report report = new Report();
113 runTestFromString(code, rule, report, languageVersion);
114 return report;
115 }
116
117
118
119
120 public void runTestFromString(String code, Rule rule, Report report, LanguageVersion languageVersion) throws PMDException {
121 PMD p = new PMD();
122 p.getConfiguration().setDefaultLanguageVersion(languageVersion);
123 RuleContext ctx = new RuleContext();
124 ctx.setReport(report);
125 ctx.setSourceCodeFilename("n/a");
126 ctx.setLanguageVersion(languageVersion);
127 RuleSet rules = new RuleSet();
128 rules.addRule(rule);
129 p.getSourceCodeProcessor().processSourceCode(new StringReader(code), new RuleSets(rules), ctx);
130 }
131
132
133
134
135
136 protected String getCleanRuleName(Rule rule) {
137 String fullClassName = rule.getClass().getName();
138 if (fullClassName.equals(rule.getName())) {
139
140 String packageName = rule.getClass().getPackage().getName();
141 return fullClassName.substring(packageName.length()+1);
142 } else {
143 return rule.getName();
144 }
145 }
146
147
148
149
150
151
152 public TestDescriptor[] extractTestsFromXml(Rule rule) {
153 String testsFileName = getCleanRuleName(rule);
154
155 return extractTestsFromXml(rule, testsFileName);
156 }
157
158 public TestDescriptor[] extractTestsFromXml(Rule rule, String testsFileName) {
159 return extractTestsFromXml(rule, testsFileName, "xml/");
160 }
161
162
163
164
165
166 public TestDescriptor[] extractTestsFromXml(Rule rule, String testsFileName, String baseDirectory) {
167 String testXmlFileName = baseDirectory + testsFileName + ".xml";
168 InputStream inputStream = getClass().getResourceAsStream(testXmlFileName);
169 if (inputStream == null) {
170 throw new RuntimeException("Couldn't find " + testXmlFileName);
171 }
172
173 Document doc;
174 try {
175 DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
176 doc = builder.parse(inputStream);
177 } catch (ParserConfigurationException pce) {
178 pce.printStackTrace();
179 throw new RuntimeException("Couldn't parse " + testXmlFileName + ", due to: " + pce.getMessage());
180 } catch (FactoryConfigurationError fce) {
181 fce.printStackTrace();
182 throw new RuntimeException("Couldn't parse " + testXmlFileName + ", due to: " + fce.getMessage());
183 } catch (IOException ioe) {
184 ioe.printStackTrace();
185 throw new RuntimeException("Couldn't parse " + testXmlFileName + ", due to: " + ioe.getMessage());
186 } catch (SAXException se) {
187 se.printStackTrace();
188 throw new RuntimeException("Couldn't parse " + testXmlFileName + ", due to: " + se.getMessage());
189 }
190
191 return parseTests(rule, doc);
192 }
193
194 private TestDescriptor[] parseTests(Rule rule, Document doc) {
195 Element root = doc.getDocumentElement();
196 NodeList testCodes = root.getElementsByTagName("test-code");
197
198 TestDescriptor[] tests = new TestDescriptor[testCodes.getLength()];
199 for (int i = 0; i < testCodes.getLength(); i++) {
200 Element testCode = (Element)testCodes.item(i);
201
202 boolean reinitializeRule = true;
203 Node reinitializeRuleAttribute = testCode.getAttributes().getNamedItem("reinitializeRule");
204 if (reinitializeRuleAttribute != null) {
205 String reinitializeRuleValue = reinitializeRuleAttribute.getNodeValue();
206 if ("false".equalsIgnoreCase(reinitializeRuleValue) ||
207 "0".equalsIgnoreCase(reinitializeRuleValue)) {
208 reinitializeRule = false;
209 }
210 }
211
212 boolean isRegressionTest = true;
213 Node regressionTestAttribute = testCode.getAttributes().getNamedItem("regressionTest");
214 if (regressionTestAttribute != null) {
215 String reinitializeRuleValue = regressionTestAttribute.getNodeValue();
216 if ("false".equalsIgnoreCase(reinitializeRuleValue)) {
217 isRegressionTest = false;
218 }
219 }
220
221 NodeList ruleProperties = testCode.getElementsByTagName("rule-property");
222 Properties properties = new Properties();
223 for (int j = 0; j < ruleProperties.getLength(); j++) {
224 Node ruleProperty = ruleProperties.item(j);
225 String propertyName = ruleProperty.getAttributes().getNamedItem("name").getNodeValue();
226 properties.setProperty(propertyName, parseTextNode(ruleProperty));
227 }
228 int expectedProblems = Integer.parseInt(getNodeValue(testCode, "expected-problems", true));
229 String description = getNodeValue(testCode, "description", true);
230 String code = getNodeValue(testCode, "code", false);
231 if (code == null) {
232
233 NodeList coderefs = testCode.getElementsByTagName("code-ref");
234 if (coderefs.getLength()==0) {
235 throw new RuntimeException("Required tag is missing from the test-xml. Supply either a code or a code-ref tag");
236 }
237 Node coderef = coderefs.item(0);
238 String referenceId = coderef.getAttributes().getNamedItem("id").getNodeValue();
239 NodeList codeFragments = root.getElementsByTagName("code-fragment");
240 for (int j = 0; j < codeFragments.getLength(); j++) {
241 String fragmentId = codeFragments.item(j).getAttributes().getNamedItem("id").getNodeValue();
242 if (referenceId.equals(fragmentId)) {
243 code = parseTextNode(codeFragments.item(j));
244 }
245 }
246
247 if (code==null) {
248 throw new RuntimeException("No matching code fragment found for coderef");
249 }
250 }
251
252 String languageVersionString = getNodeValue(testCode, "source-type", false);
253 if (languageVersionString == null) {
254 tests[i] = new TestDescriptor(code, description, expectedProblems, rule);
255 } else {
256 LanguageVersion languageVersion = LanguageVersion.findByTerseName(languageVersionString);
257 if (languageVersion != null) {
258 tests[i] = new TestDescriptor(code, description, expectedProblems, rule, languageVersion);
259 } else {
260 throw new RuntimeException("Unknown LanguageVersion for test: " + languageVersionString);
261 }
262 }
263 tests[i].setReinitializeRule(reinitializeRule);
264 tests[i].setRegressionTest(isRegressionTest);
265 tests[i].setProperties(properties);
266 }
267 return tests;
268 }
269
270 private String getNodeValue(Element parentElm, String nodeName, boolean required) {
271 NodeList nodes = parentElm.getElementsByTagName(nodeName);
272 if (nodes == null || nodes.getLength() == 0) {
273 if (required) {
274 throw new RuntimeException("Required tag is missing from the test-xml: " + nodeName);
275 } else {
276 return null;
277 }
278 }
279 Node node = nodes.item(0);
280 return parseTextNode(node);
281 }
282
283 private static String parseTextNode(Node exampleNode) {
284 StringBuffer buffer = new StringBuffer();
285 for (int i = 0; i < exampleNode.getChildNodes().getLength(); i++) {
286 Node node = exampleNode.getChildNodes().item(i);
287 if (node.getNodeType() == Node.CDATA_SECTION_NODE
288 || node.getNodeType() == Node.TEXT_NODE) {
289 buffer.append(node.getNodeValue());
290 }
291 }
292 return buffer.toString().trim();
293 }
294
295
296
297
298
299 public void runTestFromString(String code, Rule rule, Report report) throws PMDException {
300 runTestFromString(code, rule, report, DEFAULT_LANGUAGE_VERSION);
301 }
302 }