pmdtester 1.0.0.pre.beta2 → 1.1.1

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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/.ci/build.sh +67 -0
  3. data/.ci/files/env.gpg +1 -0
  4. data/.ci/inc/install-openjdk.inc +26 -0
  5. data/.ci/manual-integration-tests.sh +20 -0
  6. data/.github/workflows/build.yml +39 -0
  7. data/.github/workflows/manual-integration-tests.yml +32 -0
  8. data/.gitignore +9 -0
  9. data/.hoerc +1 -0
  10. data/.rubocop.yml +21 -2
  11. data/.rubocop_todo.yml +7 -8
  12. data/.ruby-version +1 -0
  13. data/Gemfile +1 -13
  14. data/History.md +108 -0
  15. data/Manifest.txt +60 -0
  16. data/README.rdoc +130 -7
  17. data/Rakefile +31 -17
  18. data/bin/pmdtester +1 -1
  19. data/config/all-java.xml +1 -1
  20. data/config/design.xml +1 -1
  21. data/config/projectlist_1_0_0.xsd +2 -1
  22. data/config/projectlist_1_1_0.xsd +31 -0
  23. data/lib/pmdtester.rb +48 -0
  24. data/lib/pmdtester/builders/liquid_renderer.rb +73 -0
  25. data/lib/pmdtester/builders/pmd_report_builder.rb +133 -64
  26. data/lib/pmdtester/builders/project_builder.rb +100 -0
  27. data/lib/pmdtester/builders/project_hasher.rb +126 -0
  28. data/lib/pmdtester/builders/rule_set_builder.rb +95 -51
  29. data/lib/pmdtester/builders/simple_progress_logger.rb +27 -0
  30. data/lib/pmdtester/builders/summary_report_builder.rb +62 -120
  31. data/lib/pmdtester/cmd.rb +15 -1
  32. data/lib/pmdtester/collection_by_file.rb +55 -0
  33. data/lib/pmdtester/parsers/options.rb +33 -10
  34. data/lib/pmdtester/parsers/pmd_report_document.rb +79 -29
  35. data/lib/pmdtester/parsers/projects_parser.rb +2 -6
  36. data/lib/pmdtester/pmd_branch_detail.rb +36 -13
  37. data/lib/pmdtester/pmd_configerror.rb +62 -0
  38. data/lib/pmdtester/pmd_error.rb +34 -34
  39. data/lib/pmdtester/pmd_report_detail.rb +10 -13
  40. data/lib/pmdtester/pmd_tester_utils.rb +57 -0
  41. data/lib/pmdtester/pmd_violation.rb +66 -26
  42. data/lib/pmdtester/project.rb +28 -25
  43. data/lib/pmdtester/report_diff.rb +194 -70
  44. data/lib/pmdtester/resource_locator.rb +4 -0
  45. data/lib/pmdtester/runner.rb +82 -57
  46. data/pmdtester.gemspec +64 -0
  47. data/resources/_includes/diff_pill_row.html +6 -0
  48. data/resources/css/bootstrap.min.css +7 -0
  49. data/resources/css/datatables.min.css +36 -0
  50. data/resources/css/pmd-tester.css +131 -0
  51. data/resources/js/bootstrap.min.js +7 -0
  52. data/resources/js/code-snippets.js +66 -0
  53. data/resources/js/datatables.min.js +726 -0
  54. data/resources/js/jquery-3.2.1.slim.min.js +4 -0
  55. data/resources/js/jquery.min.js +2 -0
  56. data/resources/js/popper.min.js +5 -0
  57. data/resources/js/project-report.js +136 -0
  58. data/resources/project_diff_report.html +205 -0
  59. data/resources/project_index.html +102 -0
  60. metadata +117 -44
  61. data/.travis.yml +0 -22
  62. data/lib/pmdtester/builders/diff_builder.rb +0 -35
  63. data/lib/pmdtester/builders/diff_report_builder.rb +0 -226
  64. data/lib/pmdtester/builders/html_report_builder.rb +0 -34
  65. data/lib/pmdtester/pmdtester.rb +0 -17
  66. data/resources/css/maven-base.css +0 -155
  67. data/resources/css/maven-theme.css +0 -171
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './pmd_branch_detail'
4
-
5
3
  module PmdTester
