Skip to content

Commit 97548ef

Browse files
NEW(pmd): @W-17310907@: Update our build to be able to build/test our own PMD rules
1 parent 54f6986 commit 97548ef

File tree

14 files changed

+519
-248
lines changed

14 files changed

+519
-248
lines changed

package-lock.json

Lines changed: 186 additions & 218 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/code-analyzer-pmd-engine/gradle/libs.versions.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ pmd-apex = { module = "net.sourceforge.pmd:pmd-apex", version.ref = "pmd" }
2020
pmd-core = { module = "net.sourceforge.pmd:pmd-core", version.ref = "pmd" }
2121
pmd-html = { module = "net.sourceforge.pmd:pmd-html", version.ref = "pmd" }
2222
pmd-javascript = { module = "net.sourceforge.pmd:pmd-javascript", version.ref = "pmd" }
23+
pmd-test = { module = "net.sourceforge.pmd:pmd-test", version.ref = "pmd" }
2324
pmd-visualforce = { module = "net.sourceforge.pmd:pmd-visualforce", version.ref = "pmd" }
2425
pmd-xml = { module = "net.sourceforge.pmd:pmd-xml", version.ref = "pmd" }
2526
slf4j-nop = { module = "org.slf4j:slf4j-nop", version.ref = "slf4j-nop" }

packages/code-analyzer-pmd-engine/pmd-cpd-wrappers/build.gradle.kts

Lines changed: 20 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import java.awt.Desktop
22

