pmdtester 1.1.2 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.ci/build.sh +87 -55
  3. data/.ci/inc/fetch_ci_scripts.bash +19 -0
  4. data/.ci/manual-integration-tests.sh +31 -14
  5. data/.github/workflows/build.yml +26 -9
  6. data/.github/workflows/manual-integration-tests.yml +24 -8
  7. data/.hoerc +1 -1
  8. data/.rubocop.yml +3 -0
  9. data/History.md +31 -0
  10. data/Manifest.txt +4 -2
  11. data/README.rdoc +26 -25
  12. data/Rakefile +11 -6
  13. data/config/project-list.xml +8 -7
  14. data/config/projectlist_1_2_0.xsd +39 -0
  15. data/lib/pmdtester/builders/liquid_renderer.rb +60 -3
  16. data/lib/pmdtester/builders/pmd_report_builder.rb +9 -3
  17. data/lib/pmdtester/builders/project_builder.rb +14 -9
  18. data/lib/pmdtester/builders/project_hasher.rb +41 -39
  19. data/lib/pmdtester/builders/rule_set_builder.rb +116 -75
  20. data/lib/pmdtester/builders/summary_report_builder.rb +1 -0
  21. data/lib/pmdtester/parsers/options.rb +5 -0
  22. data/lib/pmdtester/parsers/pmd_report_document.rb +1 -3
  23. data/lib/pmdtester/parsers/projects_parser.rb +1 -1
  24. data/lib/pmdtester/pmd_branch_detail.rb +6 -0
  25. data/lib/pmdtester/pmd_tester_utils.rb +1 -0
  26. data/lib/pmdtester/pmd_violation.rb +11 -1
  27. data/lib/pmdtester/project.rb +24 -11
  28. data/lib/pmdtester/report_diff.rb +20 -4
  29. data/lib/pmdtester/runner.rb +8 -3
  30. data/lib/pmdtester/semver.rb +36 -0
  31. data/lib/pmdtester.rb +2 -1
  32. data/pmdtester.gemspec +18 -18
  33. data/resources/css/pmd-tester.css +17 -1
  34. data/resources/js/code-snippets.js +48 -15
  35. data/resources/js/project-report.js +5 -3
  36. data/resources/project_diff_report.html +9 -0
  37. data/resources/project_index.html +11 -0
  38. data/resources/project_pmd_report.html +186 -0
  39. metadata +26 -30
  40. data/.ci/files/env.gpg +0 -1
  41. data/.ci/inc/install-openjdk.inc +0 -26
@@ -57,17 +57,74 @@ module PmdTester
57
57
  write_file("#{root}/index.html", render_liquid('project_diff_report.html', liquid_env))
58
58
  # generate array of violations in json
59
59
  write_file("#{root}/project_data.js", dump_violations_json(project))
60
+ # copy original pmd reports
61
+ copy_file("#{root}/base_pmd_report.xml", project.report_diff.base_report.file)
62
+ copy_file("#{root}/patch_pmd_report.xml", project.report_diff.patch_report.file)
63
+ # render full pmd reports
64
+ write_file("#{root}/base_pmd_report.html",
65
+ render_liquid('project_pmd_report.html', pmd_report_liquid_env(project, BASE)))
66
+ write_file("#{root}/base_data.js", dump_violations_json(project, BASE))
67
+ write_file("#{root}/patch_pmd_report.html",
68
+ render_liquid('project_pmd_report.html', pmd_report_liquid_env(project, PATCH)))
69
+ write_file("#{root}/patch_data.js", dump_violations_json(project, PATCH))
60
70
  end
61
71
 
62
- def dump_violations_json(project)
72
+ def dump_violations_json(project, branch = 'diff')
73
+ violations_by_file = if branch == BASE
74
+ project.report_diff.base_report.violations_by_file.to_h
75
+ elsif branch == PATCH
76
+ project.report_diff.patch_report.violations_by_file.to_h
77
+ else
78
+ project.report_diff.violation_diffs_by_file
79
+ end
80
+
63
81
  h = {
64
82
  'source_link_base' => project.webview_url,
65
83
  'source_link_template' => link_template(project),
66
- **violations_to_hash(project)
84
+ **violations_to_hash(project, violations_by_file, branch == 'diff')
67
85
  }
