Completely reworked the tests

This commit is contained in:
Erik C. Thauvin 2023-04-14 18:26:25 -07:00
parent c9902c88ef
commit 13a6a75613
13 changed files with 665 additions and 97 deletions

View file

@ -49,7 +49,6 @@ public class PmdOperation extends AbstractOperation<PmdOperation> {
/** /**
* The cache location. * The cache location.
*
*/ */
Path cache; Path cache;
/** /**
@ -64,6 +63,10 @@ public class PmdOperation extends AbstractOperation<PmdOperation> {
* The fail on violation toggle. * The fail on violation toggle.
*/ */
boolean failOnViolation; boolean failOnViolation;
/**
* The forced language.
*/
LanguageVersion forcedLanguageVersion;
/** /**
* The path of the ignore file * The path of the ignore file
*/ */
@ -81,9 +84,13 @@ public class PmdOperation extends AbstractOperation<PmdOperation> {
*/ */
URI inputUri; URI inputUri;
/** /**
* The language version. * The default language version(s).
*/ */
LanguageVersion languageVersion; List<LanguageVersion> languageVersions;
/**
* The relative roots paths.
*/
List<Path> relativizeRoots = new ArrayList<>();
/** /**
* The path to the report page. * The path to the report page.
*/ */
@ -168,6 +175,16 @@ public class PmdOperation extends AbstractOperation<PmdOperation> {
return this; return this;
} }
/**
* Add several paths to shorten paths that are output in the report.
*
* @see #addRelativizeRoot(Path...)
*/
public PmdOperation addRelativizeRoot(Path... relativeRoot) {
this.relativizeRoots.addAll(List.of(relativeRoot));
return this;
}
/** /**
* Adds new rule set paths. * Adds new rule set paths.
* <p> * <p>
@ -207,6 +224,14 @@ public class PmdOperation extends AbstractOperation<PmdOperation> {
return this; return this;
} }
/**
* Set the default language to be used for all input files.
*/
public PmdOperation defaultLanguage(LanguageVersion... languageVersion) {
this.languageVersions.addAll(List.of(languageVersion));
return this;
}
/** /**
* <p>Specifies the character set encoding of the source code files. The default is {@code UTF-8}.</p> * <p>Specifies the character set encoding of the source code files. The default is {@code UTF-8}.</p>
* *
@ -243,8 +268,8 @@ public class PmdOperation extends AbstractOperation<PmdOperation> {
/** /**
* Forces a language to be used for all input files, irrespective of file names. * Forces a language to be used for all input files, irrespective of file names.
*/ */
public PmdOperation forceLanguage(LanguageVersion languageVersion) { public PmdOperation forceVersion(LanguageVersion languageVersion) {
this.languageVersion = languageVersion; this.forcedLanguageVersion = languageVersion;
return this; return this;
} }
@ -269,29 +294,41 @@ public class PmdOperation extends AbstractOperation<PmdOperation> {
*/ */
public PMDConfiguration initConfiguration(String commandName) { public PMDConfiguration initConfiguration(String commandName) {
PMDConfiguration config = new PMDConfiguration(); PMDConfiguration config = new PMDConfiguration();
if (cache == null && project != null && incrementalAnalysis) { if (cache == null && project != null && incrementalAnalysis) {
config.setAnalysisCacheLocation( config.setAnalysisCacheLocation(
Paths.get(project.buildDirectory().getPath(), PMD_DIR, PMD_DIR + "-cache").toFile().getAbsolutePath()); Paths.get(project.buildDirectory().getPath(), PMD_DIR, PMD_DIR + "-cache").toFile().getAbsolutePath());
} else if (cache != null) { } else if (cache != null) {
config.setAnalysisCacheLocation(cache.toFile().getAbsolutePath()); config.setAnalysisCacheLocation(cache.toFile().getAbsolutePath());
} }
config.setDebug(debug); config.setDebug(debug);
config.setFailOnViolation(failOnViolation); config.setFailOnViolation(failOnViolation);
if (languageVersion != null) {
config.setForceLanguageVersion(languageVersion); if (languageVersions != null) {
config.setDefaultLanguageVersions(languageVersions);
} }
if (forcedLanguageVersion != null) {
config.setForceLanguageVersion(forcedLanguageVersion);
}
if (ignoreFile != null) { if (ignoreFile != null) {
config.setIgnoreFilePath(ignoreFile); config.setIgnoreFilePath(ignoreFile);
} }
config.setIgnoreIncrementalAnalysis(!incrementalAnalysis); config.setIgnoreIncrementalAnalysis(!incrementalAnalysis);
if (inputPaths.isEmpty()) { if (inputPaths.isEmpty()) {
throw new IllegalArgumentException(commandName + ": InputPaths required."); throw new IllegalArgumentException(commandName + ": InputPaths required.");
} else { } else {
config.setInputPathList(inputPaths); config.setInputPathList(inputPaths);
} }
if (inputUri != null) { if (inputUri != null) {
config.setInputUri(inputUri); config.setInputUri(inputUri);
} }
config.setMinimumPriority(rulePriority); config.setMinimumPriority(rulePriority);
if (project != null) { if (project != null) {
@ -301,6 +338,7 @@ public class PmdOperation extends AbstractOperation<PmdOperation> {
config.setReportFile(reportFile); config.setReportFile(reportFile);
} }
config.addRelativizeRoots(relativizeRoots);
config.setReportFormat(reportFormat); config.setReportFormat(reportFormat);
config.setRuleSets(ruleSets); config.setRuleSets(ruleSets);
config.setShowSuppressedViolations(showSuppressed); config.setShowSuppressedViolations(showSuppressed);
@ -313,6 +351,9 @@ public class PmdOperation extends AbstractOperation<PmdOperation> {
/** /**
* Sets the to source files, or directories containing source files to analyze. * Sets the to source files, or directories containing source files to analyze.
* Previously set paths will be disregarded.
*
* @see #addInputPath(Path...)
*/ */
public PmdOperation inputPaths(Path... inputPath) { public PmdOperation inputPaths(Path... inputPath) {
inputPaths.clear(); inputPaths.clear();
@ -362,6 +403,17 @@ public class PmdOperation extends AbstractOperation<PmdOperation> {
return numErrors; return numErrors;
} }
/**
* Sets several paths to shorten paths that are output in the report. Previous relative paths will be disregarded.
*
* @see #addRelativizeRoot(Path...)
*/
public PmdOperation relativizeRoots(Path... relativeRoot) {
this.relativizeRoots.clear();
this.relativizeRoots.addAll(List.of(relativeRoot));
return this;
}
/** /**
* Sets the output format of the analysis report. The default is {@code text}. * Sets the output format of the analysis report. The default is {@code text}.
*/ */
@ -371,7 +423,7 @@ public class PmdOperation extends AbstractOperation<PmdOperation> {
} }
/** /**
* Sets the rule set path(s). * Sets the rule set path(s), disregarding any previously set paths.
* <p> * <p>
* The built-in rule set paths are: * The built-in rule set paths are:
* <ul> * <ul>
@ -385,6 +437,8 @@ public class PmdOperation extends AbstractOperation<PmdOperation> {
* <li>{@code category/java/performance.xml}</li> * <li>{@code category/java/performance.xml}</li>
* <li>{@code category/java/security.xml}</li> * <li>{@code category/java/security.xml}</li>
* </ul> * </ul>
*
* @see #addRuleSet(String...)
*/ */
public PmdOperation ruleSets(String... ruleSet) { public PmdOperation ruleSets(String... ruleSet) {
ruleSets.clear(); ruleSets.clear();

View file

@ -16,11 +16,15 @@
package rife.bld.extension; package rife.bld.extension;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatCode;
@ -31,113 +35,223 @@ import static org.assertj.core.api.Assertions.assertThatCode;
* @author <a href="https://erik.thauvin.net/">Erik C. Thauvin</a> * @author <a href="https://erik.thauvin.net/">Erik C. Thauvin</a>
* @since 1.0 * @since 1.0
*/ */
public class PmdOperationTest { class PmdOperationTest {
public static final String COMMAND_NAME = "pmd"; static final String CODE_STYLE = "category/java/codestyle.xml";
public static final String TEST = "test"; static final Path CODE_STYLE_SAMPLE = Path.of("src/test/resources/java/CodeStyle.java");
static final PmdOperation pmdOperation = new PmdOperation(); static final int CODING_STYLE_ERRORS = 16;
static final String COMMAND_NAME = "pmd";
static final String ERROR_PRONE = "category/java/errorprone.xml";
static final int ERROR_PRONE_ERRORS = 8;
static final Path ERROR_PRONE_SAMPLE = Path.of("src/test/resources/java/ErrorProne.java");
static final String TEST = "test";
static final String CATEGORY_FOO = "category/foo.xml";
@BeforeEach PmdOperation newPmdOperation() {
void initializeOperation() { final PmdOperation pmdOperation = new PmdOperation();
pmdOperation.inputPaths = List.of(Paths.get("src/main")); pmdOperation.inputPaths(Path.of("src/main"), Path.of("src/test"));
pmdOperation.reportFile = Paths.get("build", COMMAND_NAME, "pmd-test-report.txt"); pmdOperation.reportFile = Paths.get("build", COMMAND_NAME, "pmd-test-report.txt");
pmdOperation.cache = Paths.get("build", COMMAND_NAME, "pmd-cache"); pmdOperation.cache = Paths.get("build", COMMAND_NAME, "pmd-cache");
pmdOperation.failOnViolation = false; pmdOperation.failOnViolation = false;
return pmdOperation;
} }
@Test @Test
void testConfigFile() { void testAddInputPaths() {
var pmd = pmdOperation.ruleSets("src/test/resources/pmd.xml"); var pmd = newPmdOperation().ruleSets(PmdOperation.RULE_SET_DEFAULT, CODE_STYLE).inputPaths(ERROR_PRONE_SAMPLE)
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))) .addInputPath(CODE_STYLE_SAMPLE);
.as("no errors").isEqualTo(0); assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(42);
} }
@Test @Test
void testEmptyRuleSets() { void testCache() {
var pmd = pmdOperation.ruleSets(""); var cache = Path.of("build/pmd/temp-cache");
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))) var pmd = newPmdOperation().ruleSets(CODE_STYLE).inputPaths(CODE_STYLE_SAMPLE).cache(cache);
.as("no errors").isEqualTo(0); assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(CODING_STYLE_ERRORS);
var f = cache.toFile();
assertThat(f.exists()).as("file exits").isTrue();
assertThat(f.delete()).as("delete file").isTrue();
} }
@Test @Test
void testJavaQuickStart() { void testDebug() {
var pmd = pmdOperation.ruleSets("rulesets/java/quickstart.xml"); var pmd = newPmdOperation().ruleSets(CATEGORY_FOO).debug(true);
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))) var config = pmd.initConfiguration(COMMAND_NAME);
.as("no errors").isEqualTo(0); assertThat(config.isDebug()).isTrue();
} }
@Test @Test
void testJavaErrorProne() { void testEncoding() {
var pmd = pmdOperation.ruleSets("category/java/errorprone.xml"); var pmd = newPmdOperation().ruleSets(CATEGORY_FOO).encoding("UTF-16");
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))) var config = pmd.initConfiguration(COMMAND_NAME);
.as("many errors").isGreaterThan(0); assertThat(config.getSourceEncoding()).isEqualTo(StandardCharsets.UTF_16);
}
@Test
void testJavaCodeStyleAndErrorProne() {
var pmd = pmdOperation.addRuleSet("category/java/codestyle.xml", "category/java/errorprone.xml");
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME)))
.as("many errors").isGreaterThan(0);
}
@Test
void testJavaCodeStyle() {
var pmd = pmdOperation.ruleSets("category/java/codestyle.xml");
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME)))
.as("many errors").isGreaterThan(0);
}
@Test
void testJavaDesign() {
var pmd = pmdOperation.ruleSets("category/java/design.xml");
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME)))
.as("many errors").isGreaterThan(0);
}
@Test
void testJavaDocumentation() {
var pmd = pmdOperation.ruleSets("category/java/documentation.xml");
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME)))
.as("many errors").isGreaterThan(0);
}
@Test
void testJavaBestPractices() {
var pmd = pmdOperation.ruleSets("category/java/bestpractices.xml");
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME)))
.as("no errors").isEqualTo(0);
}
@Test
void testJavaMultiThreading() {
var pmd = pmdOperation.ruleSets("category/java/multithreading");
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME)))
.as("no errors").isEqualTo(0);
}
@Test
void testJavaPerformance() {
var pmd = pmdOperation.ruleSets("category/java/performance.xml");
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME)))
.as("no errors").isEqualTo(0);
}
@Test
void testJavaSecurity() {
var pmd = pmdOperation.ruleSets("category/java/security.xml");
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME)))
.as("no errors").isEqualTo(0);
}
@Test
void testPmdOperation() {
assertThat(pmdOperation.performPmdAnalysis(TEST, pmdOperation.initConfiguration(COMMAND_NAME)))
.as("no errors").isEqualTo(0);
} }
@Test @Test
void testFailOnValidation() { void testFailOnValidation() {
assertThatCode(() -> pmdOperation.ruleSets("category/java/documentation.xml").failOnViolation(true) var pmd = newPmdOperation().ruleSets("category/java/documentation.xml")
.performPmdAnalysis(TEST, pmdOperation.initConfiguration(COMMAND_NAME)) .inputPaths(Path.of("src/test/resources/java/Documentation.java"));
assertThatCode(() -> pmd.failOnViolation(true).performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))
).isInstanceOf(RuntimeException.class).hasMessageContaining('[' + TEST + ']'); ).isInstanceOf(RuntimeException.class).hasMessageContaining('[' + TEST + ']');
} }
@Test
void testIgnoreFile() {
var pmd = newPmdOperation().ruleSets(ERROR_PRONE, CODE_STYLE).inputPaths(ERROR_PRONE_SAMPLE, CODE_STYLE_SAMPLE)
.ignoreFile(Path.of("src/test/resources/ignore.txt"));
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(0);
}
@Test
void testIncrementalAnalysis() {
var pmd = newPmdOperation().ruleSets(CATEGORY_FOO).incrementalAnalysis(true);
var config = pmd.initConfiguration(COMMAND_NAME);
assertThat(config.isIgnoreIncrementalAnalysis()).isFalse();
}
@Test
void testJavaBestPractices() {
var pmd = newPmdOperation().ruleSets("category/java/bestpractices.xml")
.inputPaths(Path.of("src/test/resources/java/BestPractices.java"));
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(11);
}
@Test
void testJavaCodeStyle() {
var pmd = newPmdOperation().ruleSets(CODE_STYLE).inputPaths(CODE_STYLE_SAMPLE);
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(CODING_STYLE_ERRORS);
}
@Test
void testJavaCodeStyleAndErrorProne() {
var pmd = newPmdOperation().ruleSets(CODE_STYLE).inputPaths(CODE_STYLE_SAMPLE);
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME)))
.as("code style").isEqualTo(CODING_STYLE_ERRORS);
pmd = pmd.addRuleSet(ERROR_PRONE).addInputPath(ERROR_PRONE_SAMPLE);
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME)))
.as("code style + error prone").isEqualTo(40);
}
@Test
void testJavaDesign() {
var pmd = newPmdOperation().ruleSets("category/java/design.xml")
.inputPaths(Path.of("src/test/resources/java/Design.java"))
.cache(Path.of("build/pmd/design-cache"));
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(4);
}
@Test
void testJavaDocumentation() {
var pmd = newPmdOperation().ruleSets("category/java/documentation.xml")
.inputPaths(Path.of("src/test/resources/java/Documentation.java"));
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(4);
}
@Test
void testJavaErrorProne() {
var pmd = newPmdOperation().ruleSets(ERROR_PRONE).inputPaths(ERROR_PRONE_SAMPLE);
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(ERROR_PRONE_ERRORS);
}
@Test
void testJavaMultiThreading() {
var pmd = newPmdOperation().ruleSets("category/java/multithreading.xml")
.inputPaths(Path.of("src/test/resources/java/MultiThreading.java"));
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(4);
}
@Test
void testJavaPerformance() {
var pmd = newPmdOperation().ruleSets("category/java/performance.xml")
.inputPaths(Path.of("src/test/resources/java/Performance.java"));
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(9);
}
@Test
void testJavaQuickStart() {
var pmd = newPmdOperation().ruleSets("rulesets/java/quickstart.xml")
.inputPaths(Path.of("src/test/resources/java"));
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(35);
}
@Test
void testJavaSecurity() {
var pmd = newPmdOperation().ruleSets("category/java/security.xml")
.inputPaths(Path.of("src/test/resources/java/Security.java"));
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(0);
}
@Test
void testMainOperation() {
var pmd = newPmdOperation().inputPaths(Path.of("src/main"))
.performPmdAnalysis(TEST, newPmdOperation().initConfiguration(COMMAND_NAME));
assertThat(pmd).isEqualTo(0);
}
@Test
void testRelativizeRoots() {
var foo = Path.of("foo/bar");
var bar = Path.of("bar/foo");
var pmd = newPmdOperation().ruleSets(CATEGORY_FOO).relativizeRoots(foo).addRelativizeRoot(bar);
var config = pmd.initConfiguration(COMMAND_NAME);
assertThat(config.getRelativizeRoots()).contains(foo).contains(bar);
}
@Test
void testReportFormat() throws IOException {
var pmd = newPmdOperation().ruleSets(ERROR_PRONE).reportFormat("xml").inputPaths(ERROR_PRONE_SAMPLE);
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(ERROR_PRONE_ERRORS);
try (var br = Files.newBufferedReader(pmd.reportFile)) {
assertThat(br.readLine()).as("xml report").startsWith("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
}
}
@Test
void testRuleSetsConfigFile() {
var pmd = newPmdOperation().ruleSets("src/test/resources/pmd.xml")
.ignoreFile(Path.of("src/test/resources/ignore-all.txt"));
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(0);
}
@Test
void testRuleSetsEmpty() {
var pmd = newPmdOperation().ruleSets("");
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(0);
}
@Test
void testShowSuppressed() {
var pmd = newPmdOperation().ruleSets(ERROR_PRONE).showSuppressed(true);
var config = pmd.initConfiguration(COMMAND_NAME);
assertThat(config.isShowSuppressedViolations()).isTrue();
}
@Test
void testSuppressedMarker() {
var pmd = newPmdOperation().ruleSets(ERROR_PRONE).suppressedMarker(TEST);
var config = pmd.initConfiguration(COMMAND_NAME);
assertThat(config.getSuppressMarker()).isEqualTo(TEST);
}
@Test
void testThreads() {
var pmd = newPmdOperation().ruleSets(ERROR_PRONE).threads(5);
var config = pmd.initConfiguration(COMMAND_NAME);
assertThat(config.getThreads()).isEqualTo(5);
}
@Test
void testUri() throws URISyntaxException {
var uri = new URI("https://example.com");
var pmd = newPmdOperation().ruleSets(ERROR_PRONE).uri(uri);
var config = pmd.initConfiguration(COMMAND_NAME);
assertThat(config.getUri()).isEqualTo(uri);
}
@Test
void testXml() {
var pmd = newPmdOperation().inputPaths(Path.of("src/test/resources/pmd.xml"))
.ruleSets("src/test/resources/xml/basic.xml");
assertThat(pmd.performPmdAnalysis(TEST, pmd.initConfiguration(COMMAND_NAME))).isEqualTo(0);
}
} }

