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.
- checksums.yaml +4 -4
- data/.ci/build.sh +85 -77
- data/.ci/inc/fetch_ci_scripts.bash +19 -0
- data/.ci/manual-integration-tests.sh +37 -0
- data/.github/workflows/build.yml +26 -9
- data/.github/workflows/manual-integration-tests.yml +48 -0
- data/.hoerc +1 -1
- data/.rubocop.yml +3 -0
- data/History.md +44 -0
- data/Manifest.txt +6 -1
- data/README.rdoc +23 -18
- data/Rakefile +1 -1
- data/config/project-list.xml +8 -7
- data/config/projectlist_1_2_0.xsd +39 -0
- data/lib/pmdtester/builders/liquid_renderer.rb +60 -3
- data/lib/pmdtester/builders/pmd_report_builder.rb +15 -5
- data/lib/pmdtester/builders/project_builder.rb +14 -9
- data/lib/pmdtester/builders/project_hasher.rb +44 -42
- data/lib/pmdtester/builders/rule_set_builder.rb +4 -0
- data/lib/pmdtester/builders/summary_report_builder.rb +1 -0
- data/lib/pmdtester/parsers/options.rb +5 -0
- data/lib/pmdtester/parsers/pmd_report_document.rb +0 -2
- data/lib/pmdtester/parsers/projects_parser.rb +1 -1
- data/lib/pmdtester/pmd_branch_detail.rb +6 -0
- data/lib/pmdtester/pmd_report_detail.rb +1 -1
- data/lib/pmdtester/pmd_tester_utils.rb +5 -2
- data/lib/pmdtester/project.rb +24 -11
- data/lib/pmdtester/report_diff.rb +36 -10
- data/lib/pmdtester/runner.rb +2 -1
- data/lib/pmdtester/semver.rb +36 -0
- data/lib/pmdtester.rb +2 -1
- data/pmdtester.gemspec +8 -8
- data/resources/css/pmd-tester.css +18 -1
- data/resources/js/code-snippets.js +62 -22
- data/resources/js/project-report.js +6 -4
- data/resources/project_diff_report.html +9 -0
- data/resources/project_index.html +11 -0
- data/resources/project_pmd_report.html +186 -0
- metadata +15 -10
- 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
|
-
|
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
|
-
|
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
|
-
' -
|
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
|
-
"
|
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.
|
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.
|
21
|
-
|
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.
|
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
|
-
|
91
|
-
|
92
|
-
|
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
|
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
|
-
|
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
|
56
|
-
|
57
|
-
|
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
|
71
|
-
|
72
|
-
|
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
|
83
|
-
|
84
|
-
|
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
|
@@ -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
|
@@ -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
|
-
|
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,
|
data/lib/pmdtester/project.rb
CHANGED
@@ -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 :
|
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
|
-
@
|
37
|
+
@src_subpath = project.at_xpath('src-subpath')&.text || '.'
|
38
|
+
@exclude_patterns = []
|
35
39
|
project.xpath('exclude-pattern').each do |ep|
|
36
|
-
@
|
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{/#{
|
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{/#{
|
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{/#{
|
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
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
-
:
|
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
|
-
|
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 =
|
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
|
data/lib/pmdtester/runner.rb
CHANGED
@@ -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)
|