68
86
 
69
- project_data = JSON.fast_generate(h)
87
+ project_data = JSON.fast_generate(h, indent: ' ', object_nl: "\n", array_nl: "\n")
70
88
  "let project = #{project_data}"
71
89
  end
90
+
91
+ private
92
+
93
+ def copy_file(target_file, source_file)
94
+ if File.exist? source_file
95
+ FileUtils.cp(source_file, target_file)
96
+ logger&.info "Written #{target_file}"
97
+ else
98
+ logger&.warn "File #{source_file} not found"
99
+ end
100
+ end
101
+
102
+ def pmd_report_liquid_env(project, branch)
103
+ report = if branch == BASE
104
+ project.report_diff.base_report
105
+ else
106
+ project.report_diff.patch_report
107
+ end
108
+ {
109
+ 'project_name' => project.name,
110
+ 'branch' => branch,
111
+ 'report' => report_to_h(project, report)
112
+ }
113
+ end
114
+
115
+ def report_to_h(project, report)
116
+ {
117
+ 'violation_counts' => report.violations_by_file.total_size,
118
+ 'error_counts' => report.errors_by_file.total_size,
119
+ 'configerror_counts' => report.configerrors_by_rule.values.flatten.length,
120
+
121
+ 'execution_time' => PmdReportDetail.convert_seconds(report.exec_time),
122
+ 'timestamp' => report.timestamp,
123
+
124
+ 'rules' => report.rule_summaries,
125
+ 'errors' => report.errors_by_file.all_values.map { |e| error_to_hash(e, project) },
126
+ 'configerrors' => report.configerrors_by_rule.values.flatten.map { |e| configerror_to_hash(e) }
127
+ }
128
+ end
72
129
  end
73
130
  end
@@ -69,7 +69,7 @@ module PmdTester
69
69
  ' -Dmaven.source.skip=true' \
70
70
  ' -Dcheckstyle.skip=true' \
71
71
  ' -Dpmd.skip=true' \
72
- ' -T1C'
72
+ ' -T1C -B'
73
73
  Cmd.execute(package_cmd)
74
74
  end
75
75
 
@@ -97,11 +97,12 @@ module PmdTester
97
97
  def generate_pmd_report(project)
98
98
  error_recovery_options = @error_recovery ? 'PMD_JAVA_OPTS="-Dpmd.error_recovery -ea" ' : ''
99
99
  run_path = "#{saved_distro_path(@pmd_branch_details.branch_last_sha)}/bin/run.sh"
100
+ fail_on_violation = should_use_long_cli_options ? '--fail-on-violation false' : '-failOnViolation false'
100
101
  pmd_cmd = "#{error_recovery_options}" \
101
102
  "#{run_path} pmd -d #{project.local_source_path} -f xml " \
102
103
  "-R #{project.get_config_path(@pmd_branch_name)} " \
103
104
  "-r #{project.get_pmd_report_path(@pmd_branch_name)} " \
104
- "-failOnViolation false -t #{@threads} " \
105
+ "#{fail_on_violation} -t #{@threads} " \
105
106
  "#{project.auxclasspath}"
106
107
  start_time = Time.now
107
108
  if File.exist?(project.get_pmd_report_path(@pmd_branch_name))
@@ -119,7 +120,7 @@ module PmdTester
119
120
  doc = Nokogiri::XML(File.read(@branch_config))
120
121
  ruleset = doc.at_css('ruleset')
121
122
  ruleset.add_child("\n")
122
- project.exclude_pattern.each do |exclude_pattern|
123
+ project.exclude_patterns.each do |exclude_pattern|
123
124
  ruleset.add_child(" <exclude-pattern>#{exclude_pattern}</exclude-pattern>\n")
