pmdtester 1.1.0 → 1.3.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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/.ci/build.sh +85 -77
  3. data/.ci/inc/fetch_ci_scripts.bash +19 -0
  4. data/.ci/manual-integration-tests.sh +37 -0
  5. data/.github/workflows/build.yml +26 -9
  6. data/.github/workflows/manual-integration-tests.yml +48 -0
  7. data/.hoerc +1 -1
  8. data/.rubocop.yml +3 -0
  9. data/History.md +44 -0
  10. data/Manifest.txt +6 -1
  11. data/README.rdoc +23 -18
  12. data/Rakefile +1 -1
  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 +15 -5
  17. data/lib/pmdtester/builders/project_builder.rb +14 -9
  18. data/lib/pmdtester/builders/project_hasher.rb +44 -42
  19. data/lib/pmdtester/builders/rule_set_builder.rb +4 -0
  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 +0 -2
  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_report_detail.rb +1 -1
  26. data/lib/pmdtester/pmd_tester_utils.rb +5 -2
  27. data/lib/pmdtester/project.rb +24 -11
  28. data/lib/pmdtester/report_diff.rb +36 -10
  29. data/lib/pmdtester/runner.rb +2 -1
  30. data/lib/pmdtester/semver.rb +36 -0
  31. data/lib/pmdtester.rb +2 -1
  32. data/pmdtester.gemspec +8 -8
  33. data/resources/css/pmd-tester.css +18 -1
  34. data/resources/js/code-snippets.js +62 -22
  35. data/resources/js/project-report.js +6 -4
  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 +15 -10
  40. data/.ci/files/env.gpg +0 -1
@@ -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
@@ -31,9 +31,10 @@ module PmdTester
31
31
 
32
32
  raise "Wrong branch #{get_last_commit_sha}" unless build_branch_sha == get_last_commit_sha
33
33
 
34
+ distro_path = saved_distro_path(build_branch_sha)
34
35
  logger.debug "#{@pmd_branch_name}: PMD Version is #{@pmd_version} " \
35
36
  "(sha=#{build_branch_sha})"
36
- distro_path = saved_distro_path(build_branch_sha)
37
+ logger.debug "#{@pmd_branch_name}: distro_path=#{distro_path}"
37
38
  if File.directory?(distro_path)
38
39
  logger.info "#{@pmd_branch_name}: Skipping packaging - saved version exists " \
39
40
  " in #{distro_path}"
@@ -53,7 +54,9 @@ module PmdTester
53
54
  # in CI there might have been a build performed already. In that case
54
55
  # we reuse the build result, otherwise we build PMD freshly
55
56
  pmd_dist_target = "pmd-dist/target/pmd-bin-#{@pmd_version}.zip"
56
- if File.exist?("#{@local_git_repo}/#{pmd_dist_target}")
57
+ binary_exists = File.exist?(pmd_dist_target)
58
+ logger.debug "#{@pmd_branch_name}: Does the file #{pmd_dist_target} exist? #{binary_exists} (cwd: #{Dir.getwd})"
59
+ if binary_exists
57
60
  # that's a warning, because we don't know, whether this build really
58
61
  # belongs to the current branch or whether it's from a previous branch.
59
62
  # In CI, that's not a problem, because the workspace is always fresh.
@@ -65,7 +68,8 @@ module PmdTester
65
68
  ' -Dmaven.javadoc.skip=true' \
66
69
  ' -Dmaven.source.skip=true' \
67
70
  ' -Dcheckstyle.skip=true' \
68
- ' -T1C'
71
+ ' -Dpmd.skip=true' \
72
+ ' -T1C -B'
69
73
  Cmd.execute(package_cmd)
70
74
  end
71
75
 
@@ -93,11 +97,12 @@ module PmdTester
93
97
  def generate_pmd_report(project)
94
98
  error_recovery_options = @error_recovery ? 'PMD_JAVA_OPTS="-Dpmd.error_recovery -ea" ' : ''
95
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'
96
101
  pmd_cmd = "#{error_recovery_options}" \
97
102
  "#{run_path} pmd -d #{project.local_source_path} -f xml " \
