Skip to content

Commit 5b4294b

Browse files
authored
Support --auto-gen-config for rules which start with "Abstract" (#142)
This solves #124 slightly differently: As we know, which rules exist, we use that information and build up a map of rule java source files to rule references. E.g. in pmd-java/src/main/categories/java/bestpractices.xml we see, that the rule "AbstractClassWithoutAbstractMethod" is implemented in class "net.sourceforge.pmd.lang.java.rule.bestpractices.AbstractClassWithoutAbstractMethodRule". For this class name we can guess the corresponding java source (.../src/main/java/...). So, when we see this java source in the changes, then we know, we need to regression test that rule. This means, we don't guess anymore, whether a java source file is a rule implementation - we have a list of all rule implementations that we can check now. This also fixes the integration test (https://github.com/pmd/pmd-regression-tester/actions/workflows/manual-integration-tests.yml): In test case 1 we changed the rule AbstractClassWithoutAbstractMethod and we expect, that we only see differences from this rule. As other rules won't be executed, we expected that we get less errors - but with the change from #141 now all rules have been executed again. Refs - #124 - #141
2 parents c024e8a + 33891a1 commit 5b4294b

File tree

8 files changed

+195
-13
lines changed

8 files changed

+195
-13
lines changed

History.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
## New and Noteworthy
33
## Enhancements
44
## Fixed Issues
5+
* [#142](https://github.com/pmd/pmd-regression-tester/pull/142): Support `--auto-gen-config` for rules which start with "Abstract"
56
## Merged pull requests
7+
* [#142](https://github.com/pmd/pmd-regression-tester/pull/142): Support `--auto-gen-config` for rules which start with "Abstract" - [Andreas Dangel](https://github.com/adangel) (@adangel)
68
* [#143](https://github.com/pmd/pmd-regression-tester/pull/143): chore(ci): Use java 17 as default - [Andreas Dangel](https://github.com/adangel) (@adangel)
79
## Dependency Updates
810

lib/pmdtester/builders/rule_set_builder.rb

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ def initialize(options)
2525
def build?
2626
languages = determine_languages
2727
filenames = diff_filenames(languages)
28-
run_required, rule_refs = get_rule_refs(filenames)
28+
all_rules_hash = determine_all_rules
29+
run_required, rule_refs = get_rule_refs(filenames, all_rules_hash)
2930
if run_required
3031
output_filter_set(rule_refs)
3132
build_config_file(rule_refs)
@@ -71,8 +72,8 @@ def output_filter_set(rule_refs)
7172
# filtering possible or if no rules are affected.
7273
# Whether to run the regression test is returned as an additional boolean flag.
7374
#
74-
def get_rule_refs(filenames)
75-
run_required, categories, rules = determine_categories_rules(filenames)
75+
def get_rule_refs(filenames, all_rules_hash)
76+
run_required, categories, rules = determine_categories_rules(filenames, all_rules_hash)
7677
logger.debug "Regression test required: #{run_required}"
7778
logger.debug "Categories: #{categories}"
7879
logger.debug "Rules: #{rules}"
@@ -122,12 +123,12 @@ def write_dynamic_file(rule_refs)
122123

123124
private
124125

125-
def determine_categories_rules(filenames)
126+
def determine_categories_rules(filenames, all_rules_hash)
126127
regression_test_required = false
127128
categories = Set[]
128129
rules = Set[]
129130
filenames.each do |filename|
130-
matched = check_single_filename?(filename, categories, rules)
131+
matched = check_single_filename?(filename, all_rules_hash, categories, rules)
131132
regression_test_required = true if matched
132133

133134
next if matched
@@ -141,14 +142,13 @@ def determine_categories_rules(filenames)
141142
[regression_test_required, categories, rules]
142143
end
143144

144-
def check_single_filename?(filename, categories, rules)
145+
def check_single_filename?(filename, all_rules_hash, categories, rules)
145146
logger.debug "Checking #{filename}"
146147

147-
# matches Java-based rule implementations
148-
match_data = %r{.+/src/main/java/.+/lang/([^/]+)/rule/([^/]+)/([^/]+)Rule.java}.match(filename)
149-
unless match_data.nil? || match_data[3].start_with?('Abstract')
150-
logger.debug "Matches: #{match_data.inspect}"
151-
rules.add("#{match_data[1]}/#{match_data[2]}.xml/#{match_data[3]}")
148+
# direct match of a Java-based rule implementation
149+
if all_rules_hash.key?(filename)
150+
logger.debug "Direct match: #{all_rules_hash[filename]}"
151+
rules.add(all_rules_hash[filename])
152152
return true
153153
end
154154

@@ -196,5 +196,35 @@ def determine_languages
196196
end
197197
languages
198198
end
199+
200+
# Creates a mapping between java source files and the rule reference
201+
# in the ruleset, e.g.:
202+
# 'pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/NcssCountRule.java'
203+
# => 'java/design.xml/NcssCount'
204+
def determine_all_rules
205+
all_rules_hash = {}
206+
Dir.chdir(@options.local_git_repo) do
207+
Dir.glob('**/src/main/resources/category/*/*.xml').each do |rulesetfile|
208+
match_data = %r{.+/src/main/resources/category/([^/]+)/([^/.]+\.xml)}.match(rulesetfile)
209+
ref_prefix = "#{match_data[1]}/#{match_data[2]}"
210+
java_base_path = rulesetfile.gsub(%r{src/main/resources/category/.+/.+\.xml}, 'src/main/java')
211+
212+
doc = File.open(rulesetfile) { |f| Nokogiri::XML(f) }
213+
rules = doc.css('ruleset rule')
214+
rules.each do |r|
215+
next if r.attributes['class'].nil? # skip rule references
216+
217+
ref = "#{ref_prefix}/#{r.attributes['name'].content}"
218+
clazz = r.attributes['class'].content
219+
220+
# guess java file name at standard location src/main/java relative to the category ruleset
221+
java_filename = "#{java_base_path}/#{clazz.gsub('.', '/')}.java"
222+
223+
all_rules_hash[java_filename] = ref
224+
end
225+
end
226+
end
227+
all_rules_hash
228+
end
199229
end
200230
end
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ruleset 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" name="Dynamic PmdTester Ruleset">
3+
<description>The ruleset generated by PmdTester dynamically</description>
4+
<rule ref="category/java/bestpractices.xml/AbstractClassWithoutAbstractMethod"/>
5+
</ruleset>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<ruleset name="Best Practices"
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>
9+
Rules which enforce generally accepted best practices.
10+
</description>
11+
12+
13+
<rule name="AvoidGlobalModifier"
14+
language="java"
15+
since="1.0"
16+
message="not important for this test"
17+
class="net.sourceforge.pmd.lang.apex.rule.bestpractices.AvoidGlobalModifierRule"
18+
externalInfoUrl="not important for this test">
19+
<description>
20+
not important for this test
21+
</description>
22+
<priority>3</priority>
23+
<example>
24+
</example>
25+
</rule>
26+
27+
</ruleset>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<ruleset name="Best Practices"
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>
9+
Rules which enforce generally accepted best practices.
10+
</description>
11+
12+
<rule name="AbstractClassWithoutAbstractMethod"
13+
language="java"
14+
since="1.0"
15+
message="not important for this test"
16+
class="net.sourceforge.pmd.lang.java.rule.bestpractices.AbstractClassWithoutAbstractMethodRule"
17+
externalInfoUrl="not important for this test">
18+
<description>
19+
not important for this test
20+
</description>
21+
<priority>3</priority>
22+
<example>
23+
</example>
24+
</rule>
25+
26+
<rule name="AReference" ref="AbstractClassWithoutAbstractMethod"/>
27+
28+
</ruleset>
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<ruleset name="Code Style"
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>
9+
Rules which enforce a specific coding style.
10+
</description>
11+
12+
<rule name="UnnecessaryConstructor"
13+
language="java"
14+
since="1.0"
15+
message="not important for this test"
16+
class="net.sourceforge.pmd.lang.java.rule.codestyle.UnnecessaryConstructorRule"
17+
externalInfoUrl="not important for this test">
18+
<description>
19+
not important for this test
20+
</description>
21+
<priority>3</priority>
22+
<example>
23+
</example>
24+
</rule>
25+
26+
<rule name="UnnecessaryReturnValue"
27+
language="java"
28+
since="1.0"
29+
message="not important for this test"
30+
class="net.sourceforge.pmd.lang.java.rule.codestyle.UnnecessaryReturnValueRule"
31+
externalInfoUrl="not important for this test">
32+
<description>
33+
not important for this test
34+
</description>
35+
<priority>3</priority>
36+
<example>
37+
</example>
38+
</rule>
39+
</ruleset>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<ruleset name="Design"
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>
9+
Rules that help you discover design issues.
10+
</description>
11+
12+
<rule name="NcssCount"
13+
language="java"
14+
since="1.0"
15+
message="not important for this test"
16+
class="net.sourceforge.pmd.lang.java.rule.design.NcssCountRule"
17+
externalInfoUrl="not important for this test">
18+
<description>
19+
not important for this test
20+
</description>
21+
<priority>3</priority>
22+
<example>
23+
</example>
24+
</rule>
25+
26+
</ruleset>

test/test_rule_set_builder.rb

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def cleanup
1515
def mock_build?(diff_filenames, filter_set = nil, patch_config = nil)
1616
options = mock
1717
options.expects(:patch_config).returns(Options::DEFAULT_CONFIG_PATH)
18-
options.expects(:local_git_repo).returns('.')
18+
options.expects(:local_git_repo).returns("#{PATH_TO_TEST_RESOURCES}/partial-pmd-repo").twice
1919
options.expects(:base_branch).returns('base_branch')
2020
options.expects(:patch_branch).returns('patch_branch')
2121
options.expects(:filter_set=).with(filter_set)
@@ -46,7 +46,9 @@ def test_build_design_codestyle_config
4646
assert_equal(expected, actual)
4747
end
4848

49-
def test_build_all_rulesets_config_abstract_rule_changed
49+
def test_build_all_rulesets_config_internal_abstract_class_changed
50+
# AbstractJavaRulechainRule is not a rule but an abstract base class for real rules. It lives
51+
# in an internal package. "internal" is not a valid ruleset category.
5052
diff_filenames = <<~DOC
5153
pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/AbstractJavaRulechainRule.java
5254
DOC
@@ -56,6 +58,18 @@ def test_build_all_rulesets_config_abstract_rule_changed
5658
"File #{RuleSetBuilder::PATH_TO_DYNAMIC_CONFIG} must not exist")
5759
end
5860

61+
def test_build_all_rulesets_config_abstract_class_changed
62+
# AbstractNamingConventionRule is not a rule despite its name, it an abstract base class for real rules.
63+
# It lives in a normal category package. "codestyle" is a valid ruleset category.
64+
diff_filenames = <<~DOC
65+
pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/AbstractNamingConventionRule.java
66+
DOC
67+
mock_build?(diff_filenames, nil, "#{PATH_TO_TEST_RESOURCES}/patch-ruleset.xml")
68+
69+
assert(!File.exist?(RuleSetBuilder::PATH_TO_DYNAMIC_CONFIG),
70+
"File #{RuleSetBuilder::PATH_TO_DYNAMIC_CONFIG} must not exist")
71+
end
72+
5973
def test_build_all_rulesets_config
6074
diff_filenames = <<~DOC
6175
pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/bestpractices/AvoidGlobalModifierRule.java
@@ -93,6 +107,17 @@ def test_filter_ruleset_single_rule
93107
assert_equal(expected, actual)
94108
end
95109

110+
def test_filter_ruleset_single_rule_named_abstract
111+
diff_filenames = <<~DOC
112+
pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AbstractClassWithoutAbstractMethodRule.java
113+
DOC
114+
mock_build?(diff_filenames, Set['java/bestpractices.xml/AbstractClassWithoutAbstractMethod'])
115+
116+
expected = File.read("#{PATH_TO_TEST_RESOURCES}/expected-abstractclasswithoutabstractmethod.xml")
117+
actual = File.read(RuleSetBuilder::PATH_TO_DYNAMIC_CONFIG)
118+
assert_equal(expected, actual)
119+
end
120+
96121
def test_filter_ruleset_single_rule_and_category
97122
diff_filenames = <<~DOC
98123
pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/design/NcssCountRule.java

0 commit comments

Comments
 (0)