124
125
  end
125
126
 
@@ -194,5 +195,10 @@ module PmdTester
194
195
  def wd_has_dirty_git_changes
195
196
  !Cmd.execute('git status --porcelain').empty?
196
197
  end
198
+
199
+ def should_use_long_cli_options
200
+ logger.debug "PMD Version: #{@pmd_version}"
201
+ Semver.compare(@pmd_version, '6.41.0') >= 0
202
+ end
197
203
  end
198
204
  end
@@ -17,11 +17,19 @@ module PmdTester
17
17
 
18
18
  @projects.each do |project|
19
19
  logger.info "Start cloning #{project.name} repository"
20
- path = project.local_source_path
21
- clone_cmd = "#{project.type} clone #{project.connection} #{path}"
20
+ path = project.clone_root_path
21
+
22
22
  if File.exist?(path)
23
23
  logger.warn "Skipping clone, project path #{path} already exists"
24
24
  else
25
+ raise "Unsupported project type '#{project.type}' - only git is supported" unless project.type == 'git'
26
+
27
+ # git:
28
+ # Don't download whole history
29
+ # Note we don't use --single-branch, because the repo is downloaded
30
+ # once but may be used with several tags.
31
+ clone_cmd = "git clone --no-single-branch --depth 1 #{project.connection} #{path}"
32
+
25
33
  Cmd.execute(clone_cmd)
26
34
  end
27
35
 
@@ -38,7 +46,7 @@ module PmdTester
38
46
  logger.info 'Building projects started'
39
47
 
40
48
  @projects.each do |project|
41
- path = project.local_source_path
49
+ path = project.clone_root_path
42
50
  Dir.chdir(path) do
43
51
  progress_logger = SimpleProgressLogger.new("building #{project.name} in #{path}")
44
52
  progress_logger.start
@@ -87,12 +95,9 @@ module PmdTester
87
95
  end
88
96
 
89
97
  def execute_reset_cmd(type, tag)
90
- case type
91
- when 'git'
92
- reset_cmd = "git checkout #{tag}; git reset --hard #{tag}"
93
- when 'hg'
94
- reset_cmd = "hg up #{tag}"
95
- end
98
+ raise "Unsupported project type '#{type}' - only git is supported" unless type == 'git'
99
+
100
+ reset_cmd = "git checkout #{tag}; git reset --hard #{tag}"
96
101
 
97
102
  Cmd.execute(reset_cmd)
98
103
  end
@@ -25,24 +25,14 @@ module PmdTester
25
25
  }
26
26
  end
27
27
 
28
- def errors_to_h(project)
29
- errors = project.report_diff.error_diffs_by_file.values.flatten
30
- errors.map { |e| error_to_hash(e, project) }
31
- end
32
-
33
- def configerrors_to_h(project)
34
- configerrors = project.report_diff.configerror_diffs_by_rule.values.flatten
35
- configerrors.map { |e| configerror_to_hash(e) }
36
- end
37
-
38
- def violations_to_hash(project)
28
+ def violations_to_hash(project, violations_by_file, is_diff)
39
29
  filename_index = []
40
30
  all_vs = []
41
- project.report_diff.violation_diffs_by_file.each do |file, vs|
31
+ violations_by_file.each do |file, vs|
42
32
  file_ref = filename_index.size
43
33
  filename_index.push(project.get_local_path(file))
44
34
  vs.each do |v|
45
- all_vs.push(make_violation_hash(file_ref, v))
35
+ all_vs.push(make_violation_hash(file_ref, v, is_diff))
46
36
  end
47
37
  end
48
38
 
@@ -52,36 +42,19 @@ module PmdTester
52
42
  }
53
43
  end
54
44
 