98
103
  "-R #{project.get_config_path(@pmd_branch_name)} " \
99
104
  "-r #{project.get_pmd_report_path(@pmd_branch_name)} " \
100
- "-failOnViolation false -t #{@threads} " \
105
+ "#{fail_on_violation} -t #{@threads} " \
101
106
  "#{project.auxclasspath}"
102
107
  start_time = Time.now
103
108
  if File.exist?(project.get_pmd_report_path(@pmd_branch_name))
@@ -115,7 +120,7 @@ module PmdTester
115
120
  doc = Nokogiri::XML(File.read(@branch_config))
116
121
  ruleset = doc.at_css('ruleset')
117
122
  ruleset.add_child("\n")
118
- project.exclude_pattern.each do |exclude_pattern|
123
+ project.exclude_patterns.each do |exclude_pattern|
119
124
  ruleset.add_child(" <exclude-pattern>#{exclude_pattern}</exclude-pattern>\n")
120
125
  end
121
126
 
@@ -190,5 +195,10 @@ module PmdTester
190
195
  def wd_has_dirty_git_changes
191
196
  !Cmd.execute('git status --porcelain').empty?
192
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
193
203
  end
194
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
@@ -9,9 +9,9 @@ module PmdTester
9
9
 
10
10
  def report_diff_to_h(rdiff)
