• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pmd / pmd / 459

07 Apr 2026 05:56PM UTC coverage: 79.049% (-0.9%) from 79.972%
459

push

github

web-flow
chore(deps): bump bigdecimal from 4.0.1 to 4.1.0 in /docs (#6572)

Bumps [bigdecimal](https://github.com/ruby/bigdecimal) from 4.0.1 to 4.1.0.
- [Release notes](https://github.com/ruby/bigdecimal/releases)
- [Changelog](https://github.com/ruby/bigdecimal/blob/master/CHANGES.md)
- [Commits](https://github.com/ruby/bigdecimal/compare/v4.0.1...v4.1.0)

---
updated-dependencies:
- dependency-name: bigdecimal
  dependency-version: 4.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

18605 of 24431 branches covered (76.15%)

Branch coverage included in aggregate %.

40591 of 50454 relevant lines covered (80.45%)

0.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

45.05
/pmd-test/src/main/java/net/sourceforge/pmd/test/RuleTst.java
1
/*
2
 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3
 */
4

5
package net.sourceforge.pmd.test;
6

7
import static org.junit.jupiter.api.Assertions.assertEquals;
8
import static org.junit.jupiter.api.Assertions.fail;
9

10
import java.io.File;
11
import java.io.IOException;
12
import java.io.InputStream;
13
import java.io.StringWriter;
14
import java.net.URI;
15
import java.net.URISyntaxException;
16
import java.net.URL;
17
import java.nio.file.Path;
18
import java.nio.file.Paths;
19
import java.security.CodeSource;
20
import java.util.ArrayList;
21
import java.util.Collection;
22
import java.util.Collections;
23
import java.util.Comparator;
24
import java.util.List;
25
import java.util.Map;
26

27
import org.apache.commons.lang3.StringUtils;
28
import org.junit.jupiter.api.Assumptions;
29
import org.junit.jupiter.api.DynamicTest;
30
import org.junit.jupiter.api.TestFactory;
31
import org.xml.sax.InputSource;
32

33
import net.sourceforge.pmd.PMDConfiguration;
34
import net.sourceforge.pmd.PmdAnalysis;
35
import net.sourceforge.pmd.internal.util.ClasspathClassLoader;
36
import net.sourceforge.pmd.lang.LanguageVersion;
37
import net.sourceforge.pmd.lang.document.FileId;
38
import net.sourceforge.pmd.lang.document.TextFile;
39
import net.sourceforge.pmd.lang.rule.Rule;
40
import net.sourceforge.pmd.lang.rule.RuleSet;
41
import net.sourceforge.pmd.lang.rule.RuleSetLoadException;
42
import net.sourceforge.pmd.lang.rule.RuleSetLoader;
43
import net.sourceforge.pmd.properties.PropertyDescriptor;
44
import net.sourceforge.pmd.renderers.TextRenderer;
45
import net.sourceforge.pmd.reporting.GlobalAnalysisListener;
46
import net.sourceforge.pmd.reporting.Report;
47
import net.sourceforge.pmd.reporting.RuleViolation;
48
import net.sourceforge.pmd.test.schema.RuleTestCollection;
49
import net.sourceforge.pmd.test.schema.RuleTestDescriptor;
50
import net.sourceforge.pmd.test.schema.TestSchemaParser;
51

52
/**
53
 * Advanced methods for test cases
54
 */
55
public abstract class RuleTst {
1✔
56

57
    protected void setUp() {
58
        // This method is intended to be overridden by subclasses.
59
    }
×
60

61
    /**
62
     * Return the rules that will be tested. Each rule must have a corresponding XML file containing a test collection.
63
     * Test collections for all these rules are run separately.
64
     */
65
    protected List<Rule> getRules() {
66
        return Collections.emptyList();
×
67
    }
68

69
    /** Return extra rules that will be run while running the tests. */
70
    protected Collection<? extends Rule> getExtraRules() {
71
        return Collections.emptyList();
1✔
72
    }
73

74
    /**
75
     * Find a rule in a certain ruleset by name.
76
     */
77
    public static Rule findRule(String ruleSet, String ruleName) {
78
        try {
79
            RuleSet parsedRset = new RuleSetLoader().warnDeprecated(false).loadFromResource(ruleSet);
×
80
            Rule rule = parsedRset.getRuleByName(ruleName);
×
81
            if (rule == null) {
×
82
                fail("Rule " + ruleName + " not found in ruleset " + ruleSet);
×
83
            } else {
84
                rule.setRuleSetName(ruleSet);
×
85
            }
86
            return rule;
×
87
        } catch (RuleSetLoadException e) {
×
88
            e.printStackTrace();
×
89
            fail("Couldn't find ruleset " + ruleSet);
×
90
            return null;
×
91
        }
92
    }
93

94
    /**
95
     * Run the rule on the given code, and check the expected number of violations.
96
     */
97
    void runTest(RuleTestDescriptor test) {
98
        Rule rule = test.getRule();
1✔
99

100
        // always reinitialize the rule, regardless of test.getReinitializeRule() (#3976 / #3302)
101
        rule = reinitializeRule(rule);
1✔
102

103
        Map<PropertyDescriptor<?>, Object> oldProperties = rule.getPropertiesByPropertyDescriptor();
1✔
104
        Report report = null;
1✔
105
        try {
106
            int res;
107
            try {
108
                // Set test specific properties onto the Rule
109
                if (test.getProperties() != null) {
1!
110
                    for (Map.Entry<Object, Object> entry : test.getProperties().entrySet()) {
1!
111
                        String propertyName = (String) entry.getKey();
×
112
                        PropertyDescriptor propertyDescriptor = rule.getPropertyDescriptor(propertyName);
×
113
                        if (propertyDescriptor == null) {
×
114
                            throw new IllegalArgumentException(
×
115
                                    "No such property '" + propertyName + "' on Rule " + rule.getName());
×
116
                        }
117

118
                        Object value = propertyDescriptor.serializer().fromString((String) entry.getValue());
×
119
                        if (propertyDescriptor.serializer().isFromStringDeprecated((String) entry.getValue())) {
×
120
                            System.err.println(rule.getName() + ":" + test.getDescription() + ": Deprecated property value used! " + entry);
×
121
                        }
122
                        rule.setProperty(propertyDescriptor, value);
×
123
                    }
×
124
                }
125

126
                String dysfunctionReason = rule.dysfunctionReason();
1✔
127
                if (StringUtils.isNotBlank(dysfunctionReason)) {
1!
128
                    throw new RuntimeException("Rule is not configured correctly: " + dysfunctionReason);
×
129
                }
130

131
                report = processUsingStringReader(test, rule);
1✔
132
                res = report.getViolations().size();
1✔
133
            } catch (Exception e) {
×
134
                e.printStackTrace();
×
135
                throw new RuntimeException('"' + test.getDescription() + "\" failed", e);
×
136
            }
1✔
137
            assertEquals(test.getExpectedProblems(), res,
1✔
138
                    '"' + test.getDescription() + "\" resulted in wrong number of failures,");
1✔
139
            assertMessages(report, test);
1✔
140
            assertLineNumbers(report, test);
1✔
141
            assertSuppressions(report, test);
1✔
142
        } catch (AssertionError e) {
1✔
143
            printReport(test, report);
1✔
144
            throw e;
1✔
145
        } finally {
146
            // Restore old properties
147
            for (Map.Entry<PropertyDescriptor<?>, Object> entry : oldProperties.entrySet()) {
1✔
148
                rule.setProperty((PropertyDescriptor) entry.getKey(), entry.getValue());
1✔
149
            }
1✔
150
        }
151
    }
1✔
152

153

154
    /**
155
     * Code to be executed if the rule is reinitialised.
156
     *
157
     * @param rule The rule to reinitialise
158
     *
159
     * @return The rule once it has been reinitialised
160
     */
161
    private Rule reinitializeRule(Rule rule) {
162
        return rule.deepCopy();
1✔
163
    }
164

165
    private void assertSuppressions(Report report, RuleTestDescriptor test) {
166
        if (!test.hasExpectedSuppressions()) {
1✔
167
            return;
1✔
168
        }
169
        List<RuleTestDescriptor.SuppressionDescriptor> expectedSuppressions = test.getExpectedSuppressions();
1✔
170
        assertEquals(expectedSuppressions.size(), report.getSuppressedViolations().size(), "wrong number of suppressed violations");
1✔
171
        for (int i = 0; i < expectedSuppressions.size(); i++) {
1✔
172
            RuleTestDescriptor.SuppressionDescriptor expectedSuppression = expectedSuppressions.get(i);
1✔
173
            Report.SuppressedViolation actualSuppression = report.getSuppressedViolations().get(i);
1✔
174
            assertEquals(expectedSuppression.getLine(), actualSuppression.getRuleViolation().getBeginLine(), "wrong line for suppression");
1✔
175
            if (StringUtils.isNotBlank(expectedSuppression.getSuppressorId())) {
1✔
176
                assertEquals(expectedSuppression.getSuppressorId(), actualSuppression.getSuppressor().getId(), "wrong suppressor id");
1✔
177
            }
178
        }
179
    }
1✔
180

181
    private void assertMessages(Report report, RuleTestDescriptor test) {
182
        if (report == null || test.getExpectedMessages().isEmpty()) {
1!
183
            return;
1✔
184
        }
185

186
        List<String> expectedMessages = test.getExpectedMessages();
×
187
        if (report.getViolations().size() != expectedMessages.size()) {
×
188
            throw new RuntimeException("Test setup error: number of expected messages doesn't match "
×
189
                                           + "number of violations for test case '" + test.getDescription() + "'");
×
190
        }
191

192
        int index = 0;
×
193
        for (RuleViolation violation : report.getViolations()) {
×
194
            String actual = violation.getDescription();
×
195
            assertEquals(expectedMessages.get(index), actual,
×
196
                         '"' + test.getDescription() + "\" produced wrong message on violation number " + (index + 1)
×
197
                             + ".");
198
            index++;
×
199
        }
×
200
    }
×
201

202
    private void assertLineNumbers(Report report, RuleTestDescriptor test) {
203
        if (report == null || test.getExpectedLineNumbers().isEmpty()) {
1!
204
            return;
1✔
205
        }
206

207
        List<Integer> expected = test.getExpectedLineNumbers();
1✔
208
        List<Integer> expectedEndLines = test.getExpectedEndLineNumbers();
1✔
209
        if (report.getViolations().size() != expected.size()) {
1!
210
            throw new RuntimeException("Test setup error: number of expected line numbers " + expected.size()
×
211
                                           + " doesn't match number of violations " + report.getViolations().size()
×
212
                                           + " for test case '"
213
                                           + test.getDescription() + "'");
×
214
        }
215

216
        int index = 0;
1✔
217
        for (RuleViolation violation : report.getViolations()) {
1✔
218
            Integer actualBeginLine = violation.getBeginLine();
1✔
219
            Integer actualEndLine = violation.getEndLine();
1✔
220

221
            assertEquals(expected.get(index), actualBeginLine,
1✔
222
                         '"' + test.getDescription() + "\" violation on wrong line number: violation number "
1✔
223
                             + (index + 1) + ".");
224
            if (!expectedEndLines.isEmpty()) {
1!
225
                assertEquals(expectedEndLines.get(index), actualEndLine,
1✔
226
                        '"' + test.getDescription() + "\" violation on wrong end line number: violation number "
1✔
227
                            + (index + 1) + ".");
228
            }
229
            index++;
1✔
230
        }
1✔
231
    }
1✔
232

233
    private void printReport(RuleTestDescriptor test, Report report) {
234
        final String separator = "--------------------------------------------------------------";
1✔
235

236
        System.out.println(separator);
1✔
237
        System.out.println("Test Failure: " + test.getDescription());
1✔
238

239
        if (report == null) {
1!
240
            System.out.println("There is no report!");
×
241
            System.out.println(separator);
×
242
            return;
×
243
        }
244

245
        System.out.println(
1✔
246
            " -> Expected " + test.getExpectedProblems() + " problem(s), " + report.getViolations().size()
1✔
247
                + " problem(s) found.");
248
        System.out.println(" -> Expected messages: " + test.getExpectedMessages());
1✔
249
        System.out.println(" -> Expected begin line numbers: " + test.getExpectedLineNumbers());
1✔
250
        if (!test.getExpectedEndLineNumbers().isEmpty()) {
1!
251
            System.out.println(" -> Expected   end line numbers: " + test.getExpectedEndLineNumbers());
×
252
        }
253
        if (test.hasExpectedSuppressions()) {
1!
254
            System.out.println(" -> Expected " + test.getExpectedSuppressions().size() + " suppression(s), "
1✔
255
                    + report.getSuppressedViolations().size() + " found.");
1✔
256
        }
257
        System.out.println();
1✔
258
        StringWriter reportOutput = new StringWriter();
1✔
259
        TextRenderer renderer = new TextRenderer();
1✔
260
        renderer.setWriter(reportOutput);
1✔
261
        try {
262
            renderer.start();
1✔
263
            renderer.renderFileReport(report);
1✔
264
            renderer.end();
1✔
265
        } catch (IOException e) {
×
266
            throw new RuntimeException(e);
×
267
        }
1✔
268
        System.out.println(reportOutput);
1✔
269
        System.out.println(separator);
1✔
270
    }
1✔
271

272
    private Report processUsingStringReader(RuleTestDescriptor test, Rule rule) {
273
        return runTestFromString(test.getCode(), rule, test.getLanguageVersion());
1✔
274
    }
275

276
    private static final ClassLoader TEST_AUXCLASSPATH_CLASSLOADER;
277

278
    static {
279
        final Path PATH_TO_JRT_FS_JAR;
280
        // find jrt-fs.jar to be added to auxclasspath
281
        // Similar logic like jdk.internal.jrtfs.SystemImage
282
        CodeSource codeSource = Object.class.getProtectionDomain().getCodeSource();
1✔
283
        if (codeSource == null) {
1!
284
            PATH_TO_JRT_FS_JAR = Paths.get(System.getProperty("java.home"), "lib", "jrt-fs.jar");
1✔
285
        } else {
286
            URL location = codeSource.getLocation();
×
287
            if (!"file".equalsIgnoreCase(location.getProtocol())) {
×
288
                throw new IllegalStateException("Object.class loaded in unexpected way from " + location);
×
289
            }
290
            try {
291
                PATH_TO_JRT_FS_JAR = Paths.get(location.toURI());
×
292
            } catch (URISyntaxException e) {
×
293
                throw new IllegalStateException(e);
×
294
            }
×
295
        }
296

297
        try {
298
            TEST_AUXCLASSPATH_CLASSLOADER = new ClasspathClassLoader(PATH_TO_JRT_FS_JAR.toString(), PMDConfiguration.class.getClassLoader());
1✔
299
        } catch (IOException e) {
×
300
            throw new RuntimeException(e);
×
301
        }
1✔
302
    }
1✔
303

304
    /**
305
     * Run the rule on the given code and put the violations in the report.
306
     */
307
    Report runTestFromString(String code, Rule rule, LanguageVersion languageVersion) {
308
        PMDConfiguration configuration = new PMDConfiguration();
1✔
309
        configuration.setIgnoreIncrementalAnalysis(true);
1✔
310
        configuration.setDefaultLanguageVersion(languageVersion);
1✔
311
        configuration.setThreads(0); // don't use separate threads
1✔
312
        configuration.setClassLoader(TEST_AUXCLASSPATH_CLASSLOADER);
1✔
313

314
        try (PmdAnalysis pmd = PmdAnalysis.create(configuration)) {
1✔
315
            pmd.files().addFile(TextFile.forCharSeq(code, FileId.fromPathLikeString("file"), languageVersion));
1✔
316
            Collection<? extends Rule> extraRules = getExtraRules();
1✔
317
            if (!extraRules.isEmpty()) {
1!
318
                pmd.addRuleSet(RuleSet.create("extra rules", "description", "file.xml", Collections.emptyList(), Collections.emptyList(), extraRules));
×
319
            }
320
            pmd.addRuleSet(RuleSet.forSingleRule(rule));
1✔
321
            pmd.addListener(GlobalAnalysisListener.exceptionThrower());
1✔
322
            return pmd.performAnalysisAndCollectReport();
1✔
323
        }
324
    }
325

326
    /**
327
     * getResourceAsStream tries to find the XML file in weird locations if the
328
     * ruleName includes the package, so we strip it here.
329
     */
330
    private String getCleanRuleName(Rule rule) {
331
        String fullClassName = rule.getClass().getName();
×
332
        if (fullClassName.equals(rule.getName())) {
×
333
            // We got the full class name, so we'll use the stripped name
334
            // instead
335
            String packageName = rule.getClass().getPackage().getName();
×
336
            return fullClassName.substring(packageName.length() + 1);
×
337
        } else {
338
            return rule.getName(); // Test is using findRule, smart!
×
339
        }
340
    }
341

342
    /**
343
     * Extract a set of tests from an XML file. The file should be
344
     * ./xml/RuleName.xml relative to the test class. The format is defined in
345
     * rule-tests_1_1_0.xsd in pmd-test-schema.
346
     */
347
    RuleTestCollection parseTestCollection(Rule rule) {
348
        String testsFileName = getCleanRuleName(rule);
×
349
        return parseTestCollection(rule, testsFileName);
×
350
    }
351

352
    private RuleTestCollection parseTestCollection(Rule rule, String testsFileName) {
353
        return parseTestXml(rule, testsFileName, "xml/");
×
354
    }
355

356
    /**
357
     * Extract a set of tests from an XML file with the given name. The file
358
     * should be ./xml/[testsFileName].xml relative to the test class. The
359
     * format is defined in test-data.xsd.
360
     */
361
    private RuleTestCollection parseTestXml(Rule rule, String testsFileName, String baseDirectory) {
362
        String testXmlFileName = baseDirectory + testsFileName + ".xml";
×
363
        String absoluteUriToTestXmlFile = new File(".").getAbsoluteFile().toURI() + "/src/test/resources/"
×
364
                + this.getClass().getPackage().getName().replaceAll("\\.", "/")
×
365
                + "/" + testXmlFileName;
366

367
        try (InputStream inputStream = getClass().getResourceAsStream(testXmlFileName)) {
×
368
            if (inputStream == null) {
×
369
                throw new RuntimeException("Couldn't find " + testXmlFileName);
×
370
            }
371
            InputSource source = new InputSource();
×
372
            source.setByteStream(inputStream);
×
373
            source.setSystemId(testXmlFileName);
×
374
            TestSchemaParser parser = new TestSchemaParser();
×
375
            RuleTestCollection ruleTestCollection = parser.parse(rule, source);
×
376
            ruleTestCollection.setAbsoluteUriToTestXmlFile(absoluteUriToTestXmlFile);
×
377
            return ruleTestCollection;
×
378
        } catch (Exception e) {
×
379
            throw new RuntimeException("Couldn't parse " + testXmlFileName + ", due to: " + e, e);
×
380
        }
381
    }
382

383
    /**
384
     * Run a set of tests defined in an XML test-data file for a rule. The file
385
     * should be ./xml/RuleName.xml relative to the test-class. The format is
386
     * defined in test-data.xsd.
387
     */
388
    public void runTests(Rule rule) {
389
        runTests(parseTestCollection(rule));
×
390
    }
×
391

392
    /**
393
     * Run a set of tests defined in a XML test-data file. The file should be
394
     * ./xml/[testsFileName].xml relative to the test-class. The format is
395
     * defined in test-data.xsd.
396
     */
397
    public void runTests(Rule rule, String testsFileName) {
398
        runTests(parseTestCollection(rule, testsFileName));
×
399
    }
×
400

401
    private void runTests(RuleTestCollection tests) {
402
        for (RuleTestDescriptor test : tests.getTests()) {
×
403
            runTest(test);
×
404
        }
×
405
    }
×
406

407
    @TestFactory
408
    Collection<DynamicTest> ruleTests() {
409
        setUp();
×
410
        final List<Rule> rules = new ArrayList<>(getRules());
×
411
        rules.sort(Comparator.comparing(Rule::getName));
×
412

413
        List<DynamicTest> tests = new ArrayList<>();
×
414
        for (Rule r : rules) {
×
415
            RuleTestCollection ruleTests = parseTestCollection(r);
×
416
            RuleTestDescriptor focused = ruleTests.getFocusedTestOrNull();
×
417
            for (RuleTestDescriptor t : ruleTests.getTests()) {
×
418
                if (focused != null && !focused.equals(t)) {
×
419
                    t.setDisabled(true); // disable it
×
420
                }
421
                tests.add(toDynamicTest(ruleTests, t));
×
422
            }
×
423
        }
×
424
        return tests;
×
425
    }
426

427
    private DynamicTest toDynamicTest(RuleTestCollection collection, RuleTestDescriptor testDescriptor) {
428
        URI testSourceUri = URI.create(
×
429
            collection.getAbsoluteUriToTestXmlFile() + "?line=" + testDescriptor.getLineNumber());
×
430
        if (testDescriptor.isDisabled()) {
×
431
            return DynamicTest.dynamicTest(testDescriptor.getDescription(),
×
432
                                           testSourceUri,
433
                                           () -> Assumptions.abort("Test is disabled"));
×
434
        }
435
        return DynamicTest.dynamicTest(testDescriptor.getDescription(),
×
436
                                       testSourceUri,
437
                                       () -> runTest(testDescriptor));
×
438
    }
439
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc