pmdtester 1.0.0.pre.beta3 → 1.1.2
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 +67 -0
- data/.ci/files/env.gpg +1 -0
- data/.ci/inc/install-openjdk.inc +26 -0
- data/.ci/manual-integration-tests.sh +20 -0
- data/.github/workflows/build.yml +39 -0
- data/.github/workflows/manual-integration-tests.yml +32 -0
- data/.gitignore +9 -0
- data/.hoerc +1 -1
- data/.rubocop.yml +13 -2
- data/.rubocop_todo.yml +7 -8
- data/.ruby-version +1 -0
- data/Gemfile +1 -12
- data/History.md +104 -0
- data/Manifest.txt +30 -6
- data/README.rdoc +110 -60
- data/Rakefile +27 -15
- 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 +12 -4
- data/lib/pmdtester/builders/liquid_renderer.rb +73 -0
- data/lib/pmdtester/builders/pmd_report_builder.rb +134 -60
- 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 +94 -48
- data/lib/pmdtester/builders/simple_progress_logger.rb +27 -0
- data/lib/pmdtester/builders/summary_report_builder.rb +62 -117
- data/lib/pmdtester/cmd.rb +15 -1
- data/lib/pmdtester/collection_by_file.rb +55 -0
- data/lib/pmdtester/parsers/options.rb +25 -2
- data/lib/pmdtester/parsers/pmd_report_document.rb +79 -27
- data/lib/pmdtester/parsers/projects_parser.rb +2 -4
- data/lib/pmdtester/pmd_branch_detail.rb +36 -12
- data/lib/pmdtester/pmd_configerror.rb +62 -0
- data/lib/pmdtester/pmd_error.rb +34 -34
- data/lib/pmdtester/pmd_report_detail.rb +10 -13
- data/lib/pmdtester/pmd_tester_utils.rb +57 -0
- data/lib/pmdtester/pmd_violation.rb +66 -26
- data/lib/pmdtester/project.rb +28 -23
- data/lib/pmdtester/report_diff.rb +194 -70
- data/lib/pmdtester/resource_locator.rb +4 -0
- data/lib/pmdtester/runner.rb +81 -54
- data/pmdtester.gemspec +64 -0
- 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 +132 -0
- data/resources/js/bootstrap.min.js +7 -0
- data/resources/js/code-snippets.js +73 -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 +122 -38
- data/.travis.yml +0 -22
- data/lib/pmdtester/builders/diff_builder.rb +0 -30
- data/lib/pmdtester/builders/diff_report_builder.rb +0 -225
- data/lib/pmdtester/builders/html_report_builder.rb +0 -33
- data/resources/css/maven-base.css +0 -155
- data/resources/css/maven-theme.css +0 -171
data/lib/pmdtester/pmd_error.rb
CHANGED
@@ -1,30 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module PmdTester
|
4
|
-
# This class is used to store pmd errors and its size.
|
5
|
-
class PmdErrors
|
6
|
-
attr_reader :errors
|
7
|
-
attr_reader :errors_size
|
8
|
-
|
9
|
-
def initialize
|
10
|
-
# key:filename as String => value:PmdError Array
|
11
|
-
@errors = {}
|
12
|
-
@errors_size = 0
|
13
|
-
end
|
14
|
-
|
15
|
-
def add_error_by_filename(filename, error)
|
16
|
-
if @errors.key?(filename)
|
17
|
-
@errors[filename].push(error)
|
18
|
-
else
|
19
|
-
@errors.store(filename, [error])
|
20
|
-
end
|
21
|
-
@errors_size += 1
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
4
|
# This class represents a 'error' element of Pmd xml report
|
26
5
|
# and which Pmd branch the 'error' is from
|
27
6
|
class PmdError
|
7
|
+
include PmdTester
|
28
8
|
# The pmd branch type, 'base' or 'patch'
|
29
9
|
attr_reader :branch
|
30
10
|
|
@@ -37,31 +17,51 @@ module PmdTester
|
|
37
17
|
# </xs:extension>
|
38
18
|
# </xs:simpleContent>
|
39
19
|
# </xs:complexType>
|
40
|
-
|
41
|
-
attr_accessor :
|
42
|
-
|
43
|
-
def initialize(attrs, branch)
|
44
|
-
@attrs = attrs
|
20
|
+
attr_accessor :stack_trace
|
21
|
+
attr_accessor :old_error
|
22
|
+
attr_reader :filename, :short_message
|
45
23
|
|
24
|
+
def initialize(branch:, filename:, short_message:)
|
46
25
|
@branch = branch
|
47
|
-
@
|
26
|
+
@stack_trace = ''
|
27
|
+
@changed = false
|
28
|
+
@short_message = short_message
|
29
|
+
@filename = filename
|
48
30
|
end
|
49
31
|
|
50
|
-
def
|
51
|
-
|
32
|
+
def short_filename
|
33
|
+
filename.gsub(%r{([^/]*+/)+}, '')
|
52
34
|
end
|
53
35
|
|
54
|
-
def
|
55
|
-
@
|
36
|
+
def changed?
|
37
|
+
@changed
|
56
38
|
end
|
57
39
|
|
58
40
|
def eql?(other)
|
59
|
-
filename.eql?(other.filename) &&
|
60
|
-
|
41
|
+
filename.eql?(other.filename) &&
|
42
|
+
short_message.eql?(other.short_message) &&
|
43
|
+
stack_trace.eql?(other.stack_trace)
|
61
44
|
end
|
62
45
|
|
63
46
|
def hash
|
64
|
-
[filename,
|
47
|
+
[filename, stack_trace].hash
|
48
|
+
end
|
49
|
+
|
50
|
+
def sort_key
|
51
|
+
filename
|
52
|
+
end
|
53
|
+
|
54
|
+
def try_merge?(other)
|
55
|
+
if branch != BASE &&
|
56
|
+
branch != other.branch &&
|
57
|
+
filename == other.filename &&
|
58
|
+
!changed? # not already changed
|
59
|
+
@changed = true
|
60
|
+
@old_error = other
|
61
|
+
true
|
62
|
+
else
|
63
|
+
false
|
64
|
+
end
|
65
65
|
end
|
66
66
|
end
|
67
67
|
end
|
@@ -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
|
-
|
34
|
-
|
30
|
+
PmdTester.logger.warn("#{report_info_path} doesn't exist")
|
31
|
+
PmdReportDetail.new
|
35
32
|
end
|
36
33
|
end
|
37
34
|
|
@@ -0,0 +1,57 @@
|
|
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
|
+
logger.info " with filter #{filter_set}" unless filter_set.nil?
|
43
|
+
project.compute_report_diff(base_branch, patch_branch, filter_set)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Build the diff reports and write them all
|
48
|
+
def build_html_reports(projects, base_branch_details, patch_branch_details, filter_set = nil)
|
49
|
+
compute_project_diffs(projects, base_branch_details.branch_name, patch_branch_details.branch_name,
|
50
|
+
filter_set)
|
51
|
+
|
52
|
+
SummaryReportBuilder.new.write_all_projects(projects,
|
53
|
+
base_branch_details,
|
54
|
+
patch_branch_details)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -1,23 +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
|
-
@violations.store(filename, violations)
|
17
|
-
@violations_size += violations.size
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
4
|
# This class represents a 'violation' element of Pmd xml report
|
22
5
|
# and which pmd branch the 'violation' is from
|
23
6
|
class PmdViolation
|
@@ -44,23 +27,80 @@ module PmdTester
|
|
44
27
|
# </xs:simpleContent>
|
45
28
|
# </xs:complexType>
|
46
29
|
|
47
|
-
attr_reader :
|
48
|
-
attr_accessor :
|
30
|
+
attr_reader :fname, :info_url, :line, :old_line, :old_message, :rule_name, :ruleset_name
|
31
|
+
attr_accessor :message
|
49
32
|
|
50
|
-
|
51
|
-
|
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:)
|
52
36
|
@branch = branch
|
53
|
-
@
|
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
|
54
86
|
end
|
55
87
|
|
56
88
|
def eql?(other)
|
57
|
-
|
58
|
-
|
59
|
-
|
89
|
+
rule_name.eql?(other.rule_name) &&
|
90
|
+
line.eql?(other.line) &&
|
91
|
+
fname.eql?(other.fname) &&
|
92
|
+
message.eql?(other.message)
|
60
93
|
end
|
61
94
|
|
62
95
|
def hash
|
63
|
-
[
|
96
|
+
[line, rule_name, message].hash
|
97
|
+
end
|
98
|
+
|
99
|
+
def to_liquid
|
100
|
+
{
|
101
|
+
'branch' => branch,
|
102
|
+
'changed' => changed?
|
103
|
+
}
|
64
104
|
end
|
65
105
|
end
|
66
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,6 +36,9 @@ module PmdTester
|
|
32
36
|
@exclude_pattern.push(ep.text)
|
33
37
|
end
|
34
38
|
|
39
|
+
@build_command = project.at_xpath('build-command')&.text
|
40
|
+
@auxclasspath_command = project.at_xpath('auxclasspath-command')&.text
|
41
|
+
|
35
42
|
@report_diff = nil
|
36
43
|
end
|
37
44
|
|
@@ -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
|
@@ -74,6 +85,14 @@ module PmdTester
|
|
74
85
|
end
|
75
86
|
end
|
76
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
|
+
|
77
96
|
def get_project_target_dir(branch_name)
|
78
97
|
branch_filename = PmdBranchDetail.branch_filename(branch_name)
|
79
98
|
dir = "target/reports/#{branch_filename}/#{@name}"
|
@@ -85,26 +104,12 @@ module PmdTester
|
|
85
104
|
"#{REPOSITORIES_PATH}/#{@name}"
|
86
105
|
end
|
87
106
|
|
88
|
-
def
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
def diff_report_index_path
|
95
|
-
"#{target_diff_report_path}/index.html"
|
96
|
-
end
|
97
|
-
|
98
|
-
def diff_report_index_ref_path
|
99
|
-
"./#{name}/index.html"
|
100
|
-
end
|
101
|
-
|
102
|
-
def diffs_exist?
|
103
|
-
@report_diff.diffs_exist?
|
104
|
-
end
|
105
|
-
|
106
|
-
def introduce_new_errors?
|
107
|
-
@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)
|
108
113
|
end
|
109
114
|
end
|
110
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
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
104
|
+
attr_reader :error_counts
|
105
|
+
attr_reader :violation_counts
|
106
|
+
attr_reader :configerror_counts
|
19
107
|
|
20
|
-
attr_accessor :
|
21
|
-
attr_accessor :
|
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 :
|
24
|
-
attr_accessor :
|
112
|
+
attr_accessor :rule_infos_union
|
113
|
+
attr_accessor :base_report
|
114
|
+
attr_accessor :patch_report
|
25
115
|
|
26
|
-
def initialize
|
27
|
-
@
|
28
|
-
@
|
29
|
-
@
|
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
|
-
@
|
32
|
-
@
|
33
|
-
@
|
121
|
+
@violation_diffs_by_file = {}
|
122
|
+
@error_diffs_by_file = {}
|
123
|
+
@configerror_diffs_by_rule = {}
|
34
124
|
|
35
|
-
@
|
36
|
-
@
|
37
|
-
@
|
125
|
+
@rule_infos_union = base_report.infos_by_rule.dup
|
126
|
+
@base_report = base_report
|
127
|
+
@patch_report = patch_report
|
38
128
|
|
39
|
-
@
|
40
|
-
|
41
|
-
|
42
|
-
@violation_diffs = {}
|
43
|
-
@error_diffs = {}
|
129
|
+
@violation_diffs_by_rule = {}
|
130
|
+
diff_with(patch_report)
|
44
131
|
end
|
45
132
|
|
46
|
-
def
|
47
|
-
|
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
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
@
|
55
|
-
@
|
56
|
-
|
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
|
-
|
59
|
-
@
|
60
|
-
|
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
|
67
|
-
|
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
|
-
@
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|
-
|
79
|
-
|
80
|
-
|
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(
|
84
|
-
|
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
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
102
|
-
|
103
|
-
|
104
|
-
|
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
|