55
- def link_template(project)
56
- l_str = project.type == 'git' ? 'L' : 'l'
57
- "#{project.webview_url}/{file}##{l_str}{line}"
58
- end
59
-
60
- def violation_type(violation)
61
- if violation.changed?
62
- '~'
63
- elsif violation.branch == 'patch'
64
- '+'
65
- else
66
- '-'
67
- end
45
+ def errors_to_h(project)
46
+ errors = project.report_diff.error_diffs_by_file.values.flatten
47
+ errors.map { |e| error_to_hash(e, project) }
68
48
  end
69
49
 
70
- def make_violation_hash(file_ref, violation)
71
- h = {
72
- 't' => violation_type(violation),
73
- 'l' => violation.line,
74
- 'f' => file_ref,
75
- 'r' => violation.rule_name,
76
- 'm' => violation.changed? ? diff_fragments(violation) : violation.message
77
- }
78
- h['ol'] = violation.old_line if violation.changed? && violation.line != violation.old_line
79
- h
50
+ def configerrors_to_h(project)
51
+ configerrors = project.report_diff.configerror_diffs_by_rule.values.flatten
52
+ configerrors.map { |e| configerror_to_hash(e) }
80
53
  end
81
54
 
82
- def diff_fragments(violation)
83
- diff = Differ.diff_by_word(violation.message, violation.old_message)
84
- diff.format_as(:html)
55
+ def link_template(project)
56
+ l_str = project.type == 'git' ? 'L' : 'l'
57
+ "#{project.webview_url}/{file}##{l_str}{line}"
85
58
  end
86
59
 
87
60
  def error_to_hash(error, project)
@@ -122,5 +95,34 @@ module PmdTester
122
95
  'added'
123
96
  end
124
97
  end
98
+
99
+ private
100
+
101
+ def violation_type(violation)
102
+ if violation.changed?
103
+ '~'
104
+ elsif violation.branch == PATCH
105
+ '+'
106
+ else
107
+ '-'
108
+ end
109
+ end
110
+
111
+ def make_violation_hash(file_ref, violation, is_diff = TRUE)
112
+ h = {
113
+ 't' => is_diff ? violation_type(violation) : '+',
114
+ 'l' => violation.line,
115
+ 'f' => file_ref,
116
+ 'r' => violation.rule_name,
117
+ 'm' => is_diff && violation.changed? ? diff_fragments(violation) : violation.message
118
+ }
119
+ h['ol'] = violation.old_line if is_diff && violation.changed? && violation.line != violation.old_line
120
+ h
121
+ end
122
+
123
+ def diff_fragments(violation)
124
+ diff = Differ.diff_by_word(violation.message, violation.old_message)
125
+ diff.format_as(:html)
126
+ end
125
127
  end
126
128
  end
@@ -9,69 +9,70 @@ module PmdTester
9
9
  # Attention: we only consider java rulesets now.
10
10
  class RuleSetBuilder
11
11
  include PmdTester
12
- ALL_CATEGORIES = Set['bestpractices.xml', 'codestyle.xml', 'design.xml', 'documentation.xml',
13
- 'errorprone.xml', 'multithreading.xml', 'performance.xml',
14
- 'security.xml'].freeze
15
- PATH_TO_PMD_JAVA_BASED_RULES =
16
- 'pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule'
17
- PATH_TO_PMD_XPATH_BASED_RULES = 'pmd-java/src/main/resources/category/java'
18
- PATH_TO_ALL_JAVA_RULES =
19
- ResourceLocator.locate('config/all-java.xml')
20
12
  PATH_TO_DYNAMIC_CONFIG = 'target/dynamic-config.xml'
21
- NO_JAVA_RULES_CHANGED_MESSAGE = 'No java rules have been changed!'
13
+ NO_RULES_CHANGED_MESSAGE = 'No regression tested rules have been changed!'
22
14
 
23
15
  def initialize(options)
24
16
  @options = options
25
17
  end
26
18
 