View file

@ -0,0 +1 @@
src/test/resources/java/

View file

@ -0,0 +1,2 @@
src/test/resources/java/ErrorProne.java
src/test/resources/java/CodeStyle.java

View file

@ -0,0 +1,50 @@
/*
* Copyright 2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java;/**
* BestPractices class
*
* @author <a href="https://erik.thauvin.net/">Erik C. Thauvin</a>
* @since 1.0
*/
public class BestPractices {
private final String ip = "127.0.0.1"; // not recommended
private StringBuffer buffer; // potential memory leak as an instance variable;
private String[] x;
void bar(int a) {
switch (a) {
case 1: // do something
break;
default: // the default case should be last, by convention
break;
case 2:
break;
}
}
public void foo(String[] param) {
// Don't do this, make a copy of the array at least
this.x = param;
}
private void greet(String name) {
name = name.trim();
System.out.println("Hello " + name);
}
}

View file

@ -0,0 +1,46 @@
/*
* Copyright 2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java;
/**
* CodeStyle class
*
* @author <a href="https://erik.thauvin.net/">Erik C. Thauvin</a>
* @since 1.0
*/
public final class CodeStyle {
final int FinalField = 1;
private int x;
private int y; // class cannot be subclassed, so is y really private or package visible?
// missing constructor
boolean bar(int x, int y) {
try {
x = y;
} catch (IllegalArgumentException e) {
throw e;
} catch (IllegalStateException e) { // Can be collapsed into the previous block
throw e;
}
if (true) ; // empty if statement
if (true) { // empty as well
}
return x == y;
}
}

View file

@ -0,0 +1,40 @@
/*
* Copyright 2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java;/**
* Design class
*
* @author <a href="https://erik.thauvin.net/">Erik C. Thauvin</a>
* @since 1.0
*/
public class Design {
String field;
int otherField;
void foo() {
throw new NullPointerException();
}
public void bar(int x, int y, int z) {
if (x > y) {
if (y > z) {
if (z == x) {
// !! too deep
}
}
}
}
}

View file

@ -0,0 +1,28 @@
/*
* Copyright 2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java;/**
* @author <a href="https://erik.thauvin.net/">Erik C. Thauvin</a>
* @since 1.0
*/
public class Documentation {
public Documentation() {
// This constructor is intentionally empty. Nothing special is needed here.
}
public void doSomething() {
}
}

View file

@ -0,0 +1,52 @@
/*
* Copyright 2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java;/**
* ErrorProne class
*
* @author <a href="https://erik.thauvin.net/">Erik C. Thauvin</a>
* @since 1.0
*/
public class ErrorProne {
static int x;
public ErrorProne(int y) {
x = y; // unsafe
// unusual use of branching statement in a loop
for (int i = 0; i < 10; i++) {
if (i * i <= 25) {
continue;
}
break;
}
}
void foo() {
try {
// do something
} catch (Throwable th) { // should not catch Throwable
th.printStackTrace();
}
}
void bar() {
try {
// do something
} catch (NullPointerException npe) {
}
}
}

