Add utility to build a report, make report immutable
This commit is contained in:
10 files changed
+98
-75
No files matched your search
@@ -12,6 +12,7 @@ import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import net.sourceforge.pmd.renderers.AbstractAccumulatingRenderer;
|
||||
import net.sourceforge.pmd.reporting.FileAnalysisListener;
|
||||
@@ -23,6 +24,10 @@ import net.sourceforge.pmd.util.datasource.DataSource;
|
||||
* A {@link Report} collects all informations during a PMD execution. This
|
||||
* includes violations, suppressed violations, metrics, error during processing
|
||||
* and configuration errors.
|
||||
*
|
||||
* <p>A report may be created by a {@link GlobalReportBuilderListener} that you
|
||||
* use as the {@link GlobalAnalysisListener} in {@linkplain PMD#processFiles(PMDConfiguration, List, List, GlobalAnalysisListener) PMD's entry point}.
|
||||
* You can also create one manually with {@link #buildReport(Consumer)}.
|
||||
*/
|
||||
public final class Report {
|
||||
// todo move to package reporting
|
||||
@@ -32,9 +37,8 @@ public final class Report {
|
||||
private final List<ProcessingError> errors = synchronizedList(new ArrayList<>());
|
||||
private final List<ConfigurationError> configErrors = synchronizedList(new ArrayList<>());
|
||||
|
||||
/** Package-private, now you must use a listener to build a report. */
|
||||
Report() {
|
||||
|
||||
// package-private, you have to use a listener to build a report.
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,7 +166,7 @@ public final class Report {
|
||||
*
|
||||
* @param violation the violation to add
|
||||
*/
|
||||
public void addRuleViolation(RuleViolation violation) {
|
||||
private void addRuleViolation(RuleViolation violation) {
|
||||
synchronized (violations) {
|
||||
int index = Collections.binarySearch(violations, violation, RuleViolation.DEFAULT_COMPARATOR);
|
||||
violations.add(index < 0 ? -index - 1 : index, violation);
|
||||
@@ -172,7 +176,7 @@ public final class Report {
|
||||
/**
|
||||
* Adds a new suppressed violation.
|
||||
*/
|
||||
public void addSuppressedViolation(SuppressedViolation sv) {
|
||||
private void addSuppressedViolation(SuppressedViolation sv) {
|
||||
suppressedRuleViolations.add(sv);
|
||||
}
|
||||
|
||||
@@ -181,7 +185,7 @@ public final class Report {
|
||||
*
|
||||
* @param error the error to add
|
||||
*/
|
||||
public void addConfigError(ConfigurationError error) {
|
||||
private void addConfigError(ConfigurationError error) {
|
||||
configErrors.add(error);
|
||||
}
|
||||
|
||||
@@ -191,7 +195,7 @@ public final class Report {
|
||||
* @param error
|
||||
* the error to add
|
||||
*/
|
||||
public void addError(ProcessingError error) {
|
||||
private void addError(ProcessingError error) {
|
||||
errors.add(error);
|
||||
}
|
||||
|
||||
@@ -206,7 +210,10 @@ public final class Report {
|
||||
* @param r the report to be merged into this.
|
||||
*
|
||||
* @see AbstractAccumulatingRenderer
|
||||
*
|
||||
* @deprecated Convert Renderer to use the reports.
|
||||
*/
|
||||
@Deprecated
|
||||
public void merge(Report r) {
|
||||
errors.addAll(r.errors);
|
||||
configErrors.addAll(r.configErrors);
|
||||
@@ -221,7 +228,7 @@ public final class Report {
|
||||
/**
|
||||
* Returns an unmodifiable list of violations that were suppressed.
|
||||
*/
|
||||
public final List<SuppressedViolation> getSuppressedViolations() {
|
||||
public List<SuppressedViolation> getSuppressedViolations() {
|
||||
return Collections.unmodifiableList(suppressedRuleViolations);
|
||||
}
|
||||
|
||||
@@ -231,7 +238,7 @@ public final class Report {
|
||||
*
|
||||
* <p>The violations list is sorted with {@link RuleViolation#DEFAULT_COMPARATOR}.
|
||||
*/
|
||||
public final List<RuleViolation> getViolations() {
|
||||
public List<RuleViolation> getViolations() {
|
||||
return Collections.unmodifiableList(violations);
|
||||
}
|
||||
|
||||
@@ -240,7 +247,7 @@ public final class Report {
|
||||
* Returns an unmodifiable list of processing errors that have been
|
||||
* recorded until now.
|
||||
*/
|
||||
public final List<ProcessingError> getProcessingErrors() {
|
||||
public List<ProcessingError> getProcessingErrors() {
|
||||
return Collections.unmodifiableList(errors);
|
||||
}
|
||||
|
||||
@@ -249,10 +256,17 @@ public final class Report {
|
||||
* Returns an unmodifiable list of configuration errors that have
|
||||
* been recorded until now.
|
||||
*/
|
||||
public final List<ConfigurationError> getConfigurationErrors() {
|
||||
public List<ConfigurationError> getConfigurationErrors() {
|
||||
return Collections.unmodifiableList(configErrors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a report by making side effects on a {@link FileAnalysisListener}.
|
||||
* This wraps a {@link ReportBuilderListener}.
|
||||
*/
|
||||
public static Report buildReport(Consumer<? super FileAnalysisListener> lambda) {
|
||||
return BaseResultProducingCloseable.using(new ReportBuilderListener(), lambda);
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link FileAnalysisListener} that accumulates events into a
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
package net.sourceforge.pmd.util;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Base class for an autocloseable that produce a result once it has
|
||||
* been closed.
|
||||
@@ -43,4 +45,14 @@ public abstract class BaseResultProducingCloseable<T> implements AutoCloseable {
|
||||
public void close() {
|
||||
closed = true;
|
||||
}
|
||||
|
||||
|
||||
public static <U, C extends BaseResultProducingCloseable<U>> U using(C closeable, Consumer<? super C> it) {
|
||||
try {
|
||||
it.accept(closeable);
|
||||
} finally {
|
||||
closeable.close();
|
||||
}
|
||||
return closeable.getResult();
|
||||
}
|
||||
}
|
||||
@@ -25,41 +25,42 @@ public class ReportTest {
|
||||
// Files are grouped together now.
|
||||
@Test
|
||||
public void testSortedReportFile() throws IOException {
|
||||
Report r = new Report();
|
||||
Node s = getNode(10, 5).withFileName("foo");
|
||||
Rule rule1 = new MockRule("name", "desc", "msg", "rulesetname");
|
||||
r.addRuleViolation(new ParametricRuleViolation<>(rule1, s, rule1.getMessage()));
|
||||
Node s1 = getNode(10, 5).withFileName("bar");
|
||||
Rule rule2 = new MockRule("name", "desc", "msg", "rulesetname");
|
||||
r.addRuleViolation(new ParametricRuleViolation<>(rule2, s1, rule2.getMessage()));
|
||||
Renderer rend = new XMLRenderer();
|
||||
String result = render(rend, r);
|
||||
String result = render(rend, Report.buildReport(r -> {
|
||||
Node s = getNode(10, 5).withFileName("foo");
|
||||
Rule rule1 = new MockRule("name", "desc", "msg", "rulesetname");
|
||||
r.onRuleViolation(new ParametricRuleViolation<>(rule1, s, rule1.getMessage()));
|
||||
Node s1 = getNode(10, 5).withFileName("bar");
|
||||
Rule rule2 = new MockRule("name", "desc", "msg", "rulesetname");
|
||||
r.onRuleViolation(new ParametricRuleViolation<>(rule2, s1, rule2.getMessage()));
|
||||
}));
|
||||
assertTrue("sort order wrong", result.indexOf("bar") < result.indexOf("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSortedReportLine() throws IOException {
|
||||
Report r = new Report();
|
||||
Node node1 = getNode(20, 5).withFileName("foo1"); // line 20: after rule2 violation
|
||||
Rule rule1 = new MockRule("rule1", "rule1", "msg", "rulesetname");
|
||||
r.addRuleViolation(new ParametricRuleViolation<>(rule1, node1, rule1.getMessage()));
|
||||
|
||||
Node node2 = getNode(10, 5).withFileName("foo1"); // line 10: before rule1 violation
|
||||
Rule rule2 = new MockRule("rule2", "rule2", "msg", "rulesetname");
|
||||
r.addRuleViolation(new ParametricRuleViolation<>(rule2, node2, rule2.getMessage())); // same file!!
|
||||
Renderer rend = new XMLRenderer();
|
||||
String result = render(rend, r);
|
||||
String result = render(rend, Report.buildReport(r -> {
|
||||
Node node1 = getNode(20, 5).withFileName("foo1"); // line 20: after rule2 violation
|
||||
Rule rule1 = new MockRule("rule1", "rule1", "msg", "rulesetname");
|
||||
r.onRuleViolation(new ParametricRuleViolation<>(rule1, node1, rule1.getMessage()));
|
||||
|
||||
Node node2 = getNode(10, 5).withFileName("foo1"); // line 10: before rule1 violation
|
||||
Rule rule2 = new MockRule("rule2", "rule2", "msg", "rulesetname");
|
||||
r.onRuleViolation(new ParametricRuleViolation<>(rule2, node2, rule2.getMessage())); // same file!!
|
||||
}));
|
||||
assertTrue("sort order wrong", result.indexOf("rule2") < result.indexOf("rule1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIterator() {
|
||||
Report r = new Report();
|
||||
Rule rule = new MockRule("name", "desc", "msg", "rulesetname");
|
||||
Node node1 = getNode(5, 5, true);
|
||||
r.addRuleViolation(new ParametricRuleViolation<>(rule, node1, rule.getMessage()));
|
||||
Node node2 = getNode(5, 6, true);
|
||||
r.addRuleViolation(new ParametricRuleViolation<>(rule, node2, rule.getMessage()));
|
||||
Report r = Report.buildReport(it -> {
|
||||
it.onRuleViolation(new ParametricRuleViolation<>(rule, node1, rule.getMessage()));
|
||||
it.onRuleViolation(new ParametricRuleViolation<>(rule, node2, rule.getMessage()));
|
||||
});
|
||||
|
||||
assertEquals(2, r.getViolations().size());
|
||||
}
|
||||
@@ -74,7 +75,7 @@ public class ReportTest {
|
||||
}
|
||||
|
||||
private static Node getNode(int line, int column, boolean nextLine) {
|
||||
DummyNode s = (DummyNode) getNode(line, column);
|
||||
DummyNode s = getNode(line, column);
|
||||
if (nextLine) {
|
||||
s.setCoords(line + 1, column + 4, line + 4, 1);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import org.junit.Test;
|
||||
import net.sourceforge.pmd.FooRule;
|
||||
import net.sourceforge.pmd.Report;
|
||||
import net.sourceforge.pmd.Report.ConfigurationError;
|
||||
import net.sourceforge.pmd.Report.GlobalReportBuilderListener;
|
||||
import net.sourceforge.pmd.Report.ProcessingError;
|
||||
import net.sourceforge.pmd.ReportTest;
|
||||
import net.sourceforge.pmd.RulePriority;
|
||||
@@ -25,6 +26,7 @@ import net.sourceforge.pmd.lang.ast.DummyNode;
|
||||
import net.sourceforge.pmd.lang.ast.DummyRoot;
|
||||
import net.sourceforge.pmd.lang.ast.Node;
|
||||
import net.sourceforge.pmd.lang.rule.ParametricRuleViolation;
|
||||
import net.sourceforge.pmd.util.BaseResultProducingCloseable;
|
||||
|
||||
public abstract class AbstractRendererTest {
|
||||
|
||||
@@ -66,20 +68,18 @@ public abstract class AbstractRendererTest {
|
||||
}
|
||||
|
||||
protected Report reportOneViolation() {
|
||||
Report report = new Report();
|
||||
report.addRuleViolation(newRuleViolation(1));
|
||||
return report;
|
||||
return Report.buildReport(it -> it.onRuleViolation(newRuleViolation(1)));
|
||||
}
|
||||
|
||||
private Report reportTwoViolations() {
|
||||
Report report = new Report();
|
||||
RuleViolation informationalRuleViolation = newRuleViolation(1);
|
||||
informationalRuleViolation.getRule().setPriority(RulePriority.LOW);
|
||||
report.addRuleViolation(informationalRuleViolation);
|
||||
RuleViolation severeRuleViolation = newRuleViolation(2);
|
||||
severeRuleViolation.getRule().setPriority(RulePriority.HIGH);
|
||||
report.addRuleViolation(severeRuleViolation);
|
||||
return report;
|
||||
return Report.buildReport(it -> {
|
||||
RuleViolation informationalRuleViolation = newRuleViolation(1);
|
||||
informationalRuleViolation.getRule().setPriority(RulePriority.LOW);
|
||||
it.onRuleViolation(informationalRuleViolation);
|
||||
RuleViolation severeRuleViolation = newRuleViolation(2);
|
||||
severeRuleViolation.getRule().setPriority(RulePriority.HIGH);
|
||||
it.onRuleViolation(severeRuleViolation);
|
||||
});
|
||||
}
|
||||
|
||||
protected RuleViolation newRuleViolation(int endColumn) {
|
||||
@@ -113,11 +113,10 @@ public abstract class AbstractRendererTest {
|
||||
@Test
|
||||
public void testRuleWithProperties() throws Exception {
|
||||
DummyNode node = createNode(1);
|
||||
Report report = new Report();
|
||||
RuleWithProperties theRule = new RuleWithProperties();
|
||||
theRule.setProperty(RuleWithProperties.STRING_PROPERTY_DESCRIPTOR,
|
||||
"the string value\nsecond line with \"quotes\"");
|
||||
report.addRuleViolation(new ParametricRuleViolation<Node>(theRule, node, "blah"));
|
||||
Report report = Report.buildReport(it -> it.onRuleViolation(new ParametricRuleViolation<Node>(theRule, node, "blah")));
|
||||
String rendered = ReportTest.render(getRenderer(), report);
|
||||
assertEquals(filter(getExpectedWithProperties()), filter(rendered));
|
||||
}
|
||||
@@ -131,7 +130,7 @@ public abstract class AbstractRendererTest {
|
||||
|
||||
@Test
|
||||
public void testRendererEmpty() throws Exception {
|
||||
Report rep = new Report();
|
||||
Report rep = Report.buildReport(it -> {});
|
||||
String actual = ReportTest.render(getRenderer(), rep);
|
||||
assertEquals(filter(getExpectedEmpty()), filter(actual));
|
||||
}
|
||||
@@ -145,27 +144,24 @@ public abstract class AbstractRendererTest {
|
||||
|
||||
@Test
|
||||
public void testError() throws Exception {
|
||||
Report rep = new Report();
|
||||
Report.ProcessingError err = new Report.ProcessingError(new RuntimeException("Error"), "file");
|
||||
rep.addError(err);
|
||||
Report rep = Report.buildReport(it -> it.onError(err));
|
||||
String actual = ReportTest.render(getRenderer(), rep);
|
||||
assertEquals(filter(getExpectedError(err)), filter(actual));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testErrorWithoutMessage() throws Exception {
|
||||
Report rep = new Report();
|
||||
Report.ProcessingError err = new Report.ProcessingError(new NullPointerException(), "file");
|
||||
rep.addError(err);
|
||||
Report rep = Report.buildReport(it -> it.onError(err));
|
||||
String actual = ReportTest.render(getRenderer(), rep);
|
||||
assertEquals(filter(getExpectedErrorWithoutMessage(err)), filter(actual));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConfigError() throws Exception {
|
||||
Report rep = new Report();
|
||||
Report.ConfigurationError err = new Report.ConfigurationError(new FooRule(), "a configuration error");
|
||||
rep.addConfigError(err);
|
||||
Report rep = BaseResultProducingCloseable.using(new GlobalReportBuilderListener(), it -> it.onConfigError(err));
|
||||
String actual = ReportTest.render(getRenderer(), rep);
|
||||
assertEquals(filter(getExpectedError(err)), filter(actual));
|
||||
}
|
||||
|
||||
@@ -89,14 +89,13 @@ public class CodeClimateRendererTest extends AbstractRendererTest {
|
||||
@Test
|
||||
public void testXPathRule() throws Exception {
|
||||
DummyNode node = createNode(1);
|
||||
Report report = new Report();
|
||||
XPathRule theRule = new XPathRule(XPathVersion.XPATH_3_1, "//dummyNode");
|
||||
|
||||
// Setup as FooRule
|
||||
theRule.setDescription("desc");
|
||||
theRule.setName("Foo");
|
||||
|
||||
report.addRuleViolation(new ParametricRuleViolation<Node>(theRule, node, "blah"));
|
||||
Report report = Report.buildReport(it -> it.onRuleViolation(new ParametricRuleViolation<Node>(theRule, node, "blah")));
|
||||
String rendered = ReportTest.render(getRenderer(), report);
|
||||
|
||||
// Output should be the exact same as for non xpath rules
|
||||
|
||||
@@ -79,10 +79,12 @@ public class JsonRendererTest extends AbstractRendererTest {
|
||||
|
||||
@Test
|
||||
public void suppressedViolations() throws IOException {
|
||||
Report rep = new Report();
|
||||
SuppressedViolation suppressed = new SuppressedViolation(newRuleViolation(1),
|
||||
ViolationSuppressor.NOPMD_COMMENT_SUPPRESSOR, "test");
|
||||
rep.addSuppressedViolation(suppressed);
|
||||
SuppressedViolation suppressed = new SuppressedViolation(
|
||||
newRuleViolation(1),
|
||||
ViolationSuppressor.NOPMD_COMMENT_SUPPRESSOR,
|
||||
"test"
|
||||
);
|
||||
Report rep = Report.buildReport(it -> it.onSuppressedRuleViolation(suppressed));
|
||||
String actual = ReportTest.render(getRenderer(), rep);
|
||||
String expected = readFile("expected-suppressed.json");
|
||||
Assert.assertEquals(filter(expected), filter(actual));
|
||||
|
||||
@@ -84,14 +84,14 @@ public class SarifRendererTest extends AbstractRendererTest {
|
||||
}
|
||||
|
||||
private Report reportTwoViolations() {
|
||||
Report report = new Report();
|
||||
RuleViolation informationalRuleViolation = newRuleViolation(1, "Foo");
|
||||
informationalRuleViolation.getRule().setPriority(RulePriority.LOW);
|
||||
report.addRuleViolation(informationalRuleViolation);
|
||||
RuleViolation severeRuleViolation = newRuleViolation(2, "Boo");
|
||||
severeRuleViolation.getRule().setPriority(RulePriority.HIGH);
|
||||
report.addRuleViolation(severeRuleViolation);
|
||||
return report;
|
||||
return Report.buildReport(reportBuilder -> {
|
||||
RuleViolation informationalRuleViolation = newRuleViolation(1, "Foo");
|
||||
informationalRuleViolation.getRule().setPriority(RulePriority.LOW);
|
||||
reportBuilder.onRuleViolation(informationalRuleViolation);
|
||||
RuleViolation severeRuleViolation = newRuleViolation(2, "Boo");
|
||||
severeRuleViolation.getRule().setPriority(RulePriority.HIGH);
|
||||
reportBuilder.onRuleViolation(severeRuleViolation);
|
||||
});
|
||||
}
|
||||
|
||||
protected String readFile(String relativePath) {
|
||||
|
||||
@@ -98,10 +98,9 @@ public class XMLRendererTest extends AbstractRendererTest {
|
||||
|
||||
private void verifyXmlEscaping(Renderer renderer, String shouldContain, Charset charset) throws Exception {
|
||||
renderer.setProperty(XMLRenderer.ENCODING, charset.name());
|
||||
Report report = new Report();
|
||||
String surrogatePair = "\ud801\udc1c";
|
||||
String msg = "The String 'literal' \"TokénizĀr " + surrogatePair + "\" appears...";
|
||||
report.addRuleViolation(createRuleViolation(msg));
|
||||
Report report = Report.buildReport(it -> it.onRuleViolation(createRuleViolation(msg)));
|
||||
String actual = renderTempFile(renderer, report, charset);
|
||||
Assert.assertTrue(actual.contains(shouldContain));
|
||||
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
||||
@@ -143,14 +142,13 @@ public class XMLRendererTest extends AbstractRendererTest {
|
||||
|
||||
Renderer renderer = getRenderer();
|
||||
|
||||
Report report = new Report();
|
||||
String formFeed = "\u000C";
|
||||
// é = U+00E9 : can be represented in ISO-8859-1 as is
|
||||
// Ā = U+0100 : cannot be represented in ISO-8859-1 -> would be a unmappable character, needs to be escaped
|
||||
String specialChars = "éĀ";
|
||||
String originalChars = formFeed + specialChars; // u000C should be removed, é should be encoded correctly as UTF-8
|
||||
String msg = "The String literal \"" + originalChars + "\" appears...";
|
||||
report.addRuleViolation(createRuleViolation(msg));
|
||||
Report report = Report.buildReport(it -> it.onRuleViolation(createRuleViolation(msg)));
|
||||
String actual = renderTempFile(renderer, report, StandardCharsets.UTF_8);
|
||||
Assert.assertTrue(actual.contains(specialChars));
|
||||
Assert.assertFalse(actual.contains(formFeed));
|
||||
|
||||
@@ -21,11 +21,10 @@ public class XSLTRendererTest {
|
||||
@Test
|
||||
public void testDefaultStylesheet() throws Exception {
|
||||
XSLTRenderer renderer = new XSLTRenderer();
|
||||
Report report = new Report();
|
||||
DummyNode node = new DummyRoot().withFileName("file");
|
||||
node.setCoords(1, 1, 1, 2);
|
||||
RuleViolation rv = new ParametricRuleViolation<Node>(new FooRule(), node, "violation message");
|
||||
report.addRuleViolation(rv);
|
||||
Report report = Report.buildReport(it -> it.onRuleViolation(rv));
|
||||
String result = ReportTest.render(renderer, report);
|
||||
Assert.assertTrue(result.contains("violation message"));
|
||||
}
|
||||
|
||||
@@ -60,10 +60,12 @@ public class YAHTMLRendererTest extends AbstractRendererTest {
|
||||
|
||||
@Test
|
||||
public void testReportMultipleViolations() throws Exception {
|
||||
Report report = new Report();
|
||||
report.addRuleViolation(newRuleViolation(1, "net.sf.pmd.test", "YAHTMLSampleClass1"));
|
||||
report.addRuleViolation(newRuleViolation(2, "net.sf.pmd.test", "YAHTMLSampleClass1"));
|
||||
report.addRuleViolation(newRuleViolation(1, "net.sf.pmd.other", "YAHTMLSampleClass2"));
|
||||
Report report = Report.buildReport(it -> {
|
||||
it.onRuleViolation(newRuleViolation(1, "net.sf.pmd.test", "YAHTMLSampleClass1"));
|
||||
it.onRuleViolation(newRuleViolation(2, "net.sf.pmd.test", "YAHTMLSampleClass1"));
|
||||
it.onRuleViolation(newRuleViolation(1, "net.sf.pmd.other", "YAHTMLSampleClass2"));
|
||||
});
|
||||
|
||||
String actual = ReportTest.render(getRenderer(), report);
|
||||
assertEquals(filter(getExpected()), filter(actual));
|
||||
|
||||
|
||||
Reference in new issue
Block a user