27
- def build
28
- filenames = diff_filenames
29
- rule_refs = get_rule_refs(filenames)
30
- output_filter_set(rule_refs)
31
- build_config_file(rule_refs)
32
- logger.debug "Dynamic configuration: #{[rule_refs]}"
33
- rule_refs
19
+ #
20
+ # Creates a dynamic ruleset based on the changed sources.
21
+ # Returns true, when rules are affected by the changed sources.
22
+ # Returns false, when no rules are affected and regression tester can be skipped.
23
+ #
24
+ def build?
25
+ languages = determine_languages
26
+ filenames = diff_filenames(languages)
27
+ run_required, rule_refs = get_rule_refs(filenames)
28
+ if run_required
29
+ output_filter_set(rule_refs)
30
+ build_config_file(rule_refs)
31
+ logger.debug "Dynamic configuration: #{[rule_refs]}"
32
+ else
33
+ logger.info NO_RULES_CHANGED_MESSAGE
34
+ end
35
+ run_required
36
+ end
37
+
38
+ def calculate_filter_set
39
+ output_filter_set([])
34
40
  end
35
41
 
36
42
  def output_filter_set(rule_refs)
37
- if rule_refs == ALL_CATEGORIES
38
- if @options.mode == Options::ONLINE
43
+ if rule_refs.empty?
44
+ if @options.mode == Options::ONLINE && @options.filter_with_patch_config
39
45
  @options.filter_set = Set[]
40
46
  doc = File.open(@options.patch_config) { |f| Nokogiri::XML(f) }
41
47
  rules = doc.css('ruleset rule')
42
48
  rules.each do |r|
43
49
  ref = r.attributes['ref'].content
44
- ref.delete_prefix!('category/java/')
50
+ ref.delete_prefix!('category/')
45
51
  @options.filter_set.add(ref)
46
52
  end
47
53
 
48
- logger.debug "Using filter based on patch config #{@options.patch_config}: " \
54
+ logger.info "Using filter based on patch config #{@options.patch_config}: " \
49
55
  "#{@options.filter_set}"
50
56
  else
51
- # if `rule_refs` contains all categories, then no need to filter the baseline
52
- logger.debug 'No filter when comparing patch to baseline'
57
+ # if `rule_refs` is empty, then no filter can be used when comparing to the baseline
58
+ logger.info 'No filter when comparing patch to baseline'
53
59
  @options.filter_set = nil
54
60
  end
55
61
  else
56
- logger.debug "Filter is now #{rule_refs}"
62
+ logger.info "Filter is now #{rule_refs}"
57
63
  @options.filter_set = rule_refs
58
64
  end
59
65
  end
60
66
 
61
- def diff_filenames
62
- filenames = nil
63
- Dir.chdir(@options.local_git_repo) do
64
- base = @options.base_branch
65
- patch = @options.patch_branch
66
- # We only need to support git here, since PMD's repo is using git.
67
- diff_cmd = "git diff --name-only #{base}..#{patch} -- pmd-core/src/main pmd-java/src/main"
68
- filenames = Cmd.execute(diff_cmd)
69
- end
70
- filenames.split("\n")
71
- end
72
-
67
+ #
68
+ # Determines the rules or category rulesets, that are potentially affected by the change.
69
+ # Returns an empty set, if all rules are affected and there is no
70
+ # filtering possible or if no rules are affected.
71
+ # Whether to run the regression test is returned as an additional boolean flag.
72
+ #
73
73
  def get_rule_refs(filenames)
74
- categories, rules = determine_categories_rules(filenames)
74
+ run_required, categories, rules = determine_categories_rules(filenames)
75
+ logger.debug "Regression test required: #{run_required}"
75
76
  logger.debug "Categories: #{categories}"
76
77
  logger.debug "Rules: #{rules}"
77
78
 
@@ -83,48 +84,11 @@ module PmdTester
83
84
  refs = Set[]
84
85
  refs.merge(categories)
85
86
  refs.merge(rules)