33
plugins {
4-
// Apply the application plugin to add support for building a CLI application in Java.
5-
application
4+
// plugin to build java code
5+
java
66

77
// Code coverage plugin
88
jacoco
@@ -27,7 +27,7 @@ dependencies {
2727

2828
// --- TEST ONLY DEPENDENCIES -----------------------------------------------
2929
testImplementation(libs.hamcrest)
30-
testImplementation(libs.junit.jupiter)
30+
testImplementation(libs.junit.jupiter) // Maps to junit-jupiter
3131
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
3232
}
3333

@@ -36,34 +36,26 @@ java {
3636
targetCompatibility = JavaVersion.VERSION_11
3737
}
3838

39-
application {
40-
mainClass = "com.salesforce.sfca.pmdwrapper.PmdWrapper"
41-
}
42-
43-
4439
// Directories of interest
45-
val pmdCpdWrappersDistDir: String = layout.projectDirectory.dir("../dist/pmd-cpd-wrappers").asFile.path;
40+
val javaLibDir: File = layout.projectDirectory.dir("../dist/java-lib").asFile;
4641
val reportsDir: String = layout.buildDirectory.dir("reports").get().asFile.path
4742

4843

49-
// ======== ASSEMBLE RELATED TASKS =====================================================================================
50-
// During assemble, we don't need to create a zip or a tar, so we disable these
51-
tasks.distZip {
52-
// Disabled distZip from running but doesn't remove it from the task dependency tree (since we can't)
53-
enabled = false
54-
}
55-
tasks.distTar {
56-
// Disabled distTar from running but doesn't remove it from the task dependency tree (since we can't)
57-
enabled = false
44+
// ======== BUILD RELATED TASKS ========================================================================================
45+
46+
// Task to build and copy jar file, giving it the name "sfca-pmd-cpd-wrappers-1.0.0.jar"
47+
tasks.jar {
48+
archiveBaseName.set("sfca-pmd-cpd-wrappers")
49+
archiveVersion.set("1.0.0")
50+
destinationDirectory.set(javaLibDir) // Default location for JAR output
5851
}
59-
// During assemble, we want to run the installDist task (which comes from the distribution plugin which comes from the application plugin)
60-
// instead ... but first we need to modify the location where the jar files should be placed.
61-
tasks.installDist {
62-
into(pmdCpdWrappersDistDir)
63-
includeEmptyDirs = false
52+
// Task to copy the runtime dependencies (and make this task run on build)
53+
tasks.register<Copy>("copyDependencies") {
54+
from(configurations.runtimeClasspath) // This includes all runtime dependencies
55+
into(javaLibDir) // Target directory for dependencies
6456
}
65-
tasks.assemble {
66-
dependsOn(tasks.installDist)
57+
tasks.build {
58+
dependsOn("copyDependencies") // Ensure dependencies are copied during the build process
6759
}
6860

6961

@@ -102,9 +94,9 @@ tasks.register("showCoverageReport") {
10294

10395

10496
// ======== CLEAN RELATED TASKS ========================================================================================
105-
tasks.register<Delete>("deletePmdCpdWrappersFromDist") {
106-
delete(pmdCpdWrappersDistDir)
97+
tasks.register<Delete>("deleteJavaLibDir") {
98+
delete(javaLibDir)
10799
}
108100
tasks.named("clean") {
109-
dependsOn("deletePmdCpdWrappersFromDist")
101+
dependsOn("deleteJavaLibDir")
110102
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import java.awt.Desktop
2+
3+
plugins {
4+
// plugin to build java code
5+
java
6+
7+
// Code coverage plugin
8+
jacoco
9+
10+
// This is a very useful utility for displaying the gradle task dependency tree
11+
// Usage: ./gradlew <task 1>...<task N> taskTree
12+
// Example Usage: ./gradlew build taskTree
13+
id("com.dorongold.task-tree") version "4.0.0"
14+
}
15+
16+
repositories {
17+
mavenCentral()
18+
}
19+
20+
// These dependencies use our version catalog. Versions are listed in the ../gradle/libs.versions.toml file.
21+
// See https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format
22+
dependencies {
23+
// --- APPLICATION DEPENDENCIES ---------------------------------------------
24+
implementation(libs.bundles.pmd7)
25+
26+
// --- TEST ONLY DEPENDENCIES -----------------------------------------------
27+
testImplementation(libs.hamcrest)
28+
testImplementation(libs.junit.jupiter) // Maps to junit-jupiter
29+
testImplementation(libs.pmd.test) // Maps to pmd-test
30+
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
31+
}
32+
33+
java {
34+
sourceCompatibility = JavaVersion.VERSION_11
35+
targetCompatibility = JavaVersion.VERSION_11
36+
}
37+
38+
// Directories of interest
39+
val javaLibDir: File = layout.projectDirectory.dir("../dist/java-lib").asFile;
40+
val reportsDir: String = layout.buildDirectory.dir("reports").get().asFile.path
41+
42+
// ======== BUILD RELATED TASKS ========================================================================================
43+
44+
// Task to build and copy jar file, giving it the name "sfca-pmd-rules-1.0.0.jar"
45+
tasks.jar {
46+
archiveBaseName.set("sfca-pmd-rules")
47+
archiveVersion.set("1.0.0")
48+
destinationDirectory.set(javaLibDir) // Default location for JAR output
49+
}
50+
// Task to copy the runtime dependencies (and make this task run on build)
51+
tasks.register<Copy>("copyDependencies") {
52+
from(configurations.runtimeClasspath) // This includes all runtime dependencies
53+
into(javaLibDir) // Target directory for dependencies
54+
}
55+
tasks.build {
56+
dependsOn("copyDependencies") // Ensure dependencies are copied during the build process
57+
}
58+
59+
60+
// ======== TEST RELATED TASKS =========================================================================================
61+
jacoco {
62+
toolVersion = "0.8.11"
63+
}
64+
tasks.test {
65+
// Use JUnit 5 for unit tests.
66+
useJUnitPlatform()
67+
68+
testLogging {
69+
events("passed", "skipped", "failed")
70+
showStandardStreams = true
71+
exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
72+
}
73+
}
74+
// The jacocoTestReport and jacocoTestCoverageVerification tasks must be run separately from the test task
75+
// Otherwise, running a single test from the IDE will trigger this verification.
76+
tasks.jacocoTestCoverageVerification {
77+
violationRules {
78+
rule {
79+
limit {
80+
minimum = BigDecimal("0.80")
81+
}
82+
}
83+
}
84+
}
85+
tasks.register("showCoverageReport") {
86+
group = "verification"
87+
dependsOn(tasks.jacocoTestReport)
88+
doLast {
89+
Desktop.getDesktop().browse(File("$reportsDir/jacoco/test/html/index.html").toURI())
90+
}
91+
}
92+
93+
94+
// ======== CLEAN RELATED TASKS ========================================================================================
95+
tasks.register<Delete>("deleteJavaLibDir") {
96+
delete(javaLibDir)
97+
}
98+
tasks.named("clean") {
99+
dependsOn("deleteJavaLibDir")
100+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.salesforce.sfca.pmdrules.examples;
2+
3+
import net.sourceforge.pmd.lang.apex.ast.ASTVariableDeclaration;
4+
import net.sourceforge.pmd.lang.apex.rule.AbstractApexRule;
5+
6+
/**
7+
* Implementation of ExampleJavaBasedRule which reports a violation if a variable with name "foo" is found.
8+
*/
9+
public class ExampleJavaBasedRule extends AbstractApexRule {
10+
11+
@Override
12+
public Object visit(ASTVariableDeclaration node, Object data) {
13+
if (node.getImage().equals("foo")) {
14+
asCtx(data).addViolation(node, node.getImage());
15+
}
16+
return data;
17+
}
18+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<ruleset name="Example Rules"
4+
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
5+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
6+
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
7+
8+
<description>Example rules used for internal testing purposes.</description>
9+
10+
11+
<rule name="ExampleJavaBasedRule"
12+
language="apex"
13+
message="A variable called &quot;foo&quot; was found."
14+
class="com.salesforce.sfca.pmdrules.examples.ExampleJavaBasedRule">
15+
<description>
16+
Example Java Based Rule - Detects when a variable is called "foo".
17+
</description>
18+
<priority>3</priority>
19+
<example>
20+
<![CDATA[
21+
public class Example {
22+
public void main() {
23+
String foo = "<-- poor variable name";
24+
System.debug(bar);
25+
}
26+
}
27+
]]>
28+
</example>
29+
</rule>
30+
31+
32+
<rule name="ExampleXPathBasedRule"
33+
language="apex"
34+
message="A variable called &quot;bar&quot; was found."
35+
class="net.sourceforge.pmd.lang.rule.xpath.XPathRule">
36+
<description>
37+
Example XPath Based Rule - Detects when a variable is called "bar".
38+
</description>
39+
<priority>5</priority>
40+
<properties>
41+
<property name="xpath">
42+
<value>
43+
<![CDATA[
44+
//VariableDeclaration[@Image="bar"]
45+
]]>
46+
</value>
47+
</property>
48+
</properties>
49+
<example>
50+
<![CDATA[
51+
public class Example {
52+
public void main() {
53+
String bar = "<-- poor variable name";
54+
System.debug(bar);
55+
}
56+
}
57+
]]>
58+
</example>
59+
</rule>
60+
</ruleset>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package sfca.rulesets.examples;
2+
3+
import net.sourceforge.pmd.test.SimpleAggregatorTst;
4+
5+
public class ExampleJavaBasedRuleTest extends SimpleAggregatorTst {
6+
7+
@Override
8+
protected void setUp() {
9+
// The test data xml file for this rule's test will always be in the resources directory using a naming
10+
// convention based off the package for this test and the rule being tested:
11+
// "resources/<TestPackageName>/xml/<RuleName>.xml".
12+
// In this case "sfca.rulesets.examples" is the package name of this test file. Thus, the associated test data
13+
// xml file for this rule must be found at: "resource/sfca/rulesets/examples/xml/ExampleJavaBasedRule.xml"
14+
addRule("sfca/rulesets/examples.xml", "ExampleJavaBasedRule");
15+
}
16+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package sfca.rulesets.examples;
2+
3+
import net.sourceforge.pmd.test.SimpleAggregatorTst;
4+
5+
public class ExampleXPathBasedRuleTest extends SimpleAggregatorTst {
6+
7+
@Override
8+
protected void setUp() {
9+
// The test data xml file for this rule's test will always be in the resources directory using a naming
10+
// convention based off the package for this test and the rule being tested:
11+
// "resources/<TestPackageName>/xml/<RuleName>.xml".
12+
// In this case "sfca.rulesets.examples" is the package name of this test file. Thus, the associated test data
13+
// xml file for this rule must be found at: "resource/sfca/rulesets/examples/xml/ExampleXPathBasedRule.xml"
14+
addRule("sfca/rulesets/examples.xml", "ExampleXPathBasedRule");
15+
}
16+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<test-data
3+
xmlns="http://pmd.sourceforge.net/rule-tests"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://pmd.sourceforge.net/rule-tests http://pmd.sourceforge.net/rule-tests_1_0_0.xsd">
6+
7+
<test-code>
8+
<description>Positive case - variable foo is used</description>
9+
<expected-problems>1</expected-problems>
10+
<expected-messages>
11+
<message>A variable called "foo" was found.</message>
12+
</expected-messages>
13+
<code>
14+
<![CDATA[
15+
public class Example {
16+
public void example() {
17+
String foo = '<-- poor variable name';
18+
System.debug(foo);
19+
}
20+
}
21+
]]>
22+
</code>
23+
</test-code>
24+
25+
<test-code>
26+
<description>Negative case - variable foo is not used</description>
27+
<expected-problems>0</expected-problems>
28+
<code>
29+
<![CDATA[
30+
public class Example {
31+
public void example() {
32+
String goodName = '<-- good variable name';
33+
System.debug(goodName);
34+
}
35+
}
36+
]]>
37+
</code>
38+
</test-code>
39+
40+
</test-data>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<test-data
3+
xmlns="http://pmd.sourceforge.net/rule-tests"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://pmd.sourceforge.net/rule-tests http://pmd.sourceforge.net/rule-tests_1_0_0.xsd">
6+
7+
<test-code>
8+
<description>Positive case - variable bar is used</description>
9+
<expected-problems>1</expected-problems>
10+
<expected-messages>
11+
<message>A variable called "bar" was found.</message>
12+
</expected-messages>
13+
<code>
14+
<![CDATA[
15+
public class Example {
16+
public void example() {
17+
String bar = '<-- poor variable name';
18+
System.debug(bar);
19+
}
20+
}
21+
]]>
22+
</code>
23+
</test-code>
24+
25+
<test-code>
26+
<description>Negative case - variable bar is not used</description>
27+
<expected-problems>0</expected-problems>
28+
<code>
29+
<![CDATA[
30+
public class Example {
31+
public void example() {
32+
String goodName = '<-- good variable name';
33+
System.debug(goodName);
34+
}
35+
}
36+
]]>
37+
</code>
38+
</test-code>
39+
40+
</test-data>

0 commit comments

Comments
 (0)