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