86
- refs
87
- end
88
-
89
- def determine_categories_rules(filenames)
90
- categories = Set[]
91
- rules = Set[]
92
- filenames.each do |filename|
93
- match_data = check_single_filename(filename)
94
-
95
- unless match_data.nil?
96
- if match_data.size == 2
97
- categories.add("#{match_data[1]}.xml")
98
- else
99
- rules.add("#{match_data[1]}.xml/#{match_data[2]}")
100
- end
101
- end
102
-
103
- next unless match_data.nil?
104
-
105
- logger.debug "Change doesn't match specific rule/category - enable all rules"
106
- categories = ALL_CATEGORIES
107
- rules.clear
108
- break
109
- end
110
- [categories, rules]
111
- end
112
-
113
- def check_single_filename(filename)
114
- logger.debug "Checking #{filename}"
115
- match_data = %r{#{PATH_TO_PMD_JAVA_BASED_RULES}/([^/]+)/([^/]+)Rule.java}.match(filename)
116
- match_data = %r{#{PATH_TO_PMD_XPATH_BASED_RULES}/([^/]+).xml}.match(filename) if match_data.nil?
117
- logger.debug "Matches: #{match_data.inspect}"
118
- match_data
87
+ [run_required, refs]
119
88
  end
120
89
 
121
90
  def build_config_file(rule_refs)
122
91
  if rule_refs.empty?
123
- logger.info NO_JAVA_RULES_CHANGED_MESSAGE
124
- return
125
- end
126
-
127
- if rule_refs == ALL_CATEGORIES
128
92
  logger.debug 'All rules are used. Not generating a dynamic ruleset.'
129
93
  logger.debug "Using the configured/default ruleset base_config=#{@options.base_config} "\
130
94
  "patch_config=#{@options.patch_config}"
@@ -143,7 +107,7 @@ module PmdTester
143
107
  'name' => 'Dynamic PmdTester Ruleset') do
144
108
  xml.description 'The ruleset generated by PmdTester dynamically'
145
109
  rule_refs.each do |entry|
146
- xml.rule('ref' => "category/java/#{entry}")
110
+ xml.rule('ref' => "category/#{entry}")
147
111
  end
148
112
  end
149
113
  end
@@ -154,5 +118,82 @@ module PmdTester
154
118
  @options.base_config = PATH_TO_DYNAMIC_CONFIG
155
119
  @options.patch_config = PATH_TO_DYNAMIC_CONFIG
156
120
  end
121
+
122
+ private
123
+
124
+ def determine_categories_rules(filenames)
125
+ regression_test_required = false
126
+ categories = Set[]
127
+ rules = Set[]
128
+ filenames.each do |filename|
129
+ matched = check_single_filename(filename, categories, rules)
130
+ regression_test_required = true if matched
131
+
132
+ next if matched
133
+
134
+ logger.info "Change in file #{filename} doesn't match specific rule/category - enable all rules"
135
+ regression_test_required = true
136
+ categories.clear
137
+ rules.clear
138
+ break
139
+ end
140
+ [regression_test_required, categories, rules]
141
+ end
142
+
143
+ def check_single_filename(filename, categories, rules)
144
+ logger.debug "Checking #{filename}"
145
+
146
+ # matches Java-based rule implementations
147
+ match_data = %r{.+/src/main/java/.+/lang/([^/]+)/rule/([^/]+)/([^/]+)Rule.java}.match(filename)
148
+ unless match_data.nil?
149
+ logger.debug "Matches: #{match_data.inspect}"
150
+ rules.add("#{match_data[1]}/#{match_data[2]}.xml/#{match_data[3]}")
151
+ return true
152
+ end
153
+
154
+ # matches xpath rules
155
+ match_data = %r{.+/src/main/resources/category/([^/]+)/([^/]+).xml}.match(filename)
156
+ unless match_data.nil?
157
+ logger.debug "Matches: #{match_data.inspect}"
158
+ categories.add("#{match_data[1]}/#{match_data[2]}.xml")
159
+ return true
160
+ end
161
+
162
+ false
163
+ end
164
+
165
+ def diff_filenames(languages)
166
+ filenames = nil
167
+ Dir.chdir(@options.local_git_repo) do
168
+ base = @options.base_branch
169
+ patch = @options.patch_branch
170
+
171
+ filepath_filter = ''
172
+ unless languages.empty?
173
+ filepath_filter = '-- pmd-core/src/main'
174
+ languages.each { |l| filepath_filter = "#{filepath_filter} pmd-#{l}/src/main" }
175
+ end
176
+
177
+ # We only need to support git here, since PMD's repo is using git.
178
+ diff_cmd = "git diff --name-only #{base}..#{patch} #{filepath_filter}"
179
+ filenames = Cmd.execute(diff_cmd)
180
+ end
181
+ filenames.split("\n")
182
+ end
183
+
184
+ #
185
+ # Determines all languages, that are part of the regression test.
186
+ # This is based on the configured rules/rulesets.
187
+ #
188
+ def determine_languages
189
+ languages = Set[]
190
+ doc = File.open(@options.patch_config) { |f| Nokogiri::XML(f) }
191
+ rules = doc.css('ruleset rule')
192
+ rules.each do |r|
193
+ ref = r.attributes['ref'].content
194
+ languages.add(ref.split('/')[1])
195
+ end
196
+ languages
197
+ end
157
198
  end
158
199
  end
@@ -80,6 +80,7 @@ module PmdTester
80
80
  'sha' => details.branch_last_sha,
81
81
  'message' => details.branch_last_message
82
82
  },
