pmdtester 1.0.0.pre.beta2
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 +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
|