View file

@ -0,0 +1,50 @@
/*
* Copyright 2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java;import java.text.SimpleDateFormat;
/**
* MultiThreading class
*
* @author <a href="https://erik.thauvin.net/">Erik C. Thauvin</a>
* @since 1.0
*/
public class MultiThreading {
private static final SimpleDateFormat sdf = new SimpleDateFormat();
/*volatile */ Object baz = null; // fix for Java5 and later: volatile
private volatile String var1; // not suggested
void bar() {
sdf.format("bar"); // poor, no thread-safety
}
void foo() {
synchronized (sdf) { // preferred
sdf.format("foo");
}
}
Object obj() {
if (baz == null) { // baz may be non-null yet not fully created
synchronized (this) {
if (baz == null) {
baz = new Object();
}
}
}
return baz;
}
}

View file

@ -0,0 +1,55 @@
/*
* Copyright 2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java;/**
* Performance class
*
* @author <a href="https://erik.thauvin.net/">Erik C. Thauvin</a>
* @since 1.0
*/
public class Performance {
public static void main(String[] as) {
for (int i = 0; i < 10; i++) {
Performance p = new Performance(); // Avoid this whenever you can it's really expensive
}
}
private boolean checkTrimEmpty(String str) {
for (int i = 0; i < str.length(); i++) {
if (!Character.isWhitespace(str.charAt(i))) {
return false;
}
}
return true;
}
void foo() {
StringBuffer sb = new StringBuffer();
sb.append("a"); // avoid this
String foo = " ";
StringBuffer buf = new StringBuffer();
buf.append("Hello"); // poor
buf.append(foo);
buf.append("World");
buf.append("Hello").append(" ").append("World");
StringBuffer sbuf = new StringBuffer("tmp = " + System.getProperty("java.io.tmpdir"));
// poor
}
}