83
+ 'timestamp' => details.timestamp,
83
84
  'execution_time' => PmdReportDetail.convert_seconds(details.execution_time),
84
85
  'jdk_info' => details.jdk_version,
85
86
  'locale' => details.language,
@@ -29,6 +29,7 @@ module PmdTester
29
29
  attr_reader :threads
30
30
  attr_reader :html_flag
31
31
  attr_reader :auto_config_flag
32
+ attr_reader :filter_with_patch_config
32
33
  attr_reader :debug_flag
33
34
  attr_accessor :filter_set
34
35
  attr_reader :keep_reports
@@ -48,6 +49,7 @@ module PmdTester
48
49
  @threads = options[:t]
49
50
  @html_flag = options[:f]
50
51
  @auto_config_flag = options[:a]
52
+ @filter_with_patch_config = options.filter_with_patch_config?
51
53
  @debug_flag = options[:d]
52
54
  @filter_set = nil
53
55
  @keep_reports = options.keep_reports?
@@ -100,6 +102,9 @@ module PmdTester
100
102
  o.bool '-a', '--auto-gen-config',
101
103
  'whether to generate configurations automatically based on branch differences,' \
102
104
  'this option only works in online and local mode'
105
+ o.bool '--filter-with-patch-config',
106
+ 'whether to use patch config to filter baseline result as if --auto-gen-config ' \
107
+ 'has been used. This option only works in online mode.'
103
108
  o.bool '--keep-reports',
104
109
  'whether to keep old reports and skip running PMD again if possible'
105
110
  o.bool '-d', '--debug',
@@ -8,14 +8,12 @@ module PmdTester
8
8
  attr_reader :violations
9
9
  attr_reader :errors
10
10
  attr_reader :configerrors
11
- attr_reader :infos_by_rules
12
11
 
13
12
  def initialize(branch_name, working_dir, filter_set = nil)
14
13
  @violations = CollectionByFile.new
15
14
  @errors = CollectionByFile.new
16
15
  @configerrors = Hash.new { |hash, key| hash[key] = [] }
17
16
 
18
- @infos_by_rules = {}
19
17
  @current_violations = []
20
18
  @current_violation = nil
21
19
  @current_error = nil
@@ -94,7 +92,7 @@ module PmdTester
94
92
  ruleset_attr = violation.ruleset_name.delete(' ').downcase! << '.xml'
95
93
  return true if @filter_set.include?(ruleset_attr)
96
94
 
97
- rule_ref = "#{ruleset_attr}/#{violation.rule_name}"
95
+ rule_ref = "#{violation.language}/#{ruleset_attr}/#{violation.rule_name}"
98
96
 
99
97
  @filter_set.include?(rule_ref)
