Added failOnError (PMD 7.3.0) support

This commit is contained in:
Erik C. Thauvin 2024-06-28 17:47:54 -07:00
parent 03fd552702
commit efce6b9729
Signed by: erik
GPG key ID: 776702A6A2DA330E
6 changed files with 70 additions and 21 deletions

View file

@ -32,7 +32,7 @@ public class PmdOperationBuild extends Project {
public PmdOperationBuild() { public PmdOperationBuild() {
pkg = "rife.bld.extension"; pkg = "rife.bld.extension";
name = "bld-pmd"; name = "bld-pmd";
version = version(1, 1, 1); version = version(1, 1, 2);
javaRelease = 17; javaRelease = 17;
@ -40,7 +40,7 @@ public class PmdOperationBuild extends Project {
autoDownloadPurge = true; autoDownloadPurge = true;
repositories = List.of(MAVEN_CENTRAL, RIFE2_RELEASES); repositories = List.of(MAVEN_CENTRAL, RIFE2_RELEASES);
var pmd = version(7, 2, 0); var pmd = version(7, 3, 0);
scope(compile) scope(compile)
.include(dependency("com.uwyn.rife2", "bld", version(1, 9, 1))) .include(dependency("com.uwyn.rife2", "bld", version(1, 9, 1)))
.include(dependency("net.sourceforge.pmd", "pmd-java", pmd)); .include(dependency("net.sourceforge.pmd", "pmd-java", pmd));

View file

@ -69,6 +69,10 @@ public class PmdOperation extends AbstractOperation<PmdOperation> {
* The encoding. * The encoding.
*/ */
private Charset encoding_ = StandardCharsets.UTF_8; private Charset encoding_ = StandardCharsets.UTF_8;
/**
* The fail on error toggle.
*/
private boolean failOnError_ = true;
/** /**
* The fail on violation toggle. * The fail on violation toggle.
*/ */
@ -296,7 +300,28 @@ public class PmdOperation extends AbstractOperation<PmdOperation> {
} }
/** /**
* Sets whether the build will continue on warnings. * Sets whether the build will exit on recoverable errors.
* <p>
* Default is: {@code true}
* <p>
* Note: If only violations are found, see {@link #failOnViolation(boolean) failOnViolation}
*
* @param failOnError whether to exit and fail the build if recoverable errors occurred
* @return this operation
* @see #failOnViolation(boolean)
*/
public PmdOperation failOnError(boolean failOnError) {
failOnError_ = failOnError;
return this;
}
/**
* Sets whether the build will continue on violations.
* <p>
* Note: If additionally recoverable errors occurred, see {@link #failOnError(boolean) failOnError}
*
* @param failOnViolation whether to exit and fail the build if violations are found
* @return this operation
*/ */
public PmdOperation failOnViolation(boolean failOnViolation) { public PmdOperation failOnViolation(boolean failOnViolation) {
failOnViolation_ = failOnViolation; failOnViolation_ = failOnViolation;
@ -411,6 +436,7 @@ public class PmdOperation extends AbstractOperation<PmdOperation> {
config.setAnalysisCacheLocation(cache_.toFile().getAbsolutePath()); config.setAnalysisCacheLocation(cache_.toFile().getAbsolutePath());
} }
config.setFailOnError(failOnError_);
config.setFailOnViolation(failOnViolation_); config.setFailOnViolation(failOnViolation_);
if (languageVersions_ != null) { if (languageVersions_ != null) {
@ -574,8 +600,8 @@ public class PmdOperation extends AbstractOperation<PmdOperation> {
* *
* @param commandName the command name * @param commandName the command name
* @param config the configuration * @param config the configuration
* @return the number of errors * @return the number of violations
* @throws RuntimeException if an error occurs * @throws ExitStatusException if an error occurs
*/ */
@SuppressWarnings({"PMD.CloseResource", "PMD.AvoidInstantiatingObjectsInLoops"}) @SuppressWarnings({"PMD.CloseResource", "PMD.AvoidInstantiatingObjectsInLoops"})
public int performPmdAnalysis(String commandName, PMDConfiguration config) throws ExitStatusException { public int performPmdAnalysis(String commandName, PMDConfiguration config) throws ExitStatusException {
@ -585,8 +611,9 @@ public class PmdOperation extends AbstractOperation<PmdOperation> {
LOGGER.log(Level.INFO, "[{0}] inputPaths{1}", new Object[]{commandName, inputPaths_}); LOGGER.log(Level.INFO, "[{0}] inputPaths{1}", new Object[]{commandName, inputPaths_});
LOGGER.log(Level.INFO, "[{0}] ruleSets{1}", new Object[]{commandName, ruleSets_}); LOGGER.log(Level.INFO, "[{0}] ruleSets{1}", new Object[]{commandName, ruleSets_});
} }
var numErrors = report.getViolations().size();
if (numErrors > 0) { var numViolations = report.getViolations().size();
if (numViolations > 0) {
for (var v : report.getViolations()) { for (var v : report.getViolations()) {
if (LOGGER.isLoggable(Level.WARNING) && !silent()) { if (LOGGER.isLoggable(Level.WARNING) && !silent()) {
final String msg; final String msg;
@ -608,7 +635,7 @@ public class PmdOperation extends AbstractOperation<PmdOperation> {
} }
var violations = String.format( var violations = String.format(
"[%s] %d rule violations were found. See the report at: %s", commandName, numErrors, "[%s] %d rule violations were found. See the report at: %s", commandName, numViolations,
config.getReportFilePath().toUri()); config.getReportFilePath().toUri());
if (config.isFailOnViolation()) { if (config.isFailOnViolation()) {
if (LOGGER.isLoggable(Level.SEVERE) && !silent()) { if (LOGGER.isLoggable(Level.SEVERE) && !silent()) {
@ -618,6 +645,8 @@ public class PmdOperation extends AbstractOperation<PmdOperation> {
} else if (LOGGER.isLoggable(Level.WARNING) && !silent()) { } else if (LOGGER.isLoggable(Level.WARNING) && !silent()) {
LOGGER.warning(violations); LOGGER.warning(violations);
} }
} else if (pmd.getReporter().numErrors() > 0 && failOnError_) {
throw new ExitStatusException(ExitStatusException.EXIT_FAILURE);
} else { } else {
var rules = pmd.getRulesets(); var rules = pmd.getRulesets();
if (!rules.isEmpty()) { if (!rules.isEmpty()) {
@ -630,7 +659,7 @@ public class PmdOperation extends AbstractOperation<PmdOperation> {
} }
} }
} }
return numErrors; return numViolations;
} }
/** /**

View file

@ -142,12 +142,6 @@ class PmdOperationTest {
assertThat(config.getSourceEncoding()).as("ISO_8859").isEqualTo(StandardCharsets.ISO_8859_1); assertThat(config.getSourceEncoding()).as("ISO_8859").isEqualTo(StandardCharsets.ISO_8859_1);
} }
@Test
void testExecuteNoProject() {
var pmd = new PmdOperation();
assertThatCode(pmd::execute).isInstanceOf(ExitStatusException.class);
}
@Test @Test
void testExecute() throws ExitStatusException { void testExecute() throws ExitStatusException {
var pmd = new PmdOperation().fromProject(new BaseProject()); var pmd = new PmdOperation().fromProject(new BaseProject());
@ -163,12 +157,30 @@ class PmdOperationTest {
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(0); assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(0);
} }
@Test
void testExecuteNoProject() {
var pmd = new PmdOperation();
assertThatCode(pmd::execute).isInstanceOf(ExitStatusException.class);
}
@Test
void testFailOnError() {
var pmd = newPmdOperation().ruleSets("src/test/resources/xml/old.xml")
.inputPaths(Path.of("src/test/resources/java/Documentation.java"));
assertThatCode(() -> pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME)))
.isInstanceOf(ExitStatusException.class);
assertThatCode(() -> pmd.failOnError(false).performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME)))
.doesNotThrowAnyException();
}
@Test @Test
void testFailOnValidation() { void testFailOnValidation() {
var pmd = newPmdOperation().ruleSets(DOCUMENTATION_XML) var pmd = newPmdOperation().ruleSets(DOCUMENTATION_XML)
.inputPaths(Path.of("src/test/resources/java/Documentation.java")); .inputPaths(Path.of("src/test/resources/java/Documentation.java"));
assertThatCode(() -> pmd.failOnViolation(true).performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))) assertThatCode(() -> pmd.failOnViolation(true).performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME)))
.isInstanceOf(ExitStatusException.class); .isInstanceOf(ExitStatusException.class);
assertThatCode(() -> pmd.failOnViolation(false).performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME)))
.doesNotThrowAnyException();
} }
@Test @Test
@ -369,7 +381,7 @@ class PmdOperationTest {
@Test @Test
void testRuleSetsEmpty() throws ExitStatusException { void testRuleSetsEmpty() throws ExitStatusException {
var pmd = newPmdOperation().ruleSets(""); var pmd = newPmdOperation().ruleSets("").failOnError(false);
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(0); assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(0);
} }

View file

@ -1,7 +1,7 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<ruleset name="test" <ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
name="test"
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0" xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd"> xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
<description>Erik's Ruleset</description> <description>Erik's Ruleset</description>
<!-- BEST PRACTICES --> <!-- BEST PRACTICES -->

View file

@ -1,8 +1,8 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<ruleset name="Basic" <ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
name="Basic"
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0" xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd"> xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
<description> <description>
The Basic ruleset contains a collection of good practices which should be followed. The Basic ruleset contains a collection of good practices which should be followed.

View file

@ -0,0 +1,8 @@
<?xml version="1.0"?>
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
name="old"
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
<description>Deprecated (old) rule</description>
<rule ref="category/java/performance.xml/UnnecessaryWrapperObjectCreation"/>
</ruleset>