11
11
  {
12
- 'violation_counts' => rdiff.violation_counts.to_h,
13
- 'error_counts' => rdiff.error_counts.to_h,
14
- 'configerror_counts' => rdiff.configerror_counts.to_h,
12
+ 'violation_counts' => rdiff.violation_counts.to_h.transform_keys(&:to_s),
13
+ 'error_counts' => rdiff.error_counts.to_h.transform_keys(&:to_s),
14
+ 'configerror_counts' => rdiff.configerror_counts.to_h.transform_keys(&:to_s),
15
15
 
16
16
  'base_execution_time' => PmdReportDetail.convert_seconds(rdiff.base_report.exec_time),
17
17
  'patch_execution_time' => PmdReportDetail.convert_seconds(rdiff.patch_report.exec_time),
@@ -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
@@ -33,6 +33,10 @@ module PmdTester
33
33
  rule_refs
34
34
  end
35
35
 
36
+ def calculate_filter_set
37
+ output_filter_set(ALL_CATEGORIES)
38
+ end
39
+
36
40
  def output_filter_set(rule_refs)
37
41
  if rule_refs == ALL_CATEGORIES
38
42
  if @options.mode == Options::ONLINE
@@ -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
@@ -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,
@@ -27,7 +27,7 @@ module PmdTester
27
27
  hash = JSON.parse(File.read(report_info_path), symbolize_names: true)
28
28
  PmdReportDetail.new(**hash)
29
29
  else
30
- puts "#{report_info_path} doesn't exist"
30
+ PmdTester.logger.warn("#{report_info_path} doesn't exist")
31
31
  PmdReportDetail.new
32
32
  end
33
33
  end
@@ -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
@@ -39,13 +40,15 @@ module PmdTester
39
40
  def compute_project_diffs(projects, base_branch, patch_branch, filter_set = nil)
40
41
  projects.each do |project|
41
42
  logger.info "Preparing report for #{project.name}"
43
+ logger.info " with filter #{filter_set}" unless filter_set.nil?
42
44
  project.compute_report_diff(base_branch, patch_branch, filter_set)
43
45
  end
44
46
  end
45
47
 
46
48
  # Build the diff reports and write them all
47
- def build_html_reports(projects, base_branch_details, patch_branch_details)
48
- compute_project_diffs(projects, base_branch_details.branch_name, patch_branch_details.branch_name)
49
+ def build_html_reports(projects, base_branch_details, patch_branch_details, filter_set = nil)
50
+ compute_project_diffs(projects, base_branch_details.branch_name, patch_branch_details.branch_name,
51
+ filter_set)
49
52
 
50
53
  SummaryReportBuilder.new.write_all_projects(projects,
51
54
  base_branch_details,
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'pathname'
4
+
3
5
  module PmdTester
4
6
  # This class represents all the information about the project
5
7
  class Project
@@ -12,7 +14,8 @@ module PmdTester
12
14
  attr_reader :connection
13
15
  attr_reader :webview_url
14
16
  attr_reader :tag
15
- attr_reader :exclude_pattern
17
+ attr_reader :exclude_patterns
18
+ attr_reader :src_subpath
16
19
  attr_accessor :report_diff
17
20
  # key: pmd branch name as String => value: local path of pmd report
18
21
  attr_reader :build_command
@@ -20,7 +23,7 @@ module PmdTester
20
23
  # stores the auxclasspath calculated after cloning/preparing the project
21
24
  attr_accessor :auxclasspath
22
25
 
23
- def initialize(project)
26
+ def initialize(project) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
24
27
  @name = project.at_xpath('name').text
25
28
  @type = project.at_xpath('type').text
26
29
  @connection = project.at_xpath('connection').text
@@ -31,9 +34,10 @@ module PmdTester
31
34
  @webview_url = default_webview_url
32
35
  @webview_url = webview_url_element.text unless webview_url_element.nil?
33
36
 
34
- @exclude_pattern = []
37
+ @src_subpath = project.at_xpath('src-subpath')&.text || '.'
38
+ @exclude_patterns = []
35
39
  project.xpath('exclude-pattern').each do |ep|
36
- @exclude_pattern.push(ep.text)
40
+ @exclude_patterns.push(ep.text)
37
41
  end
38
42
 
39
43
  @build_command = project.at_xpath('build-command')&.text
@@ -56,17 +60,17 @@ module PmdTester
56
60
  # Change the file path from 'LOCAL_DIR/SOURCE_CODE_PATH' to
57
61
  # 'WEB_VIEW_URL/SOURCE_CODE_PATH'
58
62
  def get_webview_url(file_path)
59
- file_path.gsub(%r{/#{local_source_path}}, @webview_url)
63
+ file_path.gsub(%r{/#{clone_root_path}}, @webview_url)
60
64
  end
61
65
 
62
66
  # Change the file path from 'LOCAL_DIR/SOURCE_CODE_PATH' to
63
67
  # 'PROJECT_NAME/SOURCE_CODE_PATH'
64
68
  def get_path_inside_project(file_path)
65
- file_path.gsub(%r{/#{local_source_path}}, @name)
69
+ file_path.gsub(%r{/#{clone_root_path}}, @name)
66
70
  end
67
71
 
68
72
  def get_local_path(file_path)
69
- file_path.sub(%r{/#{local_source_path}/}, '')
73
+ file_path.sub(%r{/#{clone_root_path}/}, '')
70
74
  end
71
75
 
72
76
  def get_pmd_report_path(branch_name)
@@ -93,6 +97,19 @@ module PmdTester
93
97
  end
94
98
  end
95
99
 
100
+ ##
101
+ # Path to the sources to analyze (below or equal to clone_root_path)
102
+ def local_source_path
103
+ # normalize path
104
+ Pathname.new("#{clone_root_path}/#{src_subpath}").cleanpath
105
+ end
106
+
107
+ ##
108
+ # Path to the clone directory
109
+ def clone_root_path
110
+ "#{REPOSITORIES_PATH}/#{@name}"
111
+ end
112
+
96
113
  def get_project_target_dir(branch_name)
97
114
  branch_filename = PmdBranchDetail.branch_filename(branch_name)
98
115
  dir = "target/reports/#{branch_filename}/#{@name}"
@@ -100,10 +117,6 @@ module PmdTester
100
117
  dir
101
118
  end
102
119
 
103
- def local_source_path
104
- "#{REPOSITORIES_PATH}/#{@name}"
105
- end
106
-
107
120
  def compute_report_diff(base_branch, patch_branch, filter_set)
108
121
  self.report_diff = build_report_diff(get_pmd_report_path(base_branch),
109
122
  get_pmd_report_path(patch_branch),
@@ -26,13 +26,17 @@ module PmdTester
26
26
 
27
27
  def to_h
28
28
  {
29
- 'changed' => changed,
30
- 'new' => new,
31
- 'removed' => removed,
32
- 'base_total' => base_total,
33
- 'patch_total' => patch_total
29
+ changed: changed,
30
+ new: new,
31
+ removed: removed,
32
+ base_total: base_total,
33
+ patch_total: patch_total
34
34
  }
35
35
  end
36
+
37
+ def to_s
38
+ "RunningDiffCounters[#{to_h}]"
39
+ end
36
40
  end
37
41
 
38
42
  # Simple info about a rule, collected by the report xml parser
@@ -53,35 +57,57 @@ module PmdTester
53
57
  :configerrors_by_rule,
54
58
  :exec_time,
55
59
  :timestamp,
56
- :infos_by_rule
60
+ :file
57
61
 
58
62
  def initialize(report_document: nil,
63
+ file: '',
59
64
  exec_time: 0,
60
65
  timestamp: '0')
61
66
  initialize_empty
62
67
  initialize_with_report_document report_document unless report_document.nil?
63
68
  @exec_time = exec_time
64
69
  @timestamp = timestamp
70
+ @file = file
65
71
  end
66
72
 
67
73
  def self.empty
68
74
  new
69
75
  end
70
76
 
77
+ def rule_summaries
78
+ summary = {}
79
+ @violations_by_file.each_value do |violation|
80
+ unless summary.key?(violation.rule_name)
81
+ summary[violation.rule_name] = {
82
+ 'name' => violation.rule_name,
83
+ 'info_url' => violation.info_url,
84
+ 'count' => 0
85
+ }
86
+ end
87
+ summary[violation.rule_name]['count'] += 1
88
+ end
89
+
90
+ summary.values
91
+ end
92
+
71
93
  private
72
94
 
73
95
  def initialize_with_report_document(report_document)
74
96
  @violations_by_file = report_document.violations
75
97
  @errors_by_file = report_document.errors
76
98
  @configerrors_by_rule = report_document.configerrors
77
- @infos_by_rule = report_document.infos_by_rules
99
+
100
+ PmdTester.logger.debug("Loaded #{@violations_by_file.total_size} violations " \
101
+ "in #{@violations_by_file.num_files} files")
102
+ PmdTester.logger.debug("Loaded #{@errors_by_file.total_size} errors " \
103
+ "in #{@errors_by_file.num_files} files")
104
+ PmdTester.logger.debug("Loaded #{@configerrors_by_rule.size} config errors")
78
105
  end
79
106
 
80
107
  def initialize_empty
81
108
  @violations_by_file = CollectionByFile.new
82
109
  @errors_by_file = CollectionByFile.new
83
110
  @configerrors_by_rule = {}
84
- @infos_by_rule = {}
85
111
  end
86
112
  end
87
113
 
@@ -112,7 +138,7 @@ module PmdTester
112
138
  @error_diffs_by_file = {}
113
139
  @configerror_diffs_by_rule = {}
114
140
 
115
- @rule_infos_union = base_report.infos_by_rule.dup
141
+ @rule_infos_union = {}
116
142
  @base_report = base_report
117
143
  @patch_report = patch_report
118
144
 
@@ -125,7 +151,7 @@ module PmdTester
125
151
  {
126
152
  'name' => rule,
127
153
  'info_url' => @rule_infos_union[rule].info_url,
128
- **counters.to_h
154
+ **counters.to_h.transform_keys(&:to_s)
129
155
  }
130
156
  end
131
157
  end
@@ -59,12 +59,13 @@ module PmdTester
59
59
  @options.patch_config = "#{baseline_path}/config.xml"
60
60
  else
61
61
  logger.info "Using config #{@options.patch_config} which might differ from baseline"
62
+ RuleSetBuilder.new(@options).calculate_filter_set if @options.filter_with_patch_config
62
63
  end
63
64
 
64
65
  patch_branch_details = create_pmd_report(config: @options.patch_config, branch: @options.patch_branch)
65
66
 
66
67
  base_branch_details = PmdBranchDetail.load(@options.base_branch, logger)
67
- build_html_reports(@projects, base_branch_details, patch_branch_details)
68
+ build_html_reports(@projects, base_branch_details, patch_branch_details, @options.filter_set)
68
69
  end
69
70
 
70
71
  def determine_project_list_for_online_mode(baseline_path)