S19: consume versioned engineering resources and move landscape documentation
All checks were successful
Build, test and publish / verify (push) Successful in 8m54s
All checks were successful
Build, test and publish / verify (push) Successful in 8m54s
This commit is contained in:
372
generated/tooling/standards/src/test/java/StandardsTest.java
Normal file
372
generated/tooling/standards/src/test/java/StandardsTest.java
Normal file
@@ -0,0 +1,372 @@
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
import net.sourceforge.pmd.*;
|
||||
import net.sourceforge.pmd.lang.*;
|
||||
import net.sourceforge.pmd.lang.document.FileId;
|
||||
import net.sourceforge.pmd.lang.java.ast.*;
|
||||
import net.sourceforge.pmd.lang.rule.RuleSet;
|
||||
import org.junit.jupiter.api.*;
|
||||
|
||||
class StandardsTest {
|
||||
private List<String> analyze(String text) throws Exception {
|
||||
Standards.root = Path.of("").toAbsolutePath();
|
||||
Standards.module = "backend";
|
||||
Standards.ownedTypes.clear();
|
||||
Standards.findings.clear();
|
||||
Standards.units.clear();
|
||||
Standards.types.clear();
|
||||
Standards.codeLines.clear();
|
||||
final var config = new PMDConfiguration();
|
||||
config.setThreads(1);
|
||||
config.prependAuxClasspath(
|
||||
Path.of(System.getProperty("java.home"), "lib/jrt-fs.jar").toString());
|
||||
config.prependAuxClasspath(
|
||||
System.getProperty("surefire.test.class.path", System.getProperty("java.class.path")));
|
||||
config.setIgnoreIncrementalAnalysis(true);
|
||||
config.setDefaultLanguageVersion(
|
||||
LanguageRegistry.PMD.getLanguageById("java").getVersion("25"));
|
||||
try (var analysis = PmdAnalysis.create(config)) {
|
||||
analysis.addRuleSet(RuleSet.forSingleRule(new Standards.Measurements()));
|
||||
analysis.files().addSourceFile(FileId.fromPath(Standards.root.resolve("Fixture.java")), text);
|
||||
var report = analysis.performAnalysisAndCollectReport();
|
||||
assertTrue(
|
||||
report.getProcessingErrors().isEmpty(),
|
||||
report.getProcessingErrors().toString());
|
||||
}
|
||||
return List.copyOf(Standards.findings);
|
||||
}
|
||||
|
||||
private boolean contains(List<String> findings, String prefix, String metric) {
|
||||
return findings.stream().anyMatch(s -> s.startsWith(prefix) && s.endsWith(metric));
|
||||
}
|
||||
|
||||
@Test
|
||||
void cyclomaticAndCognitiveBoundaries() throws Exception {
|
||||
var at = analyze("class Fixture { public void run(final boolean b) { " + "if (b) {}".repeat(9) + "} }");
|
||||
assertTrue(contains(at, "REVIEW", "cyclomatic=10.0"));
|
||||
var over = analyze("class Fixture { public void run(final boolean b) { " + "if (b) {}".repeat(10) + "} }");
|
||||
assertTrue(contains(over, "BLOCK", "cyclomatic=11.0"));
|
||||
assertTrue(contains(
|
||||
analyze("class Fixture { public void run(final boolean b) {" + "if (b) {}".repeat(15) + "} }"),
|
||||
"REVIEW",
|
||||
"cognitive=15.0"));
|
||||
assertTrue(contains(
|
||||
analyze("class Fixture { public void run(final boolean b) {" + "if (b) {}".repeat(16) + "} }"),
|
||||
"BLOCK",
|
||||
"cognitive=16.0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void methodAndConstructorParameterBoundariesAndImplicitRecords() throws Exception {
|
||||
assertTrue(contains(
|
||||
analyze("class Fixture { public void run(final int a, final int b, final int c, final int d,"
|
||||
+ " final int e) {} }"),
|
||||
"REVIEW",
|
||||
"parameters=5.0"));
|
||||
assertTrue(contains(
|
||||
analyze("class Fixture { public void run(final int a, final int b, final int c, final int d,"
|
||||
+ " final int e, final int f) {} }"),
|
||||
"BLOCK",
|
||||
"parameters=6.0"));
|
||||
var implicit = analyze("record Fixture(int a, int b, int c, int d, int e, int f, int g, int h) {}");
|
||||
assertFalse(implicit.stream().anyMatch(s -> s.contains("parameters=")));
|
||||
var compact = analyze(
|
||||
"record Fixture(int a, int b, int c, int d, int e, int f, int g, int h) { public Fixture" + " {} }");
|
||||
assertTrue(contains(compact, "BLOCK", "parameters=8.0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void physicalLinesExcludeCommentsAndCountBraces() throws Exception {
|
||||
var findings = analyze("class Fixture {\n"
|
||||
+ "public void run() {\n"
|
||||
+ "// comment\n"
|
||||
+ "/* multi\n"
|
||||
+ " comment */\n\n"
|
||||
+ "System.out.println(1); // code\n"
|
||||
+ "}\n"
|
||||
+ "}");
|
||||
assertTrue(contains(findings, "METRIC", "executableLines=3.0"));
|
||||
assertTrue(contains(findings, "METRIC", "classLines=5.0"));
|
||||
assertTrue(contains(
|
||||
analyze("class Fixture {\npublic void run() {\n" + "System.out.println(1);\n".repeat(48) + "}\n}"),
|
||||
"REVIEW",
|
||||
"executableLines=50.0"));
|
||||
assertTrue(contains(
|
||||
analyze("class Fixture {\npublic void run() {\n" + "System.out.println(1);\n".repeat(49) + "}\n}"),
|
||||
"BLOCK",
|
||||
"executableLines=51.0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void nestingElseIfCatchSwitchLambdaAndNestedMethods() throws Exception {
|
||||
var chain = analyze(
|
||||
"class Fixture { public void run(final boolean b) { if (b) {} else if (b) {} else if (b)" + " {} } }");
|
||||
assertTrue(contains(chain, "METRIC", "nesting=1.0"));
|
||||
var nested = analyze("class Fixture { public void run(final boolean b) { if (b) { while (b) { switch (1) {"
|
||||
+ " default -> { try {} catch (Exception e) { Runnable r = () -> { if (b) {} }; } } }"
|
||||
+ " } } } }");
|
||||
assertTrue(contains(nested, "BLOCK", "nesting=5.0"));
|
||||
var separate = analyze("class Fixture { public void run(final boolean b) { if (b) { class Inner { public void"
|
||||
+ " go() { if (b) {} } } } } }");
|
||||
assertFalse(separate.stream().anyMatch(s -> s.endsWith("nesting=2.0")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void lambdaComplexityContributesAndNestedClassDoesNot() throws Exception {
|
||||
var findings = analyze("class Fixture { public void run(final boolean b) { Runnable r = () -> { if (b) {} };"
|
||||
+ " class Inner { public void nested() { if (b) {} } } } }");
|
||||
assertEquals(
|
||||
2, findings.stream().filter(s -> s.endsWith("cyclomatic=2.0")).count(), findings.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void varAndFinalRules() throws Exception {
|
||||
var findings =
|
||||
analyze("class Fixture { public void run(int p) { var a = new Object(); var b = (Object) a; var c"
|
||||
+ " = name(); int count = 0; count++; } private String name() { return \"x\"; } }");
|
||||
assertEquals(
|
||||
1, findings.stream().filter(s -> s.contains("var requires")).count());
|
||||
assertTrue(findings.stream().anyMatch(s -> s.contains("requires final: p")));
|
||||
assertFalse(findings.stream().anyMatch(s -> s.contains("requires final: count")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void patternBindingsRequireFinalOnlyWhenAssignedOnce() throws Exception {
|
||||
var findings = analyze("""
|
||||
record Pair(Object key, Object value) {}
|
||||
class Fixture {
|
||||
public void run(final Object input) {
|
||||
if (input instanceof String unchanged) { System.out.println(unchanged); }
|
||||
if (input instanceof final String protectedValue) { System.out.println(protectedValue); }
|
||||
if (input instanceof String reassigned) { reassigned = reassigned.trim(); System.out.println(reassigned); }
|
||||
switch (input) {
|
||||
case String switched -> System.out.println(switched);
|
||||
default -> {}
|
||||
}
|
||||
if (input instanceof Pair(var key, var value)) {
|
||||
System.out.println(key);
|
||||
System.out.println(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
""");
|
||||
for (String binding : List.of("unchanged", "switched", "key", "value"))
|
||||
assertTrue(findings.stream().anyMatch(s -> s.contains("requires final: " + binding)), findings.toString());
|
||||
for (String binding : List.of("protectedValue", "reassigned"))
|
||||
assertFalse(findings.stream().anyMatch(s -> s.contains("requires final: " + binding)), findings.toString());
|
||||
assertFalse(findings.stream().anyMatch(s -> s.contains("var requires")), findings.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void inheritanceCountsOnlyProjectSuperclasses() throws Exception {
|
||||
var findings = analyze("class A {} class B extends A {} class C extends B {} class D extends C {} class Fixture"
|
||||
+ " extends D {} class External extends java.util.ArrayList<String> {}");
|
||||
assertTrue(contains(findings, "REVIEW", "ownSuperclassDepth=3.0"));
|
||||
assertTrue(contains(findings, "BLOCK", "ownSuperclassDepth=4.0"));
|
||||
assertTrue(contains(findings, "METRIC", "ownSuperclassDepth=0.0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void injectionUsesResolvedTypesQualifiersInheritedMembersAndImplicitConstructors() throws Exception {
|
||||
var findings = analyze("""
|
||||
class Parent {
|
||||
@jakarta.inject.Inject private String inherited;
|
||||
@jakarta.inject.Inject public void setValue(final Integer value) {}
|
||||
}
|
||||
@jakarta.inject.Singleton class Fixture extends Parent {
|
||||
private final String value;
|
||||
public Fixture(@jakarta.inject.Named("a") final String first,
|
||||
@jakarta.inject.Named("a") final String repeated,
|
||||
@jakarta.inject.Named("b") final String second) { value = first; }
|
||||
@jakarta.inject.Inject private String repeatedInherited;
|
||||
}
|
||||
""");
|
||||
assertTrue(contains(findings, "METRIC", "injectedDependencies=4.0"), findings.toString());
|
||||
String fields = "";
|
||||
for (int i = 0; i < 7; i++)
|
||||
fields += "@jakarta.inject.Inject @jakarta.inject.Named(\"" + i + "\") private String d" + i + ";";
|
||||
assertTrue(contains(analyze("class Fixture {" + fields + "}"), "REVIEW", "injectedDependencies=7.0"));
|
||||
assertTrue(contains(
|
||||
analyze("class Fixture {" + fields + "@jakarta.inject.Inject private Integer eighth;}"),
|
||||
"BLOCK",
|
||||
"injectedDependencies=8.0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void fanoutCountsDistinctJdkTypesAndPublicMethodsExcludeOnlyTrueAccessors() throws Exception {
|
||||
var findings = analyze("""
|
||||
class Fixture {
|
||||
private String name;
|
||||
private java.util.List<String> values;
|
||||
private java.util.List<String> repeated;
|
||||
public String getName() { return name; }
|
||||
public void setName(final String value) { this.name = value; }
|
||||
public String getOther() { return name; }
|
||||
public String getValidatedName() { if (name == null) { throw new IllegalStateException(); } return name; }
|
||||
public static int calculate() { return 1; }
|
||||
}
|
||||
""");
|
||||
assertTrue(contains(findings, "METRIC", "fanOut=3.0"), findings.toString());
|
||||
assertTrue(contains(findings, "METRIC", "publicMethods=3.0"), findings.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void commentedAccessorsRemainTrivialWhileAdditionalWorkStillCounts() throws Exception {
|
||||
var findings = analyze("""
|
||||
class Fixture {
|
||||
private String name;
|
||||
private String trimmed;
|
||||
private String logged;
|
||||
private String calculated;
|
||||
private String appended;
|
||||
public String getName() { /* Intent comment. */ return name; }
|
||||
public void setName(final String value) { this./* Field comment. */name = value; }
|
||||
public String name() { // Read the stored value.
|
||||
return this.name;
|
||||
}
|
||||
public void setName(final String value, final boolean validate) { this.name = value; }
|
||||
public void setTrimmed(final String value) { this.trimmed = value.trim(); }
|
||||
public String getLogged() { System.out.println(logged); return logged; }
|
||||
public String getCalculated() { return calculated.trim(); }
|
||||
public void setAppended(final String value) { this.appended += value; }
|
||||
}
|
||||
""");
|
||||
assertTrue(contains(findings, "METRIC", "publicMethods=5.0"), findings.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void classLengthBoundaryAndNestedTypeLines() throws Exception {
|
||||
assertTrue(contains(
|
||||
analyze("class Fixture {\n" + "private int field;\n".repeat(498) + "}"), "REVIEW", "classLines=500.0"));
|
||||
assertTrue(contains(
|
||||
analyze("class Fixture {\n" + "private int field;\n".repeat(499) + "}"), "BLOCK", "classLines=501.0"));
|
||||
var separate = analyze("class Fixture {\nprivate class Inner {\nprivate int value;\n}\nprivate int other;\n}");
|
||||
assertEquals(
|
||||
2, separate.stream().filter(s -> s.endsWith("classLines=3.0")).count());
|
||||
assertTrue(contains(analyze("class Fixture { private class Inner {} }"), "METRIC", "classLines=1.0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void switchBooleanAndThrowTreatmentIsPinned() throws Exception {
|
||||
var findings = analyze("class Fixture { public int run(final boolean a, final boolean b) { if (a && b) { throw"
|
||||
+ " new IllegalStateException(); } return switch (1) { case 0, 1 -> 1; default -> 2;"
|
||||
+ " }; } }");
|
||||
assertTrue(contains(findings, "METRIC", "cyclomatic=6.0"), findings.toString());
|
||||
}
|
||||
|
||||
private Path project(String production, String tests) throws Exception {
|
||||
Path directory = Files.createTempDirectory("s7-fixture-");
|
||||
Files.writeString(directory.resolve("engineering.properties"), "BACKEND_MODULE=backend\n");
|
||||
Path prod = directory.resolve("backend/src/main/java");
|
||||
Path test = directory.resolve("backend/src/test/java");
|
||||
Files.createDirectories(prod);
|
||||
Files.createDirectories(test);
|
||||
Path config = directory.resolve("generated/tooling/standards/checkstyle.xml");
|
||||
Files.createDirectories(config.getParent());
|
||||
Files.copy(Path.of(System.getProperty("basedir"), "checkstyle.xml"), config);
|
||||
if (production != null) Files.writeString(prod.resolve("Fixture.java"), production);
|
||||
if (tests != null) Files.writeString(test.resolve("ExampleTest.java"), tests);
|
||||
return directory;
|
||||
}
|
||||
|
||||
@Test
|
||||
void checksFailWithoutRewritingSourcesAndExposeReviewSignals() throws Exception {
|
||||
String source = "package example;\n"
|
||||
+ "import java.util.*;\n"
|
||||
+ "class Fixture { public void run(int x) { if (x == 0) System.out.println(x); } }";
|
||||
Path directory = project(source, null);
|
||||
assertThrows(IllegalStateException.class, () -> Standards.main(new String[] {directory.toString()}));
|
||||
assertEquals(source, Files.readString(directory.resolve("backend/src/main/java/Fixture.java")));
|
||||
String report = Files.readString(directory.resolve("backend/target/standards/report.txt"));
|
||||
assertTrue(report.contains("form of import"), report);
|
||||
assertTrue(report.contains("must use '{}'s"), report);
|
||||
assertTrue(report.contains("requires final: x"), report);
|
||||
}
|
||||
|
||||
@Test
|
||||
void productionCpdCountsEveryOccurrenceAndTestClonesDoNotGate() throws Exception {
|
||||
String method = "public void first() {\n" + "System.out.println(123);\n".repeat(30) + "}\n";
|
||||
String source = "package example;\nclass Fixture {\n" + method + method.replace("first", "second") + "}";
|
||||
Path directory = project(source, null);
|
||||
assertThrows(IllegalStateException.class, () -> Standards.main(new String[] {directory.toString()}));
|
||||
String report = Files.readString(directory.resolve("backend/target/standards/report.txt"));
|
||||
assertTrue(report.contains("BLOCK productionDuplication=") && report.contains("(64/67)"), report);
|
||||
assertTrue(report.lines().filter(l -> l.startsWith("CLONE")).count() >= 2, report);
|
||||
Path testProject =
|
||||
project("package example; class Fixture {}", source.replace("class Fixture", "class ExampleTest"));
|
||||
Standards.main(new String[] {testProject.toString()});
|
||||
String testReport = Files.readString(testProject.resolve("backend/target/standards/report.txt"));
|
||||
assertTrue(testReport.contains("productionDuplication=0.0%"), testReport);
|
||||
}
|
||||
|
||||
@Test
|
||||
void parserFailureNamesTheAffectedFileAndFails() throws Exception {
|
||||
Path directory = project("package example; class Fixture { public void broken( }", null);
|
||||
Exception failure = assertThrows(Exception.class, () -> Standards.main(new String[] {directory.toString()}));
|
||||
assertTrue(
|
||||
failure.toString().contains("Fixture.java")
|
||||
|| Standards.findings.stream().anyMatch(f -> f.contains("Fixture.java")),
|
||||
failure.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void exactCoverageGateAcceptsEightyPercentAndRejectsRoundedNearMiss() throws Exception {
|
||||
Path directory = Files.createTempDirectory("s7-coverage-");
|
||||
Path xml = directory.resolve("report.xml");
|
||||
Path helper = Path.of(System.getProperty("basedir"), "../coverage/Coverage.java")
|
||||
.normalize();
|
||||
for (long[] counts : List.of(new long[] {4, 1, 0}, new long[] {799999, 200001, 1}, new long[] {0, 0, 1})) {
|
||||
Files.writeString(
|
||||
xml,
|
||||
"<report><counter type=\"LINE\" covered=\"" + counts[0] + "\" missed=\"" + counts[1]
|
||||
+ "\"/></report>");
|
||||
Process process = new ProcessBuilder(
|
||||
Path.of(System.getProperty("java.home"), "bin/java").toString(),
|
||||
helper.toString(),
|
||||
xml.toString())
|
||||
.redirectErrorStream(true)
|
||||
.redirectOutput(directory.resolve("output.log").toFile())
|
||||
.start();
|
||||
assertEquals(counts[2], process.waitFor(), Files.readString(directory.resolve("output.log")));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void overridesAndImplicitRecordInjectionAreMeasured() throws Exception {
|
||||
var override = analyze("class Fixture extends java.util.ArrayList<String> { public void clear() {} }");
|
||||
assertTrue(override.stream().anyMatch(f -> f.contains("require @Override")), override.toString());
|
||||
var record = analyze("@jakarta.inject.Singleton record Fixture(String one, String repeated, Integer two) {}");
|
||||
assertTrue(contains(record, "METRIC", "injectedDependencies=2.0"), record.toString());
|
||||
var inherited = analyze("class Parent<T> { @jakarta.inject.Inject private T dependency; } class Fixture extends"
|
||||
+ " Parent<String> { @jakarta.inject.Inject private String repeated; }");
|
||||
assertEquals(
|
||||
2,
|
||||
inherited.stream()
|
||||
.filter(f -> f.endsWith("injectedDependencies=1.0"))
|
||||
.count(),
|
||||
inherited.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void waiverMatchesOnlyTheExactMetricSignatureAndRetainedValue() throws Exception {
|
||||
Standards.waivers.add(new Standards.Waiver(
|
||||
"parameters",
|
||||
"Fixture#run(int,int,int,int,int,int)",
|
||||
6,
|
||||
"Test fixture agreement: preserve an example signature"));
|
||||
try {
|
||||
var waived = analyze("class Fixture { public void run(final int a, final int b, final int c, final int d,"
|
||||
+ " final int e, final int f) {} }");
|
||||
assertTrue(
|
||||
waived.stream().anyMatch(f -> f.startsWith("WAIVER") && f.contains("parameters=6.0")),
|
||||
waived.toString());
|
||||
var changed = analyze("class Fixture { public void run(final int a, final int b, final int c, final int d,"
|
||||
+ " final int e, final int f, final int g) {} }");
|
||||
assertTrue(contains(changed, "BLOCK", "parameters=7.0"));
|
||||
} finally {
|
||||
Standards.waivers.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user