100
98
  end
@@ -21,7 +21,7 @@ module PmdTester
21
21
  end
22
22
 
23
23
  def schema_file_path
24
- ResourceLocator.locate('config/projectlist_1_1_0.xsd')
24
+ ResourceLocator.locate('config/projectlist_1_2_0.xsd')
25
25
  end
26
26
  end
27
27
 
@@ -10,6 +10,8 @@ module PmdTester
10
10
  attr_accessor :branch_last_sha
11
11
  attr_accessor :branch_last_message
12
12
  attr_accessor :branch_name
13
+ # Start of the regression report
14
+ attr_accessor :timestamp
13
15
  # The branch's execution time on all standard projects
14
16
  attr_accessor :execution_time
15
17
  attr_accessor :jdk_version
@@ -26,6 +28,7 @@ module PmdTester
26
28
  @branch_name = branch_name
27
29
  branch_filename = PmdBranchDetail.branch_filename(branch_name)
28
30
  @base_branch_dir = "target/reports/#{branch_filename}" unless @branch_name.nil?
31
+ @timestamp = Time.now
29
32
  @execution_time = 0
30
33
  # the result of command 'java -version' is going to stderr
31
34
  @jdk_version = Cmd.stderr_of('java -version')
@@ -42,11 +45,13 @@ module PmdTester
42
45
  details.branch_last_sha = hash['branch_last_sha']
43
46
  details.branch_last_message = hash['branch_last_message']
44
47
  details.branch_name = hash['branch_name']
48
+ details.timestamp = hash['timestamp']
45
49
  details.execution_time = hash['execution_time']
46
50
  details.jdk_version = hash['jdk_version']
47
51
  details.language = hash['language']
48
52
  details.pull_request = hash['pull_request']
49
53
  else
54
+ details.timestamp = Time.now
50
55
  details.jdk_version = ''
51
56
  details.language = ''
52
57
  logger&.warn "#{details.path_to_save_file} doesn't exist!"
@@ -58,6 +63,7 @@ module PmdTester
58
63
  hash = { branch_last_sha: @branch_last_sha,
59
64
  branch_last_message: @branch_last_message,
60
65
  branch_name: @branch_name,
66
+ timestamp: @timestamp,
61
67
  execution_time: @execution_time,
62
68
  jdk_version: @jdk_version,
63
69
  language: @language,
@@ -29,6 +29,7 @@ module PmdTester
29
29
  parser.parse_file(report_file) if File.exist?(report_file)
30
30
  Report.new(
31
31
  report_document: doc,
32
+ file: report_file,
32
33
 
33
34
  timestamp: report_details.timestamp,
34
35
  exec_time: report_details.execution_time
@@ -27,7 +27,7 @@ module PmdTester
27
27
  # </xs:simpleContent>
28
28
  # </xs:complexType>
29
29
 
30
- attr_reader :fname, :info_url, :line, :old_line, :old_message, :rule_name, :ruleset_name
30
+ attr_reader :fname, :info_url, :line, :old_line, :old_message, :rule_name, :ruleset_name, :language
31
31
  attr_accessor :message
32
32
 
33
33
  # rubocop:disable Metrics/ParameterLists
@@ -43,6 +43,8 @@ module PmdTester
43
43
 
44
44
  @ruleset_name = ruleset_name
45
45
 
46
+ @language = determine_language_from_info_url
47
+
46
48
  @changed = false
47
49
  @old_message = nil
48
50
  @old_line = nil
@@ -102,5 +104,13 @@ module PmdTester
102
104
  'changed' => changed?
103
105
  }
104
106
  end
107
+
108
+ private
109
+
110
+ def determine_language_from_info_url
111
+ # @info_url is e.g. http://pmd.sourceforge.net/snapshot/pmd_rules_java_codestyle.html#fielddeclarationsshouldbeatstartofclass
112
+ m = @info_url.match(/pmd_rules_(\w+)_/)
113
+ m[1]
114
+ end
105
115
  end
106
116
  end