pmdtester 1.1.2 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
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