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 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 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 {}"); 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 values; private java.util.List 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, ""); 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 { 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 { @jakarta.inject.Inject private T dependency; } class Fixture extends" + " Parent { @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(); } } }