pmdtester 1.0.1 → 1.1.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.
- checksums.yaml +4 -4
- data/.ci/build.sh +91 -0
- data/.ci/files/env.gpg +1 -0
- data/.github/workflows/build.yml +39 -0
- data/.gitignore +9 -0
- data/.hoerc +1 -1
- data/.rubocop.yml +6 -2
- data/.ruby-version +1 -0
- data/History.md +40 -0
- data/Manifest.txt +24 -9
- data/README.rdoc +45 -30
- data/Rakefile +5 -3
- data/config/all-java.xml +1 -1
- data/config/design.xml +1 -1
- data/config/projectlist_1_0_0.xsd +2 -1
- data/config/projectlist_1_1_0.xsd +31 -0
- data/lib/pmdtester.rb +8 -7
- data/lib/pmdtester/builders/liquid_renderer.rb +73 -0
- data/lib/pmdtester/builders/pmd_report_builder.rb +102 -78
- data/lib/pmdtester/builders/project_builder.rb +100 -0
- data/lib/pmdtester/builders/project_hasher.rb +126 -0
- data/lib/pmdtester/builders/rule_set_builder.rb +92 -47
- data/lib/pmdtester/builders/simple_progress_logger.rb +4 -4
- data/lib/pmdtester/builders/summary_report_builder.rb +62 -131
- data/lib/pmdtester/collection_by_file.rb +55 -0
- data/lib/pmdtester/parsers/options.rb +19 -0
- data/lib/pmdtester/parsers/pmd_report_document.rb +74 -29
- data/lib/pmdtester/parsers/projects_parser.rb +2 -4
- data/lib/pmdtester/pmd_branch_detail.rb +29 -19
- data/lib/pmdtester/pmd_configerror.rb +23 -24
- data/lib/pmdtester/pmd_error.rb +34 -34
- data/lib/pmdtester/pmd_report_detail.rb +9 -12
- data/lib/pmdtester/pmd_tester_utils.rb +55 -0
- data/lib/pmdtester/pmd_violation.rb +66 -28
- data/lib/pmdtester/project.rb +21 -48
- data/lib/pmdtester/report_diff.rb +179 -111
- data/lib/pmdtester/resource_locator.rb +4 -0
- data/lib/pmdtester/runner.rb +66 -64
- data/pmdtester.gemspec +27 -36
- data/resources/_includes/diff_pill_row.html +6 -0
- data/resources/css/bootstrap.min.css +7 -0
- data/resources/css/datatables.min.css +36 -0
- data/resources/css/pmd-tester.css +131 -0
- data/resources/js/bootstrap.min.js +7 -0
- data/resources/js/code-snippets.js +66 -0
- data/resources/js/datatables.min.js +726 -0
- data/resources/js/jquery-3.2.1.slim.min.js +4 -0
- data/resources/js/jquery.min.js +2 -0
- data/resources/js/popper.min.js +5 -0
- data/resources/js/project-report.js +136 -0
- data/resources/project_diff_report.html +205 -0
- data/resources/project_index.html +102 -0
- metadata +64 -20
- data/.travis.yml +0 -40
- data/lib/pmdtester/builders/diff_builder.rb +0 -31
- data/lib/pmdtester/builders/diff_report/configerrors.rb +0 -50
- data/lib/pmdtester/builders/diff_report/errors.rb +0 -71
- data/lib/pmdtester/builders/diff_report/violations.rb +0 -77
- data/lib/pmdtester/builders/diff_report_builder.rb +0 -99
- data/lib/pmdtester/builders/html_report_builder.rb +0 -56
- data/resources/css/maven-base.css +0 -155
- data/resources/css/maven-theme.css +0 -171
@@ -7,12 +7,12 @@ module PmdTester
|
|
7
7
|
class PmdReportDetail
|
8
8
|
attr_accessor :execution_time
|
9
9
|
attr_accessor :timestamp
|
10
|
-
|
10
|
+
attr_accessor :working_dir
|
11
11
|
|
12
|
-
def initialize
|
13
|
-
@execution_time =
|
14
|
-
@timestamp =
|
15
|
-
@working_dir =
|
12
|
+
def initialize(execution_time: 0, timestamp: '', working_dir: Dir.getwd)
|
13
|
+
@execution_time = execution_time
|
14
|
+
@timestamp = timestamp
|
15
|
+
@working_dir = working_dir
|
16
16
|
end
|
17
17
|
|
18
18
|
def save(report_info_path)
|
@@ -22,16 +22,13 @@ module PmdTester
|
|
22
22
|
file.close
|
23
23
|
end
|
24
24
|
|
25
|
-
def load(report_info_path)
|
25
|
+
def self.load(report_info_path)
|
26
26
|
if File.exist?(report_info_path)
|
27
|
-
hash = JSON.parse(File.read(report_info_path))
|
28
|
-
|
29
|
-
@timestamp = hash['timestamp']
|
30
|
-
@working_dir = hash['working_dir']
|
31
|
-
hash
|
27
|
+
hash = JSON.parse(File.read(report_info_path), symbolize_names: true)
|
28
|
+
PmdReportDetail.new(**hash)
|
32
29
|
else
|
33
30
|
puts "#{report_info_path} doesn't exist"
|
34
|
-
|
31
|
+
PmdReportDetail.new
|
35
32
|
end
|
36
33
|
end
|
37
34
|
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PmdTester
|
4
|
+
# Some functions that that don't belong in a specific class,
|
5
|
+
module PmdTesterUtils
|
6
|
+
include PmdTester
|
7
|
+
|
8
|
+
# Parse the base and the patch report, compute their diff
|
9
|
+
# Returns a +ReportDiff+
|
10
|
+
def build_report_diff(base_report_file, patch_report_file, base_info, patch_info, filter_set = nil)
|
11
|
+
base_details = PmdReportDetail.load(base_info)
|
12
|
+
patch_details = PmdReportDetail.load(patch_info)
|
13
|
+
|
14
|
+
base_report = parse_pmd_report(base_report_file, BASE, base_details, filter_set)
|
15
|
+
patch_report = parse_pmd_report(patch_report_file, PATCH, patch_details)
|
16
|
+
|
17
|
+
logger.info 'Calculating diffs'
|
18
|
+
ReportDiff.new(base_report: base_report, patch_report: patch_report)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Parse the +report_file+ to produce a +Report+.
|
22
|
+
# For the schema of xml reports, refer to http://pmd.sourceforge.net/report_2_0_0.xsd
|
23
|
+
def parse_pmd_report(report_file, branch, report_details, filter_set = nil)
|
24
|
+
require 'nokogiri'
|
25
|
+
|
26
|
+
logger.info "Parsing #{report_file}"
|
27
|
+
doc = PmdReportDocument.new(branch, report_details.working_dir, filter_set)
|
28
|
+
parser = Nokogiri::XML::SAX::Parser.new(doc)
|
29
|
+
parser.parse_file(report_file) if File.exist?(report_file)
|
30
|
+
Report.new(
|
31
|
+
report_document: doc,
|
32
|
+
|
33
|
+
timestamp: report_details.timestamp,
|
34
|
+
exec_time: report_details.execution_time
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Fill the report_diff field of every project
|
39
|
+
def compute_project_diffs(projects, base_branch, patch_branch, filter_set = nil)
|
40
|
+
projects.each do |project|
|
41
|
+
logger.info "Preparing report for #{project.name}"
|
42
|
+
project.compute_report_diff(base_branch, patch_branch, filter_set)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# 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
|
+
|
50
|
+
SummaryReportBuilder.new.write_all_projects(projects,
|
51
|
+
base_branch_details,
|
52
|
+
patch_branch_details)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -1,25 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module PmdTester
|
4
|
-
# This class is used to store pmd violations and its size.
|
5
|
-
class PmdViolations
|
6
|
-
attr_reader :violations
|
7
|
-
attr_reader :violations_size
|
8
|
-
|
9
|
-
def initialize
|
10
|
-
# key:filename as String => value:PmdViolation Array
|
11
|
-
@violations = {}
|
12
|
-
@violations_size = 0
|
13
|
-
end
|
14
|
-
|
15
|
-
def add_violations_by_filename(filename, violations)
|
16
|
-
return if violations.empty?
|
17
|
-
|
18
|
-
@violations.store(filename, violations)
|
19
|
-
@violations_size += violations.size
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
4
|
# This class represents a 'violation' element of Pmd xml report
|
24
5
|
# and which pmd branch the 'violation' is from
|
25
6
|
class PmdViolation
|
@@ -46,23 +27,80 @@ module PmdTester
|
|
46
27
|
# </xs:simpleContent>
|
47
28
|
# </xs:complexType>
|
48
29
|
|
49
|
-
attr_reader :
|
50
|
-
attr_accessor :
|
30
|
+
attr_reader :fname, :info_url, :line, :old_line, :old_message, :rule_name, :ruleset_name
|
31
|
+
attr_accessor :message
|
51
32
|
|
52
|
-
|
53
|
-
|
33
|
+
# rubocop:disable Metrics/ParameterLists
|
34
|
+
# Disable it: how is replacing a long parameter list with a single hash helping?
|
35
|
+
def initialize(branch:, fname:, info_url:, bline:, rule_name:, ruleset_name:)
|
54
36
|
@branch = branch
|
55
|
-
@
|
37
|
+
@fname = fname
|
38
|
+
@message = ''
|
39
|
+
|
40
|
+
@info_url = info_url
|
41
|
+
@line = bline
|
42
|
+
@rule_name = rule_name
|
43
|
+
|
44
|
+
@ruleset_name = ruleset_name
|
45
|
+
|
46
|
+
@changed = false
|
47
|
+
@old_message = nil
|
48
|
+
@old_line = nil
|
49
|
+
end
|
50
|
+
# rubocop:enable Metrics/ParameterLists
|
51
|
+
|
52
|
+
def line_move?(other)
|
53
|
+
message.eql?(other.message) && (line - other.line).abs <= 5
|
54
|
+
end
|
55
|
+
|
56
|
+
def try_merge?(other)
|
57
|
+
if branch != BASE && branch != other.branch && rule_name == other.rule_name &&
|
58
|
+
!changed? && # not already changed
|
59
|
+
(line == other.line || line_move?(other))
|
60
|
+
@changed = true
|
61
|
+
@old_message = other.message
|
62
|
+
@old_line = other.line
|
63
|
+
true
|
64
|
+
else
|
65
|
+
false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# only makes sense if this is a diff
|
70
|
+
def added?
|
71
|
+
branch != BASE && !changed?
|
72
|
+
end
|
73
|
+
|
74
|
+
# only makes sense if this is a diff
|
75
|
+
def changed?
|
76
|
+
@changed
|
77
|
+
end
|
78
|
+
|
79
|
+
# only makes sense if this is a diff
|
80
|
+
def removed?
|
81
|
+
branch == BASE
|
82
|
+
end
|
83
|
+
|
84
|
+
def sort_key
|
85
|
+
line
|
56
86
|
end
|
57
87
|
|
58
88
|
def eql?(other)
|
59
|
-
|
60
|
-
|
61
|
-
|
89
|
+
rule_name.eql?(other.rule_name) &&
|
90
|
+
line.eql?(other.line) &&
|
91
|
+
fname.eql?(other.fname) &&
|
92
|
+
message.eql?(other.message)
|
62
93
|
end
|
63
94
|
|
64
95
|
def hash
|
65
|
-
[
|
96
|
+
[line, rule_name, message].hash
|
97
|
+
end
|
98
|
+
|
99
|
+
def to_liquid
|
100
|
+
{
|
101
|
+
'branch' => branch,
|
102
|
+
'changed' => changed?
|
103
|
+
}
|
66
104
|
end
|
67
105
|
end
|
68
106
|
end
|
data/lib/pmdtester/project.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
module PmdTester
|
4
4
|
# This class represents all the information about the project
|
5
5
|
class Project
|
6
|
+
include PmdTesterUtils
|
7
|
+
|
6
8
|
REPOSITORIES_PATH = 'target/repositories'
|
7
9
|
|
8
10
|
attr_reader :name
|
@@ -13,15 +15,17 @@ module PmdTester
|
|
13
15
|
attr_reader :exclude_pattern
|
14
16
|
attr_accessor :report_diff
|
15
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
|
16
22
|
|
17
23
|
def initialize(project)
|
18
24
|
@name = project.at_xpath('name').text
|
19
25
|
@type = project.at_xpath('type').text
|
20
26
|
@connection = project.at_xpath('connection').text
|
21
27
|
|
22
|
-
@tag = 'master'
|
23
|
-
tag_element = project.at_xpath('tag')
|
24
|
-
@tag = tag_element.text unless tag_element.nil?
|
28
|
+
@tag = project.at_xpath('tag')&.text || 'master'
|
25
29
|
|
26
30
|
webview_url_element = project.at_xpath('webview-url')
|
27
31
|
@webview_url = default_webview_url
|
@@ -32,7 +36,10 @@ module PmdTester
|
|
32
36
|
@exclude_pattern.push(ep.text)
|
33
37
|
end
|
34
38
|
|
35
|
-
@
|
39
|
+
@build_command = project.at_xpath('build-command')&.text
|
40
|
+
@auxclasspath_command = project.at_xpath('auxclasspath-command')&.text
|
41
|
+
|
42
|
+
@report_diff = nil
|
36
43
|
end
|
37
44
|
|
38
45
|
# Generate the default webview url for the projects
|
@@ -58,6 +65,10 @@ module PmdTester
|
|
58
65
|
file_path.gsub(%r{/#{local_source_path}}, @name)
|
59
66
|
end
|
60
67
|
|
68
|
+
def get_local_path(file_path)
|
69
|
+
file_path.sub(%r{/#{local_source_path}/}, '')
|
70
|
+
end
|
71
|
+
|
61
72
|
def get_pmd_report_path(branch_name)
|
62
73
|
if branch_name.nil?
|
63
74
|
nil
|
@@ -93,50 +104,12 @@ module PmdTester
|
|
93
104
|
"#{REPOSITORIES_PATH}/#{@name}"
|
94
105
|
end
|
95
106
|
|
96
|
-
def
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
def diff_report_index_path
|
103
|
-
"#{target_diff_report_path}/index.html"
|
104
|
-
end
|
105
|
-
|
106
|
-
def diff_report_index_ref_path
|
107
|
-
"./#{name}/index.html"
|
108
|
-
end
|
109
|
-
|
110
|
-
def diffs_exist?
|
111
|
-
@report_diff.nil? ? false : @report_diff.diffs_exist?
|
112
|
-
end
|
113
|
-
|
114
|
-
def introduce_new_errors?
|
115
|
-
@report_diff.nil? ? false : @report_diff.introduce_new_errors?
|
116
|
-
end
|
117
|
-
|
118
|
-
def removed_errors_size
|
119
|
-
@report_diff.removed_errors_size
|
120
|
-
end
|
121
|
-
|
122
|
-
def new_errors_size
|
123
|
-
@report_diff.new_errors_size
|
124
|
-
end
|
125
|
-
|
126
|
-
def removed_violations_size
|
127
|
-
@report_diff.removed_violations_size
|
128
|
-
end
|
129
|
-
|
130
|
-
def new_violations_size
|
131
|
-
@report_diff.new_violations_size
|
132
|
-
end
|
133
|
-
|
134
|
-
def removed_configerrors_size
|
135
|
-
@report_diff.removed_configerrors_size
|
136
|
-
end
|
137
|
-
|
138
|
-
def new_configerrors_size
|
139
|
-
@report_diff.new_configerrors_size
|
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)
|
140
113
|
end
|
141
114
|
end
|
142
115
|
end
|
@@ -1,157 +1,225 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module PmdTester
|
4
|
-
#
|
5
|
-
|
6
|
-
|
7
|
-
class ReportDiff
|
8
|
-
include PmdTester
|
9
|
-
|
10
|
-
attr_accessor :base_violations_size
|
11
|
-
attr_accessor :patch_violations_size
|
12
|
-
attr_accessor :new_violations_size
|
13
|
-
attr_accessor :removed_violations_size
|
14
|
-
attr_accessor :violation_diffs_size
|
4
|
+
# A bunch of counters to summarize differences
|
5
|
+
class RunningDiffCounters
|
6
|
+
attr_accessor :changed, :new, :removed, :patch_total, :base_total
|
15
7
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
attr_accessor :removed_errors_size
|
20
|
-
attr_accessor :error_diffs_size
|
8
|
+
def initialize(base_total)
|
9
|
+
@base_total = base_total
|
10
|
+
@patch_total = 0
|
21
11
|
|
22
|
-
|
23
|
-
|
24
|
-
attr_accessor :new_configerrors_size
|
25
|
-
attr_accessor :removed_configerrors_size
|
26
|
-
attr_accessor :configerrors_diffs_size
|
27
|
-
|
28
|
-
attr_accessor :base_execution_time
|
29
|
-
attr_accessor :patch_execution_time
|
30
|
-
attr_accessor :diff_execution_time
|
31
|
-
|
32
|
-
attr_accessor :base_timestamp
|
33
|
-
attr_accessor :patch_timestamp
|
12
|
+
@new = @removed = @changed = 0
|
13
|
+
end
|
34
14
|
|
35
|
-
|
36
|
-
|
37
|
-
|
15
|
+
def changed_total
|
16
|
+
new + removed + changed
|
17
|
+
end
|
38
18
|
|
39
|
-
def
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
43
26
|
|
44
|
-
|
45
|
-
|
46
|
-
|
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
|
+
end
|
47
37
|
|
48
|
-
|
49
|
-
|
38
|
+
# Simple info about a rule, collected by the report xml parser
|
39
|
+
class RuleInfo
|
40
|
+
attr_reader :name, :info_url
|
50
41
|
|
51
|
-
|
52
|
-
@
|
53
|
-
@
|
42
|
+
def initialize(name, info_url)
|
43
|
+
@name = name
|
44
|
+
@info_url = info_url
|
54
45
|
end
|
46
|
+
end
|
55
47
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
48
|
+
# A full report, created by the report XML parser,
|
49
|
+
# can be diffed with another report into a ReportDiff
|
50
|
+
class Report
|
51
|
+
attr_reader :violations_by_file,
|
52
|
+
:errors_by_file,
|
53
|
+
:configerrors_by_rule,
|
54
|
+
:exec_time,
|
55
|
+
:timestamp,
|
56
|
+
:infos_by_rule
|
57
|
+
|
58
|
+
def initialize(report_document: nil,
|
59
|
+
exec_time: 0,
|
60
|
+
timestamp: '0')
|
61
|
+
initialize_empty
|
62
|
+
initialize_with_report_document report_document unless report_document.nil?
|
63
|
+
@exec_time = exec_time
|
64
|
+
@timestamp = timestamp
|
62
65
|
end
|
63
66
|
|
64
|
-
def
|
65
|
-
|
66
|
-
@patch_errors_size = 0
|
67
|
-
@new_errors_size = 0
|
68
|
-
@removed_errors_size = 0
|
69
|
-
@error_diffs_size = 0
|
67
|
+
def self.empty
|
68
|
+
new
|
70
69
|
end
|
71
70
|
|
72
|
-
|
73
|
-
@base_configerrors_size = 0
|
74
|
-
@patch_configerrors_size = 0
|
75
|
-
@new_configerrors_size = 0
|
76
|
-
@removed_configerrors_size = 0
|
77
|
-
@configerrors_diffs_size = 0
|
78
|
-
end
|
71
|
+
private
|
79
72
|
|
80
|
-
def
|
81
|
-
|
73
|
+
def initialize_with_report_document(report_document)
|
74
|
+
@violations_by_file = report_document.violations
|
75
|
+
@errors_by_file = report_document.errors
|
76
|
+
@configerrors_by_rule = report_document.configerrors
|
77
|
+
@infos_by_rule = report_document.infos_by_rules
|
82
78
|
end
|
83
79
|
|
84
|
-
def
|
85
|
-
|
80
|
+
def initialize_empty
|
81
|
+
@violations_by_file = CollectionByFile.new
|
82
|
+
@errors_by_file = CollectionByFile.new
|
83
|
+
@configerrors_by_rule = {}
|
84
|
+
@infos_by_rule = {}
|
86
85
|
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# This class represents all the diff report information,
|
89
|
+
# including the summary information of the original pmd reports,
|
90
|
+
# as well as the specific information of the diff report.
|
91
|
+
class ReportDiff
|
92
|
+
include PmdTester
|
87
93
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
94
|
+
attr_reader :error_counts
|
95
|
+
attr_reader :violation_counts
|
96
|
+
attr_reader :configerror_counts
|
97
|
+
|
98
|
+
attr_accessor :violation_diffs_by_file
|
99
|
+
attr_accessor :error_diffs_by_file
|
100
|
+
attr_accessor :configerror_diffs_by_rule
|
101
|
+
|
102
|
+
attr_accessor :rule_infos_union
|
103
|
+
attr_accessor :base_report
|
104
|
+
attr_accessor :patch_report
|
105
|
+
|
106
|
+
def initialize(base_report:, patch_report:)
|
107
|
+
@violation_counts = RunningDiffCounters.new(base_report.violations_by_file.total_size)
|
108
|
+
@error_counts = RunningDiffCounters.new(base_report.errors_by_file.total_size)
|
109
|
+
@configerror_counts = RunningDiffCounters.new(base_report.configerrors_by_rule.values.flatten.length)
|
110
|
+
|
111
|
+
@violation_diffs_by_file = {}
|
112
|
+
@error_diffs_by_file = {}
|
113
|
+
@configerror_diffs_by_rule = {}
|
114
|
+
|
115
|
+
@rule_infos_union = base_report.infos_by_rule.dup
|
116
|
+
@base_report = base_report
|
117
|
+
@patch_report = patch_report
|
118
|
+
|
119
|
+
@violation_diffs_by_rule = {}
|
120
|
+
diff_with(patch_report)
|
95
121
|
end
|
96
122
|
|
97
|
-
def
|
98
|
-
@
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
123
|
+
def rule_summaries
|
124
|
+
@violation_diffs_by_rule.map do |(rule, counters)|
|
125
|
+
{
|
126
|
+
'name' => rule,
|
127
|
+
'info_url' => @rule_infos_union[rule].info_url,
|
128
|
+
**counters.to_h
|
129
|
+
}
|
130
|
+
end
|
104
131
|
end
|
105
132
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
@
|
111
|
-
@
|
112
|
-
|
133
|
+
private
|
134
|
+
|
135
|
+
def diff_with(patch_report)
|
136
|
+
@violation_counts.patch_total = patch_report.violations_by_file.total_size
|
137
|
+
@error_counts.patch_total = patch_report.errors_by_file.total_size
|
138
|
+
@configerror_counts.patch_total = patch_report.configerrors_by_rule.values.flatten.length
|
139
|
+
|
140
|
+
@violation_diffs_by_file = build_diffs(@violation_counts, &:violations_by_file)
|
141
|
+
count(@violation_diffs_by_file) { |v| getvdiff(v.rule_name) } # record the diffs in the rule counter
|
142
|
+
|
143
|
+
@error_diffs_by_file = build_diffs(@error_counts, &:errors_by_file)
|
144
|
+
@configerror_diffs_by_rule = build_diffs(@configerror_counts, &:configerrors_by_rule)
|
145
|
+
|
146
|
+
count_by_rule(@base_report.violations_by_file, base: true)
|
147
|
+
count_by_rule(@patch_report.violations_by_file, base: false)
|
148
|
+
self
|
113
149
|
end
|
114
150
|
|
115
|
-
def
|
116
|
-
|
117
|
-
base_details.load(base_info) unless base_info.nil?
|
118
|
-
patch_details = PmdReportDetail.new
|
119
|
-
patch_details.load(patch_info) unless patch_info.nil?
|
151
|
+
def record_rule_info(violation)
|
152
|
+
return if @rule_infos_union.key?(violation.rule_name)
|
120
153
|
|
121
|
-
@
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
154
|
+
@rule_infos_union[violation.rule_name] = RuleInfo.new(violation.rule_name, violation.info_url)
|
155
|
+
end
|
156
|
+
|
157
|
+
def getvdiff(rule_name)
|
158
|
+
@violation_diffs_by_rule.fetch(rule_name) do |_|
|
159
|
+
@violation_diffs_by_rule[rule_name] = RunningDiffCounters.new(0)
|
160
|
+
end
|
161
|
+
end
|
126
162
|
|
127
|
-
|
128
|
-
|
129
|
-
|
163
|
+
def count_by_rule(violations_h, base:)
|
164
|
+
violations_h.each_value do |v|
|
165
|
+
record_rule_info(v)
|
166
|
+
rule_diff = getvdiff(v.rule_name)
|
167
|
+
if base
|
168
|
+
rule_diff.base_total += 1
|
169
|
+
else
|
170
|
+
rule_diff.patch_total += 1
|
171
|
+
end
|
172
|
+
end
|
130
173
|
end
|
131
174
|
|
132
|
-
def build_diffs(
|
133
|
-
|
175
|
+
def build_diffs(counters, &getter)
|
176
|
+
base_hash = getter.yield(@base_report)
|
177
|
+
patch_hash = getter.yield(@patch_report)
|
178
|
+
# Keys are filenames
|
179
|
+
# Values are lists of violations/errors
|
180
|
+
diffs = base_hash.to_h.merge(patch_hash.to_h) do |_key, base_value, patch_value|
|
181
|
+
# make the difference of values
|
134
182
|
(base_value | patch_value) - (base_value & patch_value)
|
135
183
|
end
|
136
184
|
|
137
185
|
diffs.delete_if do |_key, value|
|
138
186
|
value.empty?
|
139
187
|
end
|
188
|
+
|
189
|
+
merge_changed_items(diffs)
|
190
|
+
count(diffs) { |_| counters }
|
191
|
+
diffs
|
140
192
|
end
|
141
193
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
194
|
+
# @param diff_h a hash { filename => list[violation]}, containing those that changed
|
195
|
+
# in case of config errors it's a hash { rulename => list[configerror] }
|
196
|
+
def merge_changed_items(diff_h)
|
197
|
+
diff_h.each do |fname, different|
|
198
|
+
different.sort_by!(&:sort_key)
|
199
|
+
diff_h[fname] = different.delete_if do |v|
|
200
|
+
v.branch == BASE &&
|
201
|
+
# try_merge will set v2.changed = true if it succeeds
|
202
|
+
different.any? { |v2| v2.try_merge?(v) }
|
148
203
|
end
|
149
204
|
end
|
150
|
-
[new_size, removed_size]
|
151
205
|
end
|
152
206
|
|
153
|
-
def
|
154
|
-
|
207
|
+
def count(item_h)
|
208
|
+
item_h = { '' => item_h } if item_h.is_a?(Array)
|
209
|
+
|
210
|
+
item_h.each do |_k, items|
|
211
|
+
items.each do |item|
|
212
|
+
counter = yield item
|
213
|
+
|
214
|
+
if item.changed?
|
215
|
+
counter.changed += 1
|
216
|
+
elsif item.branch.eql?(BASE)
|
217
|
+
counter.removed += 1
|
218
|
+
else
|
219
|
+
counter.new += 1
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
155
223
|
end
|
156
224
|
end
|
157
225
|
end
|