Support formats csv and txt for CPD
This commit is contained in:
6 files changed
+303
-23
No files matched your search
@@ -1,2 +1,4 @@
|
||||
invoker.goals = verify
|
||||
invoker.goals.1 = verify
|
||||
invoker.goals.2 = pmd:cpd-check -Dformat=csv
|
||||
invoker.goals.3 = pmd:cpd-check -Dformat=txt
|
||||
invoker.buildResult = failure
|
||||
@@ -14,6 +14,9 @@ String readFile(File file) throws IOException {
|
||||
|
||||
File buildLogPath = new File(basedir, "build.log");
|
||||
String buildLog = readFile(buildLogPath);
|
||||
if (buildLog.contains("An API incompatibility was encountered while")) {
|
||||
throw new RuntimeException("Executing failed due to API incompatibility");
|
||||
}
|
||||
if (!buildLog.contains("[INFO] CPD Failure: Found 8 lines of duplicated code at locations:")) {
|
||||
throw new RuntimeException("No CPD failures detected, did CPD run?");
|
||||
}
|
||||
@@ -23,8 +26,7 @@ if (!buildLog.contains(classA + " line 3")) {
|
||||
}
|
||||
|
||||
File cpdXmlReport = new File(basedir, "target/cpd.xml");
|
||||
if(!cpdXmlReport.exists())
|
||||
{
|
||||
if (!cpdXmlReport.exists()) {
|
||||
throw new FileNotFoundException("Could not find cpd xml report: " + cpdXmlReport);
|
||||
}
|
||||
String cpdXml = readFile(cpdXmlReport);
|
||||
@@ -34,3 +36,27 @@ if (!cpdXml.contains("<duplication lines=\"8\" tokens=\"67\">")) {
|
||||
if (!cpdXml.contains(classA + "\"/>")) {
|
||||
throw new RuntimeException("Expected duplication has not been reported");
|
||||
}
|
||||
|
||||
File csvReport = new File(basedir, "target/cpd.csv");
|
||||
if (!csvReport.exists()) {
|
||||
throw new FileNotFoundException("Could not find cpd csv report: " + csvReport);
|
||||
}
|
||||
String csv = readFile(csvReport);
|
||||
if (!csv.contains("8,67,2,3,")) {
|
||||
throw new RuntimeException("Expected duplication in CSV has not been reported");
|
||||
}
|
||||
if (!csv.contains(classA + ",")) {
|
||||
throw new RuntimeException("Expected duplication in CSV has not been reported");
|
||||
}
|
||||
|
||||
File textReport = new File(basedir, "target/cpd.txt");
|
||||
if (!textReport.exists()) {
|
||||
throw new FileNotFoundException("Could not find cpd text report: " + textReport);
|
||||
}
|
||||
String text = readFile(textReport);
|
||||
if (!text.contains("Found a 8 line (67 tokens) duplication in the following files:")) {
|
||||
throw new RuntimeException("Expected duplication in TXT has not been reported");
|
||||
}
|
||||
if (!text.contains("Starting at line 3 of ") && !text.contains(classA.toString())) {
|
||||
throw new RuntimeException("Expected duplication in TXT has not been reported");
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
// This class has been taken from 7.0.0-SNAPSHOT
|
||||
// Changes: implements old interface CPDRenderer, old render(Iterator<Match> matches, Writer writer) method
|
||||
|
||||
package net.sourceforge.pmd.cpd;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
|
||||
import net.sourceforge.pmd.cpd.renderer.CPDRenderer;
|
||||
import net.sourceforge.pmd.lang.document.FileLocation;
|
||||
import net.sourceforge.pmd.lang.document.TextFile;
|
||||
import net.sourceforge.pmd.lang.java.JavaLanguageModule;
|
||||
|
||||
/**
|
||||
* Renders a report to CSV. The CSV format renders each match (duplication)
|
||||
* as a single line with the following columns:
|
||||
* <ul>
|
||||
* <li>lines (optional): The number of lines the first mark of a match spans.
|
||||
* Only output if the {@code lineCountPerFile} is disabled (see ctor params).</li>
|
||||
* <li>tokens: The number of duplicated tokens in a match (size of the match).</li>
|
||||
* <li>occurrences: The number of duplicates in a match (number of times the tokens were found in distinct places).</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Trailing each line are pairs (or triples, if {@code lineCountPerFile} is enabled)
|
||||
* of fields describing each file where the duplication was found in the format
|
||||
* {@code (start line, line count (optional), file path)}. These repeat at least twice.
|
||||
*
|
||||
* <h3>Examples</h3>
|
||||
* <p>
|
||||
* Example without {@code lineCountPerFile}:
|
||||
* <pre>{@code
|
||||
* lines,tokens,occurrences
|
||||
* 10,75,2,48,/var/file1,73,/var/file2
|
||||
* }</pre>
|
||||
* This describes one match with the following characteristics:
|
||||
* <ul>
|
||||
* <li>The first duplicate instance is 10 lines long;
|
||||
* <li>75 duplicated tokens;
|
||||
* <li>2 duplicate instances;
|
||||
* <li>The first duplicate instance is in file {@code /var/file1} and starts at line 48;</li>
|
||||
* <li>The second duplicate instance is in file {@code /var/file2} and starts at line 73.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Example with {@code lineCountPerFile}:
|
||||
* <pre>{@code
|
||||
* tokens,occurrences
|
||||
* 75,2,48,10,/var/file1,73,12,/var/file2
|
||||
* }</pre>
|
||||
* This describes one match with the following characteristics:
|
||||
* <ul>
|
||||
* <li>75 duplicated tokens
|
||||
* <li>2 duplicate instances
|
||||
* <li>The first duplicate instance is in file {@code /var/file1}, starts at line 48, and is 10 lines long;</li>
|
||||
* <li>The second duplicate instance is in file {@code /var/file2}, starts at line 73, and is 12 lines long.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class CSVRenderer implements CPDReportRenderer, CPDRenderer {
|
||||
|
||||
private final char separator;
|
||||
private final boolean lineCountPerFile;
|
||||
|
||||
public static final char DEFAULT_SEPARATOR = ',';
|
||||
public static final boolean DEFAULT_LINECOUNTPERFILE = false;
|
||||
|
||||
public CSVRenderer() {
|
||||
this(DEFAULT_SEPARATOR, DEFAULT_LINECOUNTPERFILE);
|
||||
}
|
||||
|
||||
public CSVRenderer(boolean lineCountPerFile) {
|
||||
this(DEFAULT_SEPARATOR, lineCountPerFile);
|
||||
}
|
||||
|
||||
public CSVRenderer(char separatorChar) {
|
||||
this(separatorChar, DEFAULT_LINECOUNTPERFILE);
|
||||
}
|
||||
|
||||
public CSVRenderer(char separatorChar, boolean lineCountPerFile) {
|
||||
this.separator = separatorChar;
|
||||
this.lineCountPerFile = lineCountPerFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(CPDReport report, Writer writer) throws IOException {
|
||||
if (!lineCountPerFile) {
|
||||
writer.append("lines").append(separator);
|
||||
}
|
||||
writer.append("tokens").append(separator).append("occurrences").append(System.lineSeparator());
|
||||
|
||||
for (Match match : report.getMatches()) {
|
||||
if (!lineCountPerFile) {
|
||||
writer.append(String.valueOf(match.getLineCount())).append(separator);
|
||||
}
|
||||
writer.append(String.valueOf(match.getTokenCount())).append(separator)
|
||||
.append(String.valueOf(match.getMarkCount())).append(separator);
|
||||
for (Iterator<Mark> marks = match.iterator(); marks.hasNext();) {
|
||||
Mark mark = marks.next();
|
||||
FileLocation loc = mark.getLocation();
|
||||
|
||||
writer.append(String.valueOf(loc.getStartLine())).append(separator);
|
||||
if (lineCountPerFile) {
|
||||
writer.append(String.valueOf(loc.getLineCount())).append(separator);
|
||||
}
|
||||
writer.append(StringEscapeUtils.escapeCsv(report.getDisplayName(loc.getFileId())));
|
||||
if (marks.hasNext()) {
|
||||
writer.append(separator);
|
||||
}
|
||||
}
|
||||
writer.append(System.lineSeparator());
|
||||
}
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
// ------------------- compat extensions --------------------
|
||||
@Override
|
||||
public void render(Iterator<Match> matches, Writer writer) throws IOException {
|
||||
RendererHelper.render(matches, writer, this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.cpd;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import net.sourceforge.pmd.cpd.renderer.CPDRenderer;
|
||||
import net.sourceforge.pmd.lang.document.TextFile;
|
||||
import net.sourceforge.pmd.lang.java.JavaLanguageModule;
|
||||
|
||||
final class RendererHelper {
|
||||
private RendererHelper() {
|
||||
// utility class
|
||||
}
|
||||
|
||||
static void render(Iterator<Match> matches, Writer writer, CPDReportRenderer renderer) throws IOException {
|
||||
List<Match> matchesList = new ArrayList<>();
|
||||
matches.forEachRemaining(matchesList::add);
|
||||
|
||||
List<TextFile> textFiles = new ArrayList<>();
|
||||
Set<String> paths = new HashSet<>();
|
||||
for (Match match : matchesList) {
|
||||
for (Mark mark : match.getMarkSet()) {
|
||||
paths.add(mark.getFilename());
|
||||
}
|
||||
}
|
||||
for (String path : paths) {
|
||||
textFiles.add(TextFile.forPath(Paths.get(path), StandardCharsets.UTF_8, JavaLanguageModule.getInstance().getDefaultVersion()));
|
||||
}
|
||||
|
||||
try (SourceManager sourceManager = new SourceManager(textFiles)) {
|
||||
CPDReport report = new CPDReport(sourceManager, matchesList, Collections.emptyMap());
|
||||
renderer.render(report, writer);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
// This class has been taken from 7.0.0-SNAPSHOT
|
||||
// Changes: implements old interface CPDRenderer, old render(Iterator<Match> matches, Writer writer) method
|
||||
|
||||
package net.sourceforge.pmd.cpd;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.Iterator;
|
||||
|
||||
import net.sourceforge.pmd.cpd.renderer.CPDRenderer;
|
||||
import net.sourceforge.pmd.lang.document.Chars;
|
||||
import net.sourceforge.pmd.lang.document.FileLocation;
|
||||
import net.sourceforge.pmd.util.StringUtil;
|
||||
|
||||
public class SimpleRenderer implements CPDReportRenderer, CPDRenderer {
|
||||
|
||||
private String separator;
|
||||
private boolean trimLeadingWhitespace;
|
||||
|
||||
public static final String DEFAULT_SEPARATOR = "=====================================================================";
|
||||
|
||||
public SimpleRenderer() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
public SimpleRenderer(boolean trimLeadingWhitespace) {
|
||||
this(DEFAULT_SEPARATOR);
|
||||
this.trimLeadingWhitespace = trimLeadingWhitespace;
|
||||
}
|
||||
|
||||
public SimpleRenderer(String theSeparator) {
|
||||
separator = theSeparator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(CPDReport report, Writer writer0) throws IOException {
|
||||
PrintWriter writer = new PrintWriter(writer0);
|
||||
Iterator<Match> matches = report.getMatches().iterator();
|
||||
if (matches.hasNext()) {
|
||||
renderOn(report, writer, matches.next());
|
||||
}
|
||||
|
||||
while (matches.hasNext()) {
|
||||
Match match = matches.next();
|
||||
writer.println(separator);
|
||||
renderOn(report, writer, match);
|
||||
}
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
private void renderOn(CPDReport report, PrintWriter writer, Match match) throws IOException {
|
||||
|
||||
writer.append("Found a ").append(String.valueOf(match.getLineCount())).append(" line (").append(String.valueOf(match.getTokenCount()))
|
||||
.append(" tokens) duplication in the following files: ").println();
|
||||
|
||||
for (Mark mark : match) {
|
||||
FileLocation loc = mark.getLocation();
|
||||
writer.append("Starting at line ")
|
||||
.append(String.valueOf(loc.getStartLine()))
|
||||
.append(" of ").append(report.getDisplayName(loc.getFileId()))
|
||||
.println();
|
||||
}
|
||||
|
||||
writer.println(); // add a line to separate the source from the desc above
|
||||
|
||||
Chars source = report.getSourceCodeSlice(match.getFirstMark());
|
||||
|
||||
if (trimLeadingWhitespace) {
|
||||
for (Chars line : StringUtil.linesWithTrimIndent(source)) {
|
||||
line.writeFully(writer);
|
||||
writer.println();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
source.writeFully(writer);
|
||||
writer.println();
|
||||
}
|
||||
|
||||
// ------------------- compat extensions --------------------
|
||||
@Override
|
||||
public void render(Iterator<Match> matches, Writer writer) throws IOException {
|
||||
RendererHelper.render(matches, writer, this);
|
||||
}
|
||||
}
|
||||
@@ -169,25 +169,6 @@ public final class XMLRenderer implements CPDReportRenderer, CPDRenderer {
|
||||
// ------------------- compat extensions --------------------
|
||||
@Override
|
||||
public void render(Iterator<Match> matches, Writer writer) throws IOException {
|
||||
List<Match> matchesList = new ArrayList<>();
|
||||
matches.forEachRemaining(matchesList::add);
|
||||
|
||||
List<TextFile> textFiles = new ArrayList<>();
|
||||
Set<String> paths = new HashSet<>();
|
||||
for (Match match : matchesList) {
|
||||
for (Mark mark : match.getMarkSet()) {
|
||||
paths.add(mark.getFilename());
|
||||
}
|
||||
}
|
||||
for (String path : paths) {
|
||||
textFiles.add(TextFile.forPath(Paths.get(path), StandardCharsets.UTF_8, JavaLanguageModule.getInstance().getDefaultVersion()));
|
||||
}
|
||||
|
||||
try (SourceManager sourcManager = new SourceManager(textFiles)) {
|
||||
CPDReport report = new CPDReport(sourcManager, matchesList, Collections.emptyMap());
|
||||
render(report, writer);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
RendererHelper.render(matches, writer, this);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user