View file

@ -0,0 +1,34 @@
/*
* Copyright 2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java;import java.security.SecureRandom;
/**
* Security class
*
* @author <a href="https://erik.thauvin.net/">Erik C. Thauvin</a>
* @since 1.0
*/
public class Security {
void bad() {
byte[] iv = new byte[] { 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, };
}
void alsoBad() {
byte[] iv = "secret iv in here".getBytes();
}
}

View file

@ -0,0 +1,42 @@
<?xml version="1.0"?>
<ruleset name="Basic"
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">
<description>
The Basic ruleset contains a collection of good practices which should be followed.
</description>
<!-- Rules, that have been moved into a category -->
<rule ref="category/java/errorprone.xml/AvoidBranchingStatementAsLastInLoop" deprecated="true" />
<rule ref="category/java/errorprone.xml/AvoidDecimalLiteralsInBigDecimalConstructor" deprecated="true" />
<rule ref="category/java/errorprone.xml/AvoidMultipleUnaryOperators" deprecated="true" />
<rule ref="category/java/errorprone.xml/AvoidUsingOctalValues" deprecated="true" />
<rule ref="category/java/errorprone.xml/BrokenNullCheck" deprecated="true" />
<rule ref="category/java/errorprone.xml/CheckSkipResult" deprecated="true" />
<rule ref="category/java/errorprone.xml/ClassCastExceptionWithToArray" deprecated="true" />
<rule ref="category/java/errorprone.xml/DontUseFloatTypeForLoopIndices" deprecated="true" />
<rule ref="category/java/errorprone.xml/JumbledIncrementer" deprecated="true" />
<rule ref="category/java/errorprone.xml/MisplacedNullCheck" deprecated="true" />
<rule ref="category/java/errorprone.xml/OverrideBothEqualsAndHashcode" deprecated="true" />
<rule ref="category/java/errorprone.xml/ReturnFromFinallyBlock" deprecated="true" />
<rule ref="category/java/errorprone.xml/UnconditionalIfStatement" deprecated="true" />
<rule ref="category/java/multithreading.xml/AvoidThreadGroup" deprecated="true" />
<rule ref="category/java/multithreading.xml/DontCallThreadRun" deprecated="true" />
<rule ref="category/java/multithreading.xml/DoubleCheckedLocking" deprecated="true" />
<rule ref="category/java/bestpractices.xml/AvoidUsingHardCodedIP" deprecated="true" />
<rule ref="category/java/bestpractices.xml/CheckResultSet" deprecated="true" />
<rule ref="category/java/codestyle.xml/ExtendsObject" deprecated="true" />
<rule ref="category/java/codestyle.xml/ForLoopShouldBeWhileLoop" deprecated="true" />
<rule ref="category/java/performance.xml/BigIntegerInstantiation" deprecated="true" />
<!-- <rule ref="category/java/performance.xml/BooleanInstantiation" deprecated="true" />-->
<rule ref="category/java/design.xml/CollapsibleIfStatements" deprecated="true" />
<rule ref="category/java/design.xml/SimplifiedTernary" deprecated="true" />
</ruleset>