6
4
  # This class represents all the information about the project
7
5
  class Project
6
+ include PmdTesterUtils
7
+
8
8
  REPOSITORIES_PATH = 'target/repositories'
9
9
 
10
10
  attr_reader :name
@@ -15,15 +15,17 @@ module PmdTester
15
15
  attr_reader :exclude_pattern
16
16
  attr_accessor :report_diff
17
17
  # key: pmd branch name as String => value: local path of pmd report
18
+ attr_reader :build_command
19
+ attr_reader :auxclasspath_command
20
+ # stores the auxclasspath calculated after cloning/preparing the project
21
+ attr_accessor :auxclasspath
18
22
 
19
23
  def initialize(project)
20
24
  @name = project.at_xpath('name').text
21
25
  @type = project.at_xpath('type').text
22
26
  @connection = project.at_xpath('connection').text
23
27
 
24
- @tag = 'master'
25
- tag_element = project.at_xpath('tag')
26
- @tag = tag_element.text unless tag_element.nil?
28
+ @tag = project.at_xpath('tag')&.text || 'master'
27
29
 
28
30
  webview_url_element = project.at_xpath('webview-url')
29
31
  @webview_url = default_webview_url
@@ -34,6 +36,9 @@ module PmdTester
34
36
  @exclude_pattern.push(ep.text)
35
37
  end
36
38
 
39
+ @build_command = project.at_xpath('build-command')&.text
40
+ @auxclasspath_command = project.at_xpath('auxclasspath-command')&.text
41
+
37
42
  @report_diff = nil
38
43
  end
39
44
 
@@ -60,6 +65,10 @@ module PmdTester
60
65
  file_path.gsub(%r{/#{local_source_path}}, @name)
61
66
  end
62
67
 
68
+ def get_local_path(file_path)
69
+ file_path.sub(%r{/#{local_source_path}/}, '')
70
+ end
71
+
63
72
  def get_pmd_report_path(branch_name)
64
73
  if branch_name.nil?
65
74
  nil
@@ -76,6 +85,14 @@ module PmdTester
76
85
  end
77
86
  end
78
87
 
88
+ def get_config_path(branch_name)
89
+ if branch_name.nil?
90
+ nil
91
+ else
92
+ "#{get_project_target_dir(branch_name)}/config.xml"
93
+ end
94
+ end
95
+
79
96
  def get_project_target_dir(branch_name)
80
97
  branch_filename = PmdBranchDetail.branch_filename(branch_name)
81
98
  dir = "target/reports/#{branch_filename}/#{@name}"
@@ -87,26 +104,12 @@ module PmdTester
87
104
  "#{REPOSITORIES_PATH}/#{@name}"
88
105
  end
89
106
 
90
- def target_diff_report_path
91
- dir = "target/reports/diff/#{@name}"
92
- FileUtils.mkdir_p(dir) unless File.directory?(dir)
93
- dir
94
- end
95
-
96
- def diff_report_index_path
97
- "#{target_diff_report_path}/index.html"
98
- end
99
-
100
- def diff_report_index_ref_path
101
- "./#{name}/index.html"
102
- end
103
-
104
- def diffs_exist?
105
- @report_diff.diffs_exist?
106
- end
107
-
108
- def introduce_new_errors?
109
- @report_diff.introduce_new_errors?
107
+ def compute_report_diff(base_branch, patch_branch, filter_set)
108
+ self.report_diff = build_report_diff(get_pmd_report_path(base_branch),
109
+ get_pmd_report_path(patch_branch),
110
+ get_report_info_path(base_branch),
111
+ get_report_info_path(patch_branch),
112
+ filter_set)
110
113
  end
111
114
  end
112
115
  end
@@ -1,111 +1,235 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PmdTester
4
+ # A bunch of counters to summarize differences
5
+ class RunningDiffCounters
6
+ attr_accessor :changed, :new, :removed, :patch_total, :base_total
7
+
8
+ def initialize(base_total)
9
+ @base_total = base_total
10
+ @patch_total = 0
11
+
12
+ @new = @removed = @changed = 0
13
+ end
14
+
15
+ def changed_total
16
+ new + removed + changed
17
+ end
18
+
19
+ def merge!(other)
20
+ self.changed += other.changed
21
+ self.new += other.new
22
+ self.removed += other.removed
23
+ self.base_total += other.base_total
24
+ self.patch_total += other.patch_total
25
+ end
26
+
27
+ def to_h
28
+ {
29
+ changed: changed,
30
+ new: new,
31
+ removed: removed,
32
+ base_total: base_total,
33
+ patch_total: patch_total
34
+ }
35
+ end
36
+
37
+ def to_s
38
+ "RunningDiffCounters[#{to_h}]"
39
+ end
40
+ end
41
+
42
+ # Simple info about a rule, collected by the report xml parser
43
+ class RuleInfo
44
+ attr_reader :name, :info_url
45
+
46
+ def initialize(name, info_url)
47
+ @name = name
48
+ @info_url = info_url
49
+ end
50
+ end
51
+
52
+ # A full report, created by the report XML parser,
53
+ # can be diffed with another report into a ReportDiff
54
+ class Report
55
+ attr_reader :violations_by_file,
56
+ :errors_by_file,
57
+ :configerrors_by_rule,
58
+ :exec_time,
59
+ :timestamp,
60
+ :infos_by_rule
61
+
62
+ def initialize(report_document: nil,
63
+ exec_time: 0,
64
+ timestamp: '0')
65
+ initialize_empty
66
+ initialize_with_report_document report_document unless report_document.nil?
67
+ @exec_time = exec_time
68
+ @timestamp = timestamp
69
+ end
70
+
71
+ def self.empty
72
+ new
73
+ end
74
+
75
+ private
76
+
77
+ def initialize_with_report_document(report_document)
78
+ @violations_by_file = report_document.violations
79
+ @errors_by_file = report_document.errors
80
+ @configerrors_by_rule = report_document.configerrors
81
+ @infos_by_rule = report_document.infos_by_rules
82
+
83
+ PmdTester.logger.debug("Loaded #{@violations_by_file.total_size} violations " \
84
+ "in #{@violations_by_file.num_files} files")
85
+ PmdTester.logger.debug("Loaded #{@errors_by_file.total_size} errors " \
86
+ "in #{@errors_by_file.num_files} files")
87
+ PmdTester.logger.debug("Loaded #{@configerrors_by_rule.size} config errors")
88
+ end
89
+
90
+ def initialize_empty
91
+ @violations_by_file = CollectionByFile.new
92
+ @errors_by_file = CollectionByFile.new
93
+ @configerrors_by_rule = {}
94
+ @infos_by_rule = {}
95
+ end
96
+ end
97
+
4
98
  # This class represents all the diff report information,
5
99
  # including the summary information of the original pmd reports,
6
100
  # as well as the specific information of the diff report.
7
101
  class ReportDiff
8
- attr_accessor :base_violations_size
9
- attr_accessor :patch_violations_size
10
- attr_accessor :violation_diffs_size
11
-
12
- attr_accessor :base_errors_size
13
- attr_accessor :patch_errors_size
14
- attr_accessor :error_diffs_size
102
+ include PmdTester
15
103
 
16
- attr_accessor :base_execution_time
17
- attr_accessor :patch_execution_time
18
- attr_accessor :diff_execution_time
104
+ attr_reader :error_counts
105
+ attr_reader :violation_counts
106
+ attr_reader :configerror_counts
19
107
 
20
- attr_accessor :base_timestamp
21
- attr_accessor :patch_timestamp
108
+ attr_accessor :violation_diffs_by_file
109
+ attr_accessor :error_diffs_by_file
110
+ attr_accessor :configerror_diffs_by_rule
22
111
 
23
- attr_accessor :violation_diffs
24
- attr_accessor :error_diffs
112
+ attr_accessor :rule_infos_union
113
+ attr_accessor :base_report
114
+ attr_accessor :patch_report
25
115
 
26
- def initialize
27
- @base_violations_size = 0
28
- @patch_violations_size = 0
29
- @violation_diffs_size = 0
116
+ def initialize(base_report:, patch_report:)
117
+ @violation_counts = RunningDiffCounters.new(base_report.violations_by_file.total_size)
118
+ @error_counts = RunningDiffCounters.new(base_report.errors_by_file.total_size)
119
+ @configerror_counts = RunningDiffCounters.new(base_report.configerrors_by_rule.values.flatten.length)
30
120
 
31
- @base_errors_size = 0
32
- @patch_errors_size = 0
33
- @error_diffs_size = 0
121
+ @violation_diffs_by_file = {}
122
+ @error_diffs_by_file = {}
123
+ @configerror_diffs_by_rule = {}
34
124
 
35
- @base_execution_time = 0
36
- @patch_execution_time = 0
37
- @diff_execution_time = 0
125
+ @rule_infos_union = base_report.infos_by_rule.dup
126
+ @base_report = base_report
127
+ @patch_report = patch_report
38
128
 
39
- @base_timestamp = ''
40
- @patch_timestamp = ''
41
-
42
- @violation_diffs = {}
43
- @error_diffs = {}
129
+ @violation_diffs_by_rule = {}
130
+ diff_with(patch_report)
44
131
  end
45
132
 
46
- def diffs_exist?
47
- !error_diffs_size.zero? || !violation_diffs_size.zero?
133
+ def rule_summaries
134
+ @violation_diffs_by_rule.map do |(rule, counters)|
135
+ {
136
+ 'name' => rule,
137
+ 'info_url' => @rule_infos_union[rule].info_url,
138
+ **counters.to_h.transform_keys(&:to_s)
139
+ }
140
+ end
48
141
  end
49
142
 
50
- def calculate_violations(base_violations, patch_violations)
51
- @base_violations_size = base_violations.violations_size
52
- @patch_violations_size = patch_violations.violations_size
53
- violation_diffs = build_diffs(base_violations.violations, patch_violations.violations)
54
- @violation_diffs = violation_diffs
55
- @violation_diffs_size = get_diffs_size(violation_diffs)
56
- end
143
+ private
144
+
145
+ def diff_with(patch_report)
146
+ @violation_counts.patch_total = patch_report.violations_by_file.total_size
147
+ @error_counts.patch_total = patch_report.errors_by_file.total_size
148
+ @configerror_counts.patch_total = patch_report.configerrors_by_rule.values.flatten.length
149
+
150
+ @violation_diffs_by_file = build_diffs(@violation_counts, &:violations_by_file)
151
+ count(@violation_diffs_by_file) { |v| getvdiff(v.rule_name) } # record the diffs in the rule counter
152
+
153
+ @error_diffs_by_file = build_diffs(@error_counts, &:errors_by_file)
154
+ @configerror_diffs_by_rule = build_diffs(@configerror_counts, &:configerrors_by_rule)
57
155
 
58
- def calculate_errors(base_errors, patch_errors)
59
- @base_errors_size = base_errors.errors_size
60
- @patch_errors_size = patch_errors.errors_size
61
- error_diffs = build_diffs(base_errors.errors, patch_errors.errors)
62
- @error_diffs = error_diffs
63
- @error_diffs_size = get_diffs_size(error_diffs)
156
+ count_by_rule(@base_report.violations_by_file, base: true)
157
+ count_by_rule(@patch_report.violations_by_file, base: false)
158
+ self
64
159
  end
65
160
 
66
- def calculate_details(base_info, patch_info)
67
- base_details = PmdReportDetail.new
68
- base_details.load(base_info) unless base_info.nil?
69
- patch_details = PmdReportDetail.new
70
- patch_details.load(patch_info) unless patch_info.nil?
161
+ def record_rule_info(violation)
162
+ return if @rule_infos_union.key?(violation.rule_name)
71
163
 
72
- @base_execution_time = base_details.format_execution_time
73
- @patch_execution_time = patch_details.format_execution_time
74
- @diff_execution_time =
75
- PmdReportDetail.convert_seconds(base_details.execution_time -
76
- patch_details.execution_time)
164
+ @rule_infos_union[violation.rule_name] = RuleInfo.new(violation.rule_name, violation.info_url)
165
+ end
166
+
167
+ def getvdiff(rule_name)
168
+ @violation_diffs_by_rule.fetch(rule_name) do |_|
169
+ @violation_diffs_by_rule[rule_name] = RunningDiffCounters.new(0)
170
+ end
171
+ end
77
172
 
78
- @base_timestamp = base_details.timestamp
79
- @patch_timestamp = patch_details.timestamp
80
- [base_details, patch_details]
173
+ def count_by_rule(violations_h, base:)
174
+ violations_h.each_value do |v|
175
+ record_rule_info(v)
176
+ rule_diff = getvdiff(v.rule_name)
177
+ if base
178
+ rule_diff.base_total += 1
179
+ else
180
+ rule_diff.patch_total += 1
181
+ end
182
+ end
81
183
  end
82
184
 
83
- def build_diffs(base_hash, patch_hash)
84
- diffs = base_hash.merge(patch_hash) do |_key, base_value, patch_value|
185
+ def build_diffs(counters, &getter)
186
+ base_hash = getter.yield(@base_report)
187
+ patch_hash = getter.yield(@patch_report)
188
+ # Keys are filenames
189
+ # Values are lists of violations/errors
190
+ diffs = base_hash.to_h.merge(patch_hash.to_h) do |_key, base_value, patch_value|
191
+ # make the difference of values
85
192
  (base_value | patch_value) - (base_value & patch_value)
86
193
  end
87
194
 
88
195
  diffs.delete_if do |_key, value|
89
196
  value.empty?
90
197
  end
198
+
199
+ merge_changed_items(diffs)
200
+ count(diffs) { |_| counters }
201
+ diffs
91
202
  end
92
203
 
93
- def get_diffs_size(diffs_hash)
94
- size = 0
95
- diffs_hash.keys.each do |key|
96
- size += diffs_hash[key].size
204
+ # @param diff_h a hash { filename => list[violation]}, containing those that changed
205
+ # in case of config errors it's a hash { rulename => list[configerror] }
206
+ def merge_changed_items(diff_h)
207
+ diff_h.each do |fname, different|
208
+ different.sort_by!(&:sort_key)
209
+ diff_h[fname] = different.delete_if do |v|
210
+ v.branch == BASE &&
211
+ # try_merge will set v2.changed = true if it succeeds
212
+ different.any? { |v2| v2.try_merge?(v) }
213
+ end
97
214
  end
98
- size
99
215
  end
100
216
 
101
- def introduce_new_errors?
102
- @error_diffs.values.each do |pmd_errors|
103
- pmd_errors.each do |pmd_error|
104
- return true if pmd_error.branch.eql?('patch')
217
+ def count(item_h)
218
+ item_h = { '' => item_h } if item_h.is_a?(Array)
219
+
220
+ item_h.each do |_k, items|
221
+ items.each do |item|
222
+ counter = yield item
223
+
224
+ if item.changed?
225
+ counter.changed += 1
226
+ elsif item.branch.eql?(BASE)
227
+ counter.removed += 1
228
+ else
229
+ counter.new += 1
230
+ end
105
231
  end
106
232
  end
107
-
108
- false
109
233
  end
110
234
  end
111
235
  end
@@ -6,5 +6,9 @@ module PmdTester
6
6
  def self.locate(resource_path)
7
7
  File.expand_path(File.dirname(__FILE__) + "/../../#{resource_path}")
8
8
  end
9
+
10
+ def self.resource(resource_path)
11
+ locate("resources/#{resource_path}")
12
+ end
9
13
  end
10
14
  end
@@ -1,25 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative './builders/diff_builder.rb'
4
- require_relative './builders/diff_report_builder.rb'
5
- require_relative './builders/rule_set_builder'
6
- require_relative './builders/summary_report_builder.rb'
7
- require_relative './builders/pmd_report_builder.rb'
8
- require_relative './parsers/options'
9
- require_relative './parsers/projects_parser'
10
- require_relative './pmdtester'
11
- require_relative './pmd_branch_detail'
12
-
13
3
  module PmdTester
14
4
  # The Runner is a class responsible of organizing all PmdTester modules
15
5
  # and running the PmdTester
16
6
  class Runner
17
- include PmdTester
7
+ include PmdTesterUtils
8
+
18
9
  def initialize(argv)
19
10
  @options = Options.new(argv)
20
11
  end
21
12
 
22
13
  def run
14
+ clean unless @options.keep_reports
15
+
23
16
  case @options.mode
24
17
  when Options::LOCAL
25
18
  run_local_mode
@@ -28,54 +21,74 @@ module PmdTester
28
21
  when Options::SINGLE
29
22
  run_single_mode
30
23
  end
24
+
25
+ summarize_diffs
26
+ end
27
+
28
+ def clean
29
+ clean_target = 'target/reports'
30
+ FileUtils.remove_dir(clean_target) if Dir.exist?(clean_target)
31
31
  end
32
32
 
33
33
  def run_local_mode
34
34
  logger.info "Mode: #{@options.mode}"
35
- RuleSetBuilder.new(@options).build if @options.auto_config_flag
36
-
37
35
  get_projects(@options.project_list) unless @options.nil?
38
- PmdReportBuilder
39
- .new(@options.base_config, @projects, @options.local_git_repo, @options.base_branch)
40
- .build
41
- PmdReportBuilder
42
- .new(@options.patch_config, @projects, @options.local_git_repo, @options.patch_branch)
43
- .build
44
-
45
- build_html_reports
36
+ rule_sets = RuleSetBuilder.new(@options).build if @options.auto_config_flag
37
+ return if rule_sets&.empty?
38
+
39
+ base_branch_details = create_pmd_report(config: @options.base_config, branch: @options.base_branch)
40
+ patch_branch_details = create_pmd_report(config: @options.patch_config, branch: @options.patch_branch)
41
+
42
+ build_html_reports(@projects, base_branch_details, patch_branch_details)
46
43
  end
47
44
 
48
45
  def run_online_mode
49
46
  logger.info "Mode: #{@options.mode}"
50
47
 
51
- baseline_path = download_baseline(@options.base_branch)
48
+ baseline_path = download_baseline(@options.baseline_download_url_prefix, @options.base_branch)
49
+
50
+ project_list = determine_project_list_for_online_mode(baseline_path)
51
+ get_projects(project_list)
52
52
 
53
53
  if @options.auto_config_flag
54
- RuleSetBuilder.new(@options).build
55
- else
54
+ return if RuleSetBuilder.new(@options).build.empty?
55
+ elsif @options.patch_config == Options::DEFAULT_CONFIG_PATH
56
56
  # patch branch build pmd reports with same configuration as base branch
57
+ # if not specified otherwise. This allows to use a different config (e.g. less rules)
58
+ # than used for creating the baseline. Use with care, though
57
59
  @options.patch_config = "#{baseline_path}/config.xml"
60
+ else
61
+ logger.info "Using config #{@options.patch_config} which might differ from baseline"
58
62
  end
59
63
 
60
- # patch branch build pmd report with same list of projects as base branch
61
- project_list = "#{baseline_path}/project-list.xml"
62
- get_projects(project_list)
64
+ patch_branch_details = create_pmd_report(config: @options.patch_config, branch: @options.patch_branch)
63
65
 
64
- PmdReportBuilder
65
- .new(@options.patch_config, @projects, @options.local_git_repo, @options.patch_branch)
66
- .build
66
+ base_branch_details = PmdBranchDetail.load(@options.base_branch, logger)
67
+ build_html_reports(@projects, base_branch_details, patch_branch_details, @options.filter_set)
68
+ end
67
69
 
68
- build_html_reports
70
+ def determine_project_list_for_online_mode(baseline_path)
71
+ # patch branch build pmd report with same list of projects as base branch
72
+ # if not specified otherwise. This allows to use a different project list
73
+ # than used for creating the baseline. Use with care, though
74
+ if @options.project_list == Options::DEFAULT_LIST_PATH
75
+ project_list = "#{baseline_path}/project-list.xml"
76
+ else
77
+ logger.info "Using project list #{@options.project_list} which might differ from baseline"
78
+ project_list = @options.project_list
79
+ end
80
+ project_list
69
81
  end
70
82
 
71
- def download_baseline(branch_name)
83
+ def download_baseline(url_prefix, branch_name)
72
84
  branch_filename = PmdBranchDetail.branch_filename(branch_name)
73
85
  zip_filename = "#{branch_filename}-baseline.zip"
74
86
  target_path = 'target/reports'
75
87
  FileUtils.mkdir_p(target_path) unless File.directory?(target_path)
76
88
 
77
- url = get_baseline_url(zip_filename)
78
- wget_cmd = "wget #{url}"
89
+ url = get_baseline_url(url_prefix, zip_filename)
90
+ logger.info "Downloading baseline for branch #{branch_name} from #{url}"
91
+ wget_cmd = "wget --timestamping #{url}"
79
92
  unzip_cmd = "unzip -qo #{zip_filename}"
80
93
 
81
94
  Dir.chdir(target_path) do
@@ -86,45 +99,57 @@ module PmdTester
86
99
  "#{target_path}/#{branch_filename}"
87
100
  end
88
101
 
89
- def get_baseline_url(zip_filename)
90
- "https://sourceforge.net/projects/pmd/files/pmd-regression-tester/#{zip_filename}"
102
+ def get_baseline_url(url_prefix, zip_filename)
103
+ "#{url_prefix}#{zip_filename}"
91
104
  end
92
105
 
93
106
  def run_single_mode
94
107
  logger.info "Mode: #{@options.mode}"
95
108
 
96
109
  get_projects(@options.project_list) unless @options.nil?
97
- branch_details = PmdReportBuilder
98
- .new(@options.patch_config, @projects,
99
- @options.local_git_repo, @options.patch_branch)
100
- .build
110
+ patch_branch_details = create_pmd_report(config: @options.patch_config, branch: @options.patch_branch)
101
111
  # copy list of projects file to the patch baseline
102
- FileUtils.cp(@options.project_list, branch_details.target_branch_project_list_path)
112
+ FileUtils.cp(@options.project_list, patch_branch_details.target_branch_project_list_path)
103
113
 
104
- build_html_reports unless @options.html_flag
114
+ # for creating a baseline, no html report is needed
115
+ return if @options.html_flag
116
+
117
+ # in single mode, we don't have a base branch, only a patch branch...
118
+ empty_base_branch_details = PmdBranchDetail.load('single-mode', logger)
119
+ build_html_reports(@projects, empty_base_branch_details, patch_branch_details)
105
120
  end
106
121
 
107
- def build_html_reports
108
- build_diff_html_reports
109
- SummaryReportBuilder.new.build(@projects, @options.base_branch, @options.patch_branch)
122
+ def get_projects(file_path)
123
+ @projects = ProjectsParser.new.parse(file_path)
110
124
  end
111
125
 
112
- def build_diff_html_reports
126
+ def summarize_diffs
127
+ error_total = RunningDiffCounters.new(0)
128
+ violations_total = RunningDiffCounters.new(0)
129
+ configerrors_total = RunningDiffCounters.new(0)
130
+
113
131
  @projects.each do |project|
114
- logger.info "Preparing report for #{project.name}"
115
- report_diffs = DiffBuilder.new.build(project.get_pmd_report_path(@options.base_branch),
116
- project.get_pmd_report_path(@options.patch_branch),
117
- project.get_report_info_path(@options.base_branch),
118
- project.get_report_info_path(@options.patch_branch),
119
- @options.filter_set)
120
- project.report_diff = report_diffs
121
- DiffReportBuilder.new.build(project)
132
+ diff = project.report_diff
133
+
134
+ # in case we are in single mode, there might be no diffs (only the patch branch is available)
135
+ next if diff.nil?
136
+
137
+ error_total.merge!(diff.error_counts)
138
+ violations_total.merge!(diff.violation_counts)
139
+ configerrors_total.merge!(diff.configerror_counts)
122
140
  end
123
- logger.info 'Built all difference reports successfully!'
141
+
142
+ {
143
+ errors: error_total.to_h,
144
+ violations: violations_total.to_h,
145
+ configerrors: configerrors_total.to_h
146
+ }
124
147
  end
125
148
 
126
- def get_projects(file_path)
127
- @projects = ProjectsParser.new.parse(file_path)
149
+ private
150
+
151
+ def create_pmd_report(config:, branch:)
152
+ PmdReportBuilder.new(@projects, @options, config, branch).build
128
153
  end
129
154
  end
130
155
  end