pmdtester 1.0.0.pre.beta2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rubocop.yml +10 -0
- data/.rubocop_todo.yml +27 -0
- data/.travis.yml +22 -0
- data/Gemfile +20 -0
- data/History.md +36 -0
- data/LICENSE +25 -0
- data/README.rdoc +33 -0
- data/Rakefile +54 -0
- data/bin/pmdtester +7 -0
- data/config/all-java.xml +18 -0
- data/config/design.xml +78 -0
- data/config/project-list.xml +29 -0
- data/config/projectlist_1_0_0.xsd +28 -0
- data/lib/pmdtester/builders/diff_builder.rb +35 -0
- data/lib/pmdtester/builders/diff_report_builder.rb +226 -0
- data/lib/pmdtester/builders/html_report_builder.rb +34 -0
- data/lib/pmdtester/builders/pmd_report_builder.rb +128 -0
- data/lib/pmdtester/builders/rule_set_builder.rb +114 -0
- data/lib/pmdtester/builders/summary_report_builder.rb +149 -0
- data/lib/pmdtester/cmd.rb +40 -0
- data/lib/pmdtester/parsers/options.rb +147 -0
- data/lib/pmdtester/parsers/pmd_report_document.rb +82 -0
- data/lib/pmdtester/parsers/projects_parser.rb +41 -0
- data/lib/pmdtester/pmd_branch_detail.rb +67 -0
- data/lib/pmdtester/pmd_error.rb +67 -0
- data/lib/pmdtester/pmd_report_detail.rb +47 -0
- data/lib/pmdtester/pmd_violation.rb +66 -0
- data/lib/pmdtester/pmdtester.rb +17 -0
- data/lib/pmdtester/project.rb +112 -0
- data/lib/pmdtester/report_diff.rb +111 -0
- data/lib/pmdtester/resource_locator.rb +10 -0
- data/lib/pmdtester/runner.rb +130 -0
- data/resources/css/maven-base.css +155 -0
- data/resources/css/maven-theme.css +171 -0
- metadata +249 -0
@@ -0,0 +1,226 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'nokogiri'
|
4
|
+
require_relative './html_report_builder'
|
5
|
+
|
6
|
+
module PmdTester
|
7
|
+
# Building diff report for a single project
|
8
|
+
class DiffReportBuilder < HtmlReportBuilder
|
9
|
+
include PmdTester
|
10
|
+
NO_DIFFERENCES_MESSAGE = 'No differences found!'
|
11
|
+
|
12
|
+
def build(project)
|
13
|
+
@project = project
|
14
|
+
@report_diff = project.report_diff
|
15
|
+
|
16
|
+
index = File.new(project.diff_report_index_path, 'w')
|
17
|
+
|
18
|
+
html_report = build_html_report('pmd xml difference report')
|
19
|
+
copy_css(project.target_diff_report_path)
|
20
|
+
|
21
|
+
index.puts html_report
|
22
|
+
index.close
|
23
|
+
|
24
|
+
logger.info "Built difference report of #{project.name} successfully!"
|
25
|
+
end
|
26
|
+
|
27
|
+
def build_body(doc)
|
28
|
+
violation_diffs = @report_diff.violation_diffs
|
29
|
+
error_diffs = @report_diff.error_diffs
|
30
|
+
doc.body(class: 'composite') do
|
31
|
+
doc.div(id: 'contentBox') do
|
32
|
+
build_summary_section(doc)
|
33
|
+
build_violations_section(doc, violation_diffs)
|
34
|
+
build_errors_section(doc, error_diffs)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def build_summary_section(doc)
|
40
|
+
doc.div(class: 'section', id: 'Summary') do
|
41
|
+
doc.h2 'Summary:'
|
42
|
+
build_summary_table(doc)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def build_summary_table(doc)
|
47
|
+
doc.table(class: 'bodyTable', border: '0') do
|
48
|
+
doc.thead do
|
49
|
+
doc.tr do
|
50
|
+
doc.th 'Item'
|
51
|
+
doc.th 'Base'
|
52
|
+
doc.th 'Patch'
|
53
|
+
doc.th 'Difference'
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
build_summary_table_body(doc)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def build_summary_table_body(doc)
|
62
|
+
doc.tbody do
|
63
|
+
build_summary_row(doc, 'number of errors', @report_diff.base_errors_size,
|
64
|
+
@report_diff.patch_errors_size, @report_diff.error_diffs_size)
|
65
|
+
build_summary_row(doc, 'number of violations', @report_diff.base_violations_size,
|
66
|
+
@report_diff.patch_violations_size, @report_diff.violation_diffs_size)
|
67
|
+
build_summary_row(doc, 'execution time', @report_diff.base_execution_time,
|
68
|
+
@report_diff.patch_execution_time, @report_diff.diff_execution_time)
|
69
|
+
build_summary_row(doc, 'timestamp', @report_diff.base_timestamp,
|
70
|
+
@report_diff.patch_timestamp, '')
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def build_summary_row(doc, item, base, patch, diff)
|
75
|
+
doc.tr do
|
76
|
+
doc.td(class: 'c') { doc.text item }
|
77
|
+
doc.td(class: 'a') { doc.text base }
|
78
|
+
doc.td(class: 'b') { doc.text patch }
|
79
|
+
doc.td(class: 'c') { doc.text diff }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def build_filename_h3(doc, filename)
|
84
|
+
doc.h3 do
|
85
|
+
doc.a(href: @project.get_webview_url(filename)) do
|
86
|
+
doc.text @project.get_path_inside_project(filename)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def build_violations_section(doc, violation_diffs)
|
92
|
+
doc.div(class: 'section', id: 'Violations') do
|
93
|
+
doc.h2 'Violations:'
|
94
|
+
|
95
|
+
doc.h3 NO_DIFFERENCES_MESSAGE if violation_diffs.empty?
|
96
|
+
violation_diffs.each do |key, value|
|
97
|
+
doc.div(class: 'section') do
|
98
|
+
build_filename_h3(doc, key)
|
99
|
+
build_violation_table(doc, key, value)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def build_violation_table(doc, key, value)
|
106
|
+
doc.table(class: 'bodyTable', border: '0') do
|
107
|
+
build_violation_table_head(doc)
|
108
|
+
build_violation_table_body(doc, key, value)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def build_violation_table_head(doc)
|
113
|
+
doc.thead do
|
114
|
+
doc.tr do
|
115
|
+
doc.th
|
116
|
+
doc.th 'priority'
|
117
|
+
doc.th 'Rule'
|
118
|
+
doc.th 'Message'
|
119
|
+
doc.th 'Line'
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def build_violation_table_body(doc, key, value)
|
125
|
+
doc.tbody do
|
126
|
+
a_index = 1
|
127
|
+
value.each do |pmd_violation|
|
128
|
+
build_violation_table_row(doc, key, pmd_violation, a_index)
|
129
|
+
a_index += 1
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def build_violation_table_row(doc, key, pmd_violation, a_index)
|
135
|
+
doc.tr(class: pmd_violation.branch == 'base' ? 'a' : 'b') do
|
136
|
+
# The anchor
|
137
|
+
doc.td do
|
138
|
+
doc.a(id: "A#{a_index}", href: "#A#{a_index}") { doc.text '#' }
|
139
|
+
end
|
140
|
+
|
141
|
+
violation = pmd_violation.attrs
|
142
|
+
|
143
|
+
# The priority of the rule
|
144
|
+
doc.td violation['priority']
|
145
|
+
|
146
|
+
# The rule that trigger the violation
|
147
|
+
doc.td do
|
148
|
+
doc.a(href: (violation['externalInfoUrl']).to_s) { doc.text violation['rule'] }
|
149
|
+
end
|
150
|
+
|
151
|
+
# The violation message
|
152
|
+
doc.td pmd_violation.text
|
153
|
+
|
154
|
+
# The begin line of the violation
|
155
|
+
line = violation['beginline']
|
156
|
+
|
157
|
+
# The link to the source file
|
158
|
+
doc.td do
|
159
|
+
link = get_link_to_source(violation, key)
|
160
|
+
doc.a(href: link.to_s) { doc.text line }
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def get_link_to_source(violation, key)
|
166
|
+
l_str = @project.type == 'git' ? 'L' : 'l'
|
167
|
+
line_str = "##{l_str}#{violation['beginline']}"
|
168
|
+
@project.get_webview_url(key) + line_str
|
169
|
+
end
|
170
|
+
|
171
|
+
def build_errors_section(doc, error_diffs)
|
172
|
+
doc.div(class: 'section', id: 'Errors') do
|
173
|
+
doc.h2 do
|
174
|
+
doc.text 'Errors:'
|
175
|
+
end
|
176
|
+
|
177
|
+
doc.h3 NO_DIFFERENCES_MESSAGE if error_diffs.empty?
|
178
|
+
error_diffs.each do |key, value|
|
179
|
+
doc.div(class: 'section') do
|
180
|
+
build_filename_h3(doc, key)
|
181
|
+
build_errors_table(doc, value)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def build_errors_table(doc, errors)
|
188
|
+
doc.table(class: 'bodyTable', border: '0') do
|
189
|
+
build_errors_table_head(doc)
|
190
|
+
build_errors_table_body(doc, errors)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def build_errors_table_head(doc)
|
195
|
+
doc.thead do
|
196
|
+
doc.tr do
|
197
|
+
doc.th
|
198
|
+
doc.th 'Message'
|
199
|
+
doc.th 'Details'
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def build_errors_table_body(doc, errors)
|
205
|
+
doc.tbody do
|
206
|
+
b_index = 1
|
207
|
+
errors.each do |pmd_error|
|
208
|
+
doc.tr(class: pmd_error.branch == 'base' ? 'a' : 'b') do
|
209
|
+
# The anchor
|
210
|
+
doc.td do
|
211
|
+
doc.a(id: "B#{b_index}", href: "#B#{b_index}") { doc.text '#' }
|
212
|
+
end
|
213
|
+
|
214
|
+
# The error message
|
215
|
+
doc.td pmd_error.msg
|
216
|
+
|
217
|
+
# Details of error
|
218
|
+
doc.td pmd_error.text
|
219
|
+
|
220
|
+
b_index += 1
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../resource_locator'
|
4
|
+
module PmdTester
|
5
|
+
# This class is the parent of all classes which is used to build html report
|
6
|
+
class HtmlReportBuilder
|
7
|
+
CSS_SRC_DIR = ResourceLocator.locate('resources/css')
|
8
|
+
|
9
|
+
def build_html_report(title_name)
|
10
|
+
html_builder = Nokogiri::HTML::Builder.new do |doc|
|
11
|
+
doc.html do
|
12
|
+
build_head(doc, title_name)
|
13
|
+
build_body(doc)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
html_builder.to_html
|
17
|
+
end
|
18
|
+
|
19
|
+
def build_head(doc, title_name)
|
20
|
+
doc.head do
|
21
|
+
doc.title title_name
|
22
|
+
|
23
|
+
doc.style(type: 'text/css', media: 'all') do
|
24
|
+
doc.text '@import url("./css/maven-base.css");@import url("./css/maven-theme.css");'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def copy_css(report_dir)
|
30
|
+
css_dest_dir = "#{report_dir}/css"
|
31
|
+
FileUtils.copy_entry(CSS_SRC_DIR, css_dest_dir)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require_relative '../cmd'
|
5
|
+
require_relative '../project'
|
6
|
+
require_relative '../pmd_branch_detail'
|
7
|
+
require_relative '../pmd_report_detail'
|
8
|
+
|
9
|
+
module PmdTester
|
10
|
+
# Building pmd xml reports according to a list of standard
|
11
|
+
# projects and branch of pmd source code
|
12
|
+
class PmdReportBuilder
|
13
|
+
include PmdTester
|
14
|
+
def initialize(branch_config, projects, local_git_repo, pmd_branch_name)
|
15
|
+
@branch_config = branch_config
|
16
|
+
@projects = projects
|
17
|
+
@local_git_repo = local_git_repo
|
18
|
+
@pmd_branch_name = pmd_branch_name
|
19
|
+
@pwd = Dir.getwd
|
20
|
+
|
21
|
+
@pmd_branch_details = PmdBranchDetail.new(pmd_branch_name)
|
22
|
+
end
|
23
|
+
|
24
|
+
def execute_reset_cmd(type, tag)
|
25
|
+
case type
|
26
|
+
when 'git'
|
27
|
+
reset_cmd = "git reset --hard #{tag}"
|
28
|
+
when 'hg'
|
29
|
+
reset_cmd = "hg up #{tag}"
|
30
|
+
end
|
31
|
+
|
32
|
+
Cmd.execute(reset_cmd)
|
33
|
+
end
|
34
|
+
|
35
|
+
def get_projects
|
36
|
+
logger.info 'Cloning projects started'
|
37
|
+
|
38
|
+
@projects.each do |project|
|
39
|
+
logger.info "Start cloning #{project.name} repository"
|
40
|
+
path = project.local_source_path
|
41
|
+
clone_cmd = "#{project.type} clone #{project.connection} #{path}"
|
42
|
+
if File.exist?(path)
|
43
|
+
logger.warn "Skipping clone, project path #{path} already exists"
|
44
|
+
else
|
45
|
+
Cmd.execute(clone_cmd)
|
46
|
+
end
|
47
|
+
|
48
|
+
Dir.chdir(path) do
|
49
|
+
execute_reset_cmd(project.type, project.tag)
|
50
|
+
end
|
51
|
+
logger.info "Cloning #{project.name} completed"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def get_pmd_binary_file
|
56
|
+
logger.info 'Start packaging PMD'
|
57
|
+
Dir.chdir(@local_git_repo) do
|
58
|
+
checkout_cmd = "git checkout #{@pmd_branch_name}"
|
59
|
+
Cmd.execute(checkout_cmd)
|
60
|
+
|
61
|
+
@pmd_branch_details.branch_last_sha = get_last_commit_sha
|
62
|
+
@pmd_branch_details.branch_last_message = get_last_commit_message
|
63
|
+
|
64
|
+
package_cmd = './mvnw clean package -Dpmd.skip=true -Dmaven.test.skip=true' \
|
65
|
+
' -Dmaven.checkstyle.skip=true -Dmaven.javadoc.skip=true'
|
66
|
+
Cmd.execute(package_cmd)
|
67
|
+
|
68
|
+
version_cmd = "./mvnw -q -Dexec.executable=\"echo\" -Dexec.args='${project.version}' " \
|
69
|
+
'--non-recursive org.codehaus.mojo:exec-maven-plugin:1.5.0:exec'
|
70
|
+
@pmd_version = Cmd.execute(version_cmd)
|
71
|
+
|
72
|
+
unzip_cmd = "unzip -qo pmd-dist/target/pmd-bin-#{@pmd_version}.zip -d #{@pwd}/target"
|
73
|
+
Cmd.execute(unzip_cmd)
|
74
|
+
end
|
75
|
+
logger.info 'Packaging PMD completed'
|
76
|
+
end
|
77
|
+
|
78
|
+
def get_last_commit_sha
|
79
|
+
get_last_commit_sha_cmd = 'git rev-parse HEAD'
|
80
|
+
Cmd.execute(get_last_commit_sha_cmd)
|
81
|
+
end
|
82
|
+
|
83
|
+
def get_last_commit_message
|
84
|
+
get_last_commit_message_cmd = 'git log -1 --pretty=%B'
|
85
|
+
Cmd.execute(get_last_commit_message_cmd)
|
86
|
+
end
|
87
|
+
|
88
|
+
def generate_pmd_report(src_root_dir, report_file)
|
89
|
+
run_path = "target/pmd-bin-#{@pmd_version}/bin/run.sh"
|
90
|
+
pmd_cmd = "#{run_path} pmd -d #{src_root_dir} -f xml -R #{@branch_config} " \
|
91
|
+
"-r #{report_file} -failOnViolation false"
|
92
|
+
start_time = Time.now
|
93
|
+
Cmd.execute(pmd_cmd)
|
94
|
+
end_time = Time.now
|
95
|
+
[end_time - start_time, end_time]
|
96
|
+
end
|
97
|
+
|
98
|
+
def generate_pmd_reports
|
99
|
+
logger.info "Generating PMD report started -- branch #{@pmd_branch_name}"
|
100
|
+
get_pmd_binary_file
|
101
|
+
|
102
|
+
sum_time = 0
|
103
|
+
@projects.each do |project|
|
104
|
+
logger.info "Generating #{project.name}'s PMD report'"
|
105
|
+
execution_time, end_time =
|
106
|
+
generate_pmd_report(project.local_source_path,
|
107
|
+
project.get_pmd_report_path(@pmd_branch_name))
|
108
|
+
sum_time += execution_time
|
109
|
+
|
110
|
+
report_details = PmdReportDetail.new
|
111
|
+
report_details.execution_time = execution_time
|
112
|
+
report_details.timestamp = end_time
|
113
|
+
report_details.save(project.get_report_info_path(@pmd_branch_name))
|
114
|
+
logger.info "#{project.name}'s PMD report was generated successfully"
|
115
|
+
end
|
116
|
+
|
117
|
+
@pmd_branch_details.execution_time = sum_time
|
118
|
+
@pmd_branch_details.save
|
119
|
+
FileUtils.cp(@branch_config, @pmd_branch_details.target_branch_config_path)
|
120
|
+
@pmd_branch_details
|
121
|
+
end
|
122
|
+
|
123
|
+
def build
|
124
|
+
get_projects
|
125
|
+
generate_pmd_reports
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'nokogiri'
|
4
|
+
require 'set'
|
5
|
+
require_relative '../cmd'
|
6
|
+
require_relative '../resource_locator'
|
7
|
+
|
8
|
+
module PmdTester
|
9
|
+
# This class is responsible for generation dynamic configuration
|
10
|
+
# according to the difference between base and patch branch of Pmd.
|
11
|
+
# Attention: we only consider java rulesets now.
|
12
|
+
class RuleSetBuilder
|
13
|
+
include PmdTester
|
14
|
+
ALL_RULE_SETS = Set['bestpractices', 'codestyle', 'design', 'documentation',
|
15
|
+
'errorprone', 'multithreading', 'performance', 'security'].freeze
|
16
|
+
PATH_TO_PMD_JAVA_BASED_RULES =
|
17
|
+
'pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule'
|
18
|
+
PATH_TO_PMD_XPATH_BASED_RULES = 'pmd-java/src/main/resources/category/java'
|
19
|
+
PATH_TO_ALL_JAVA_RULES =
|
20
|
+
ResourceLocator.locate('config/all-java.xml')
|
21
|
+
PATH_TO_DYNAMIC_CONFIG = 'target/dynamic-config.xml'
|
22
|
+
NO_JAVA_RULES_CHANGED_MESSAGE = 'No java rules have been changed!'
|
23
|
+
|
24
|
+
def initialize(options)
|
25
|
+
@options = options
|
26
|
+
end
|
27
|
+
|
28
|
+
def build
|
29
|
+
filenames = diff_filenames
|
30
|
+
rule_sets = get_rule_sets(filenames)
|
31
|
+
output_filter_set(rule_sets)
|
32
|
+
build_config_file(rule_sets)
|
33
|
+
logger.debug "Dynamic configuration: #{[rule_sets]}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def output_filter_set(rule_sets)
|
37
|
+
if rule_sets == ALL_RULE_SETS
|
38
|
+
# if `rule_sets` contains all rule sets, than no need to filter the baseline
|
39
|
+
@options.filter_set = nil
|
40
|
+
else
|
41
|
+
@options.filter_set = rule_sets
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def diff_filenames
|
46
|
+
filenames = nil
|
47
|
+
Dir.chdir(@options.local_git_repo) do
|
48
|
+
base = @options.base_branch
|
49
|
+
patch = @options.patch_branch
|
50
|
+
# We only need to support git here, since PMD's repo is using git.
|
51
|
+
diff_cmd = "git diff --name-only #{base}..#{patch} -- pmd/core pmd/java"
|
52
|
+
filenames = Cmd.execute(diff_cmd)
|
53
|
+
end
|
54
|
+
filenames.split("\n")
|
55
|
+
end
|
56
|
+
|
57
|
+
def get_rule_sets(filenames)
|
58
|
+
rule_sets = Set[]
|
59
|
+
filenames.each do |filename|
|
60
|
+
match_data = %r{#{PATH_TO_PMD_JAVA_BASED_RULES}/([^/]+)/[^/]+Rule.java}.match(filename)
|
61
|
+
if match_data.nil?
|
62
|
+
match_data = %r{#{PATH_TO_PMD_XPATH_BASED_RULES}/([^/]+).xml}.match(filename)
|
63
|
+
end
|
64
|
+
|
65
|
+
category = if match_data.nil?
|
66
|
+
nil
|
67
|
+
else
|
68
|
+
match_data[1]
|
69
|
+
end
|
70
|
+
|
71
|
+
if category.nil?
|
72
|
+
rule_sets = ALL_RULE_SETS
|
73
|
+
break
|
74
|
+
else
|
75
|
+
rule_sets.add(category)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
rule_sets
|
79
|
+
end
|
80
|
+
|
81
|
+
def build_config_file(rule_sets)
|
82
|
+
if rule_sets.empty?
|
83
|
+
puts NO_JAVA_RULES_CHANGED_MESSAGE
|
84
|
+
exit 0
|
85
|
+
end
|
86
|
+
|
87
|
+
doc = Nokogiri::XML(File.read(PATH_TO_ALL_JAVA_RULES))
|
88
|
+
doc.search('rule').each do |rule|
|
89
|
+
rule.remove unless match_ref?(rule, rule_sets)
|
90
|
+
end
|
91
|
+
|
92
|
+
description = doc.at_css('description')
|
93
|
+
description.content = 'The ruleset generated by PmdTester dynamically'
|
94
|
+
|
95
|
+
write_dynamic_file(doc)
|
96
|
+
end
|
97
|
+
|
98
|
+
def match_ref?(rule_node, rule_sets)
|
99
|
+
rule_sets.each do |rule_set|
|
100
|
+
return true unless rule_node['ref'].index(rule_set).nil?
|
101
|
+
end
|
102
|
+
|
103
|
+
false
|
104
|
+
end
|
105
|
+
|
106
|
+
def write_dynamic_file(doc)
|
107
|
+
File.open(PATH_TO_DYNAMIC_CONFIG, 'w') do |x|
|
108
|
+
x << doc.to_s.gsub(/\n\s+\n/, "\n")
|
109
|
+
end
|
110
|
+
@options.base_config = PATH_TO_DYNAMIC_CONFIG
|
111
|
+
@options.patch_config = PATH_TO_DYNAMIC_CONFIG
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './html_report_builder'
|
4
|
+
require_relative '../pmd_branch_detail'
|
5
|
+
|
6
|
+
module PmdTester
|
7
|
+
# Building summary report to show the details about projects and pmd branchs
|
8
|
+
class SummaryReportBuilder < HtmlReportBuilder
|
9
|
+
include PmdTester
|
10
|
+
REPORT_DIR = 'target/reports/diff'
|
11
|
+
BASE_CONFIG_PATH = 'target/reports/diff/base_config.xml'
|
12
|
+
PATCH_CONFIG_PATH = 'target/reports/diff/patch_config.xml'
|
13
|
+
INDEX_PATH = 'target/reports/diff/index.html'
|
14
|
+
|
15
|
+
def build(projects, base_name, patch_name)
|
16
|
+
@projects = projects
|
17
|
+
@base_details = get_branch_details(base_name)
|
18
|
+
@patch_details = get_branch_details(patch_name)
|
19
|
+
|
20
|
+
FileUtils.mkdir_p(REPORT_DIR) unless File.directory?(REPORT_DIR)
|
21
|
+
index = File.new(INDEX_PATH, 'w')
|
22
|
+
|
23
|
+
html_report = build_html_report('Summary report')
|
24
|
+
copy_css(REPORT_DIR)
|
25
|
+
|
26
|
+
index.puts html_report
|
27
|
+
index.close
|
28
|
+
|
29
|
+
logger.info 'Built summary report successfully!'
|
30
|
+
end
|
31
|
+
|
32
|
+
def get_branch_details(branch_name)
|
33
|
+
details = PmdBranchDetail.new(branch_name)
|
34
|
+
details.load
|
35
|
+
details
|
36
|
+
end
|
37
|
+
|
38
|
+
def build_body(doc)
|
39
|
+
build_branch_details_section(doc)
|
40
|
+
build_projects_section(doc)
|
41
|
+
end
|
42
|
+
|
43
|
+
def build_branch_details_section(doc)
|
44
|
+
doc.div(class: 'section', id: 'branchdetails') do
|
45
|
+
doc.h2 'Branch details:'
|
46
|
+
build_branch_details_table(doc)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def build_branch_details_table(doc)
|
51
|
+
doc.table(class: 'bodyTable', border: '0') do
|
52
|
+
build_branch_table_head(doc)
|
53
|
+
build_branch_table_body(doc)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def build_branch_table_head(doc)
|
58
|
+
doc.thead do
|
59
|
+
doc.tr do
|
60
|
+
doc.th 'Item'
|
61
|
+
doc.th 'base'
|
62
|
+
doc.th 'patch'
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def build_branch_table_body(doc)
|
68
|
+
doc.tbody do
|
69
|
+
build_branch_table_row(doc, 'branch name', @base_details.branch_name,
|
70
|
+
@patch_details.branch_name)
|
71
|
+
build_branch_table_row(doc, 'branch last commit sha', @base_details.branch_last_sha,
|
72
|
+
@patch_details.branch_last_sha)
|
73
|
+
build_branch_table_row(doc, 'branch last commit message', @base_details.branch_last_message,
|
74
|
+
@patch_details.branch_last_message)
|
75
|
+
build_branch_table_row(doc, 'total execution time', @base_details.format_execution_time,
|
76
|
+
@patch_details.format_execution_time)
|
77
|
+
build_branch_config_table_row(doc)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def build_branch_config_table_row(doc)
|
82
|
+
doc.tr do
|
83
|
+
doc.td(class: 'c') { doc.text 'branch configuration' }
|
84
|
+
base_config_src_path = @base_details.target_branch_config_path
|
85
|
+
copy_branch_config_file(base_config_src_path, BASE_CONFIG_PATH)
|
86
|
+
doc.td(class: 'a') do
|
87
|
+
doc.a(href: './base_config.xml') { doc.text 'base config' }
|
88
|
+
end
|
89
|
+
patch_config_stc_path = @patch_details.target_branch_config_path
|
90
|
+
FileUtils.cp(patch_config_stc_path, PATCH_CONFIG_PATH)
|
91
|
+
doc.td(class: 'b') do
|
92
|
+
doc.a(href: './patch_config.xml') { doc.text 'patch config' }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def copy_branch_config_file(src, dest)
|
98
|
+
FileUtils.cp(src, dest) if File.exist?(src)
|
99
|
+
end
|
100
|
+
|
101
|
+
def build_branch_table_row(doc, item, base, patch)
|
102
|
+
doc.tr do
|
103
|
+
doc.td(class: 'c') { doc.text item }
|
104
|
+
doc.td(class: 'a') { doc.text base }
|
105
|
+
doc.td(class: 'b') { doc.text patch }
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def build_projects_section(doc)
|
110
|
+
doc.div(class: 'section', id: 'projects') do
|
111
|
+
doc.h2 'Projects:'
|
112
|
+
build_projects_table(doc)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def build_projects_table(doc)
|
117
|
+
doc.table(class: 'bodyTable', border: '0') do
|
118
|
+
build_projects_table_head(doc)
|
119
|
+
build_projects_table_body(doc)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def build_projects_table_head(doc)
|
124
|
+
doc.thead do
|
125
|
+
doc.tr do
|
126
|
+
doc.th 'project name'
|
127
|
+
doc.th 'project branch/tag'
|
128
|
+
doc.th 'diff exist?'
|
129
|
+
doc.th 'introduce new errors?'
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def build_projects_table_body(doc)
|
135
|
+
doc.tbody do
|
136
|
+
@projects.each do |project|
|
137
|
+
doc.tr do
|
138
|
+
doc.td do
|
139
|
+
doc.a(href: project.diff_report_index_ref_path) { doc.text project.name }
|
140
|
+
end
|
141
|
+
doc.td project.tag
|
142
|
+
doc.td project.diffs_exist? ? 'Yes' : 'No'
|
143
|
+
doc.td project.introduce_new_errors? ? 'Yes' : 'No'
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'open3'
|
4
|
+
|
5
|
+
module PmdTester
|
6
|
+
# Containing the common method for executing shell command
|
7
|
+
class Cmd
|
8
|
+
extend PmdTester
|
9
|
+
def self.execute(cmd)
|
10
|
+
logger.debug "execute command '#{cmd}'"
|
11
|
+
|
12
|
+
stdout, stderr, status = Open3.capture3("#{cmd};")
|
13
|
+
|
14
|
+
logger.debug stdout
|
15
|
+
unless status.success?
|
16
|
+
logger.error stderr
|
17
|
+
raise CmdException.new(cmd, stderr)
|
18
|
+
end
|
19
|
+
|
20
|
+
stdout&.chomp!
|
21
|
+
|
22
|
+
stdout
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# The exception should be raised when the shell command failed.
|
27
|
+
class CmdException < StandardError
|
28
|
+
attr_reader :cmd
|
29
|
+
attr_reader :error
|
30
|
+
attr_reader :message
|
31
|
+
|
32
|
+
COMMON_MSG = 'An error occurred while executing the shell command'
|
33
|
+
|
34
|
+
def initialize(cmd, error)
|
35
|
+
@cmd = cmd
|
36
|
+
@error = error
|
37
|
+
@message = "#{COMMON_MSG} '#{cmd}'"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|