pmdtester 1.0.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ci/build.sh +99 -0
- data/.ci/inc/fetch_ci_scripts.bash +19 -0
- data/.ci/manual-integration-tests.sh +37 -0
- data/.github/workflows/build.yml +55 -0
- data/.github/workflows/manual-integration-tests.yml +43 -0
- data/.gitignore +9 -0
- data/.hoerc +1 -1
- data/.rubocop.yml +9 -2
- data/.ruby-version +1 -0
- data/History.md +79 -0
- data/Manifest.txt +28 -9
- data/README.rdoc +59 -33
- data/Rakefile +7 -5
- data/config/all-java.xml +1 -1
- data/config/design.xml +1 -1
- data/config/project-list.xml +8 -7
- data/config/projectlist_1_0_0.xsd +2 -1
- data/config/projectlist_1_1_0.xsd +31 -0
- data/config/projectlist_1_2_0.xsd +39 -0
- data/lib/pmdtester.rb +8 -7
- data/lib/pmdtester/builders/liquid_renderer.rb +130 -0
- data/lib/pmdtester/builders/pmd_report_builder.rb +107 -79
- data/lib/pmdtester/builders/project_builder.rb +105 -0
- data/lib/pmdtester/builders/project_hasher.rb +128 -0
- data/lib/pmdtester/builders/rule_set_builder.rb +96 -47
- data/lib/pmdtester/builders/simple_progress_logger.rb +4 -4
- data/lib/pmdtester/builders/summary_report_builder.rb +63 -131
- data/lib/pmdtester/collection_by_file.rb +55 -0
- data/lib/pmdtester/parsers/options.rb +24 -0
- data/lib/pmdtester/parsers/pmd_report_document.rb +72 -28
- data/lib/pmdtester/parsers/projects_parser.rb +2 -4
- data/lib/pmdtester/pmd_branch_detail.rb +35 -19
- data/lib/pmdtester/pmd_configerror.rb +23 -24
- data/lib/pmdtester/pmd_error.rb +34 -34
- data/lib/pmdtester/pmd_report_detail.rb +10 -13
- data/lib/pmdtester/pmd_tester_utils.rb +58 -0
- data/lib/pmdtester/pmd_violation.rb +66 -28
- data/lib/pmdtester/project.rb +42 -56
- data/lib/pmdtester/report_diff.rb +203 -109
- data/lib/pmdtester/resource_locator.rb +4 -0
- data/lib/pmdtester/runner.rb +67 -64
- data/pmdtester.gemspec +28 -37
- 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 +137 -0
- data/resources/project_diff_report.html +214 -0
- data/resources/project_index.html +113 -0
- data/resources/project_pmd_report.html +186 -0
- metadata +73 -25
- data/.travis.yml +0 -40
- data/lib/pmdtester/builders/diff_builder.rb +0 -31
- data/lib/pmdtester/builders/diff_report/configerrors.rb +0 -50
- data/lib/pmdtester/builders/diff_report/errors.rb +0 -71
- data/lib/pmdtester/builders/diff_report/violations.rb +0 -77
- data/lib/pmdtester/builders/diff_report_builder.rb +0 -99
- data/lib/pmdtester/builders/html_report_builder.rb +0 -56
- data/resources/css/maven-base.css +0 -155
- data/resources/css/maven-theme.css +0 -171
@@ -8,92 +8,74 @@ module PmdTester
|
|
8
8
|
# projects and branch of pmd source code
|
9
9
|
class PmdReportBuilder
|
10
10
|
include PmdTester
|
11
|
-
|
12
|
-
|
11
|
+
|
12
|
+
def initialize(projects, options, branch_config, branch_name)
|
13
13
|
@projects = projects
|
14
|
-
@local_git_repo = local_git_repo
|
15
|
-
@
|
16
|
-
@
|
14
|
+
@local_git_repo = options.local_git_repo
|
15
|
+
@threads = options.threads
|
16
|
+
@error_recovery = options.error_recovery
|
17
|
+
@branch_config = branch_config
|
18
|
+
@pmd_branch_name = branch_name
|
17
19
|
@pwd = Dir.getwd
|
18
20
|
|
19
|
-
@pmd_branch_details = PmdBranchDetail.new(pmd_branch_name)
|
20
|
-
|
21
|
-
|
22
|
-
def execute_reset_cmd(type, tag)
|
23
|
-
case type
|
24
|
-
when 'git'
|
25
|
-
reset_cmd = "git reset --hard #{tag}"
|
26
|
-
when 'hg'
|
27
|
-
reset_cmd = "hg up #{tag}"
|
28
|
-
end
|
29
|
-
|
30
|
-
Cmd.execute(reset_cmd)
|
31
|
-
end
|
32
|
-
|
33
|
-
def clone_projects
|
34
|
-
logger.info 'Cloning projects started'
|
35
|
-
|
36
|
-
@projects.each do |project|
|
37
|
-
logger.info "Start cloning #{project.name} repository"
|
38
|
-
path = project.local_source_path
|
39
|
-
clone_cmd = "#{project.type} clone #{project.connection} #{path}"
|
40
|
-
if File.exist?(path)
|
41
|
-
logger.warn "Skipping clone, project path #{path} already exists"
|
42
|
-
else
|
43
|
-
Cmd.execute(clone_cmd)
|
44
|
-
end
|
45
|
-
|
46
|
-
Dir.chdir(path) do
|
47
|
-
execute_reset_cmd(project.type, project.tag)
|
48
|
-
end
|
49
|
-
logger.info "Cloning #{project.name} completed"
|
50
|
-
end
|
51
|
-
|
52
|
-
logger.info 'Cloning projects completed'
|
21
|
+
@pmd_branch_details = PmdBranchDetail.new(@pmd_branch_name)
|
22
|
+
@project_builder = ProjectBuilder.new(@projects)
|
53
23
|
end
|
54
24
|
|
55
25
|
def get_pmd_binary_file
|
56
26
|
logger.info "#{@pmd_branch_name}: Start packaging PMD"
|
57
27
|
Dir.chdir(@local_git_repo) do
|
58
|
-
|
59
|
-
|
28
|
+
build_branch_sha = Cmd.execute("git rev-parse #{@pmd_branch_name}^{commit}")
|
29
|
+
|
30
|
+
checkout_build_branch # needs a clean working tree, otherwise fails
|
60
31
|
|
61
|
-
|
32
|
+
raise "Wrong branch #{get_last_commit_sha}" unless build_branch_sha == get_last_commit_sha
|
62
33
|
|
63
|
-
|
64
|
-
#
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
34
|
+
distro_path = saved_distro_path(build_branch_sha)
|
35
|
+
logger.debug "#{@pmd_branch_name}: PMD Version is #{@pmd_version} " \
|
36
|
+
"(sha=#{build_branch_sha})"
|
37
|
+
logger.debug "#{@pmd_branch_name}: distro_path=#{distro_path}"
|
38
|
+
if File.directory?(distro_path)
|
39
|
+
logger.info "#{@pmd_branch_name}: Skipping packaging - saved version exists " \
|
40
|
+
" in #{distro_path}"
|
69
41
|
else
|
70
|
-
build_pmd
|
42
|
+
build_pmd(into_dir: distro_path)
|
71
43
|
end
|
72
44
|
|
73
|
-
|
45
|
+
# we're still on the build branch
|
46
|
+
@pmd_branch_details.branch_last_sha = build_branch_sha
|
74
47
|
@pmd_branch_details.branch_last_message = get_last_commit_message
|
75
|
-
|
76
|
-
logger.info "#{@pmd_branch_name}: Extracting the zip"
|
77
|
-
unzip_cmd = "unzip -qo pmd-dist/target/pmd-bin-#{@pmd_version}.zip -d #{@pwd}/target"
|
78
|
-
Cmd.execute(unzip_cmd)
|
79
48
|
end
|
80
49
|
logger.info "#{@pmd_branch_name}: Packaging PMD completed"
|
81
50
|
end
|
82
51
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
@
|
52
|
+
# builds pmd on currently checked out branch
|
53
|
+
def build_pmd(into_dir:)
|
54
|
+
# in CI there might have been a build performed already. In that case
|
55
|
+
# we reuse the build result, otherwise we build PMD freshly
|
56
|
+
pmd_dist_target = "pmd-dist/target/pmd-bin-#{@pmd_version}.zip"
|
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
|
60
|
+
# that's a warning, because we don't know, whether this build really
|
61
|
+
# belongs to the current branch or whether it's from a previous branch.
|
62
|
+
# In CI, that's not a problem, because the workspace is always fresh.
|
63
|
+
logger.warn "#{@pmd_branch_name}: Reusing already existing #{pmd_dist_target}"
|
64
|
+
else
|
65
|
+
logger.info "#{@pmd_branch_name}: Building PMD #{@pmd_version}..."
|
66
|
+
package_cmd = './mvnw clean package' \
|
67
|
+
' -Dmaven.test.skip=true' \
|
68
|
+
' -Dmaven.javadoc.skip=true' \
|
69
|
+
' -Dmaven.source.skip=true' \
|
70
|
+
' -Dcheckstyle.skip=true' \
|
71
|
+
' -Dpmd.skip=true' \
|
72
|
+
' -T1C -B'
|
73
|
+
Cmd.execute(package_cmd)
|
74
|
+
end
|
90
75
|
|
91
|
-
logger.info "#{@pmd_branch_name}:
|
92
|
-
|
93
|
-
|
94
|
-
' -Dmaven.javadoc.skip=true' \
|
95
|
-
' -Dmaven.source.skip=true'
|
96
|
-
Cmd.execute(package_cmd)
|
76
|
+
logger.info "#{@pmd_branch_name}: Extracting the zip"
|
77
|
+
Cmd.execute("unzip -qo #{pmd_dist_target} -d pmd-dist/target/exploded")
|
78
|
+
Cmd.execute("mv pmd-dist/target/exploded/pmd-bin-#{@pmd_version} #{into_dir}")
|
97
79
|
end
|
98
80
|
|
99
81
|
def determine_pmd_version
|
@@ -103,7 +85,7 @@ module PmdTester
|
|
103
85
|
end
|
104
86
|
|
105
87
|
def get_last_commit_sha
|
106
|
-
get_last_commit_sha_cmd = 'git rev-parse HEAD'
|
88
|
+
get_last_commit_sha_cmd = 'git rev-parse HEAD^{commit}'
|
107
89
|
Cmd.execute(get_last_commit_sha_cmd)
|
108
90
|
end
|
109
91
|
|
@@ -113,27 +95,39 @@ module PmdTester
|
|
113
95
|
end
|
114
96
|
|
115
97
|
def generate_pmd_report(project)
|
116
|
-
|
117
|
-
|
98
|
+
error_recovery_options = @error_recovery ? 'PMD_JAVA_OPTS="-Dpmd.error_recovery -ea" ' : ''
|
99
|
+
run_path = "#{saved_distro_path(@pmd_branch_details.branch_last_sha)}/bin/run.sh"
|
100
|
+
pmd_cmd = "#{error_recovery_options}" \
|
101
|
+
"#{run_path} pmd -d #{project.local_source_path} -f xml " \
|
118
102
|
"-R #{project.get_config_path(@pmd_branch_name)} " \
|
119
103
|
"-r #{project.get_pmd_report_path(@pmd_branch_name)} " \
|
120
|
-
"-failOnViolation false -t #{@threads}"
|
104
|
+
"-failOnViolation false -t #{@threads} " \
|
105
|
+
"#{project.auxclasspath}"
|
121
106
|
start_time = Time.now
|
122
|
-
|
107
|
+
if File.exist?(project.get_pmd_report_path(@pmd_branch_name))
|
108
|
+
logger.warn "#{@pmd_branch_name}: Skipping PMD run - report " \
|
109
|
+
"#{project.get_pmd_report_path(@pmd_branch_name)} already exists"
|
110
|
+
else
|
111
|
+
Cmd.execute(pmd_cmd)
|
112
|
+
end
|
123
113
|
end_time = Time.now
|
124
114
|
[end_time - start_time, end_time]
|
125
115
|
end
|
126
116
|
|
127
117
|
def generate_config_for(project)
|
118
|
+
logger.debug "Generating ruleset with excludes from #{@branch_config}"
|
128
119
|
doc = Nokogiri::XML(File.read(@branch_config))
|
129
120
|
ruleset = doc.at_css('ruleset')
|
130
|
-
|
131
|
-
|
121
|
+
ruleset.add_child("\n")
|
122
|
+
project.exclude_patterns.each do |exclude_pattern|
|
123
|
+
ruleset.add_child(" <exclude-pattern>#{exclude_pattern}</exclude-pattern>\n")
|
132
124
|
end
|
133
125
|
|
134
126
|
File.open(project.get_config_path(@pmd_branch_name), 'w') do |x|
|
135
127
|
x << doc.to_s
|
136
128
|
end
|
129
|
+
|
130
|
+
logger.debug "Created file #{project.get_config_path(@pmd_branch_name)}"
|
137
131
|
end
|
138
132
|
|
139
133
|
def generate_pmd_reports
|
@@ -141,16 +135,14 @@ module PmdTester
|
|
141
135
|
|
142
136
|
sum_time = 0
|
143
137
|
@projects.each do |project|
|
144
|
-
progress_logger = SimpleProgressLogger.new(project.name)
|
138
|
+
progress_logger = SimpleProgressLogger.new("generating #{project.name}'s PMD report")
|
145
139
|
progress_logger.start
|
146
140
|
generate_config_for(project)
|
147
141
|
execution_time, end_time = generate_pmd_report(project)
|
148
142
|
progress_logger.stop
|
149
143
|
sum_time += execution_time
|
150
144
|
|
151
|
-
report_details = PmdReportDetail.new
|
152
|
-
report_details.execution_time = execution_time
|
153
|
-
report_details.timestamp = end_time
|
145
|
+
report_details = PmdReportDetail.new(execution_time: execution_time, timestamp: end_time)
|
154
146
|
report_details.save(project.get_report_info_path(@pmd_branch_name))
|
155
147
|
logger.info "#{project.name}'s PMD report was generated successfully"
|
156
148
|
end
|
@@ -161,10 +153,46 @@ module PmdTester
|
|
161
153
|
@pmd_branch_details
|
162
154
|
end
|
163
155
|
|
156
|
+
# returns the branch details
|
164
157
|
def build
|
165
|
-
clone_projects
|
158
|
+
@project_builder.clone_projects
|
159
|
+
@project_builder.build_projects
|
166
160
|
get_pmd_binary_file
|
167
161
|
generate_pmd_reports
|
168
162
|
end
|
163
|
+
|
164
|
+
private
|
165
|
+
|
166
|
+
def checkout_build_branch
|
167
|
+
logger.info "#{@pmd_branch_name}: Checking out the branch"
|
168
|
+
# note that this would fail if the tree is dirty
|
169
|
+
Cmd.execute("git checkout #{@pmd_branch_name}")
|
170
|
+
|
171
|
+
# determine the version
|
172
|
+
@pmd_version = determine_pmd_version
|
173
|
+
|
174
|
+
return unless wd_has_dirty_git_changes
|
175
|
+
|
176
|
+
# working dir is dirty....
|
177
|
+
# we don't allow this because we need the SHA to address the zip file
|
178
|
+
logger.error "#{@pmd_branch_name}: Won\'t build without a clean working tree, " \
|
179
|
+
'commit your changes'
|
180
|
+
end
|
181
|
+
|
182
|
+
def work_dir
|
183
|
+
"#{@pwd}/target"
|
184
|
+
end
|
185
|
+
|
186
|
+
# path to the unzipped distribution
|
187
|
+
# e.g. <cwd>/pmd-bin-<version>-<branch>-<sha>
|
188
|
+
def saved_distro_path(build_sha)
|
189
|
+
"#{work_dir}/pmd-bin-#{@pmd_version}" \
|
190
|
+
"-#{PmdBranchDetail.branch_filename(@pmd_branch_name)}" \
|
191
|
+
"-#{build_sha}"
|
192
|
+
end
|
193
|
+
|
194
|
+
def wd_has_dirty_git_changes
|
195
|
+
!Cmd.execute('git status --porcelain').empty?
|
196
|
+
end
|
169
197
|
end
|
170
198
|
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require 'tempfile'
|
5
|
+
|
6
|
+
module PmdTester
|
7
|
+
# Clones and builds the projects, that are configured in the project-list.xml
|
8
|
+
class ProjectBuilder
|
9
|
+
include PmdTester
|
10
|
+
|
11
|
+
def initialize(projects)
|
12
|
+
@projects = projects
|
13
|
+
end
|
14
|
+
|
15
|
+
def clone_projects
|
16
|
+
logger.info 'Cloning projects started'
|
17
|
+
|
18
|
+
@projects.each do |project|
|
19
|
+
logger.info "Start cloning #{project.name} repository"
|
20
|
+
path = project.clone_root_path
|
21
|
+
|
22
|
+
if File.exist?(path)
|
23
|
+
logger.warn "Skipping clone, project path #{path} already exists"
|
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
|
+
|
33
|
+
Cmd.execute(clone_cmd)
|
34
|
+
end
|
35
|
+
|
36
|
+
Dir.chdir(path) do
|
37
|
+
execute_reset_cmd(project.type, project.tag)
|
38
|
+
end
|
39
|
+
logger.info "Cloning #{project.name} completed"
|
40
|
+
end
|
41
|
+
|
42
|
+
logger.info 'Cloning projects completed'
|
43
|
+
end
|
44
|
+
|
45
|
+
def build_projects
|
46
|
+
logger.info 'Building projects started'
|
47
|
+
|
48
|
+
@projects.each do |project|
|
49
|
+
path = project.clone_root_path
|
50
|
+
Dir.chdir(path) do
|
51
|
+
progress_logger = SimpleProgressLogger.new("building #{project.name} in #{path}")
|
52
|
+
progress_logger.start
|
53
|
+
prepare_project(project)
|
54
|
+
progress_logger.stop
|
55
|
+
end
|
56
|
+
logger.info "Building #{project.name} completed"
|
57
|
+
end
|
58
|
+
|
59
|
+
logger.info 'Building projects completed'
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def prepare_project(project)
|
65
|
+
# Note: current working directory is the project directory,
|
66
|
+
# where the source code has been cloned to
|
67
|
+
if project.build_command
|
68
|
+
logger.debug "Executing build-command: #{project.build_command}"
|
69
|
+
run_as_script(Dir.getwd, project.build_command)
|
70
|
+
end
|
71
|
+
if project.auxclasspath_command
|
72
|
+
logger.debug "Executing auxclasspath-command: #{project.auxclasspath_command}"
|
73
|
+
auxclasspath = run_as_script(Dir.getwd, project.auxclasspath_command)
|
74
|
+
project.auxclasspath = "-auxclasspath #{auxclasspath}"
|
75
|
+
else
|
76
|
+
project.auxclasspath = ''
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def run_as_script(path, command)
|
81
|
+
script = Tempfile.new(['pmd-regression-', '.sh'], path)
|
82
|
+
logger.debug "Creating script #{script.path}"
|
83
|
+
begin
|
84
|
+
script.write(command)
|
85
|
+
script.close
|
86
|
+
shell = 'sh -xe'
|
87
|
+
if command.start_with?('#!')
|
88
|
+
shell = command.lines[0].chomp[2..] # remove leading "#!"
|
89
|
+
end
|
90
|
+
stdout = Cmd.execute("#{shell} #{script.path}")
|
91
|
+
ensure
|
92
|
+
script.unlink
|
93
|
+
end
|
94
|
+
stdout
|
95
|
+
end
|
96
|
+
|
97
|
+
def execute_reset_cmd(type, tag)
|
98
|
+
raise "Unsupported project type '#{type}' - only git is supported" unless type == 'git'
|
99
|
+
|
100
|
+
reset_cmd = "git checkout #{tag}; git reset --hard #{tag}"
|
101
|
+
|
102
|
+
Cmd.execute(reset_cmd)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'differ'
|
4
|
+
|
5
|
+
module PmdTester
|
6
|
+
# Turn a project report into a hash that can be rendered somewhere else
|
7
|
+
module ProjectHasher
|
8
|
+
include PmdTester
|
9
|
+
|
10
|
+
def report_diff_to_h(rdiff)
|
11
|
+
{
|
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
|
+
|
16
|
+
'base_execution_time' => PmdReportDetail.convert_seconds(rdiff.base_report.exec_time),
|
17
|
+
'patch_execution_time' => PmdReportDetail.convert_seconds(rdiff.patch_report.exec_time),
|
18
|
+
'diff_execution_time' => PmdReportDetail.convert_seconds(rdiff.patch_report.exec_time -
|
19
|
+
rdiff.base_report.exec_time),
|
20
|
+
|
21
|
+
'base_timestamp' => rdiff.base_report.timestamp,
|
22
|
+
'patch_timestamp' => rdiff.patch_report.timestamp,
|
23
|
+
|
24
|
+
'rule_diffs' => rdiff.rule_summaries
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def violations_to_hash(project, violations_by_file, is_diff)
|
29
|
+
filename_index = []
|
30
|
+
all_vs = []
|
31
|
+
violations_by_file.each do |file, vs|
|
32
|
+
file_ref = filename_index.size
|
33
|
+
filename_index.push(project.get_local_path(file))
|
34
|
+
vs.each do |v|
|
35
|
+
all_vs.push(make_violation_hash(file_ref, v, is_diff))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
{
|
40
|
+
'file_index' => filename_index,
|
41
|
+
'violations' => all_vs
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
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) }
|
48
|
+
end
|
49
|
+
|
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) }
|
53
|
+
end
|
54
|
+
|
55
|
+
def link_template(project)
|
56
|
+
l_str = project.type == 'git' ? 'L' : 'l'
|
57
|
+
"#{project.webview_url}/{file}##{l_str}{line}"
|
58
|
+
end
|
59
|
+
|
60
|
+
def error_to_hash(error, project)
|
61
|
+
escaped_stacktrace = sanitize_stacktrace(error)
|
62
|
+
old_stacktrace = error.old_error.nil? ? nil : sanitize_stacktrace(error.old_error)
|
63
|
+
|
64
|
+
{
|
65
|
+
'file_url' => project.get_webview_url(error.filename),
|
66
|
+
'stack_trace_html' => escaped_stacktrace,
|
67
|
+
'old_stack_trace_html' => old_stacktrace,
|
68
|
+
'short_message' => error.short_message,
|
69
|
+
'short_filename' => error.short_filename,
|
70
|
+
'filename' => error.filename,
|
71
|
+
'change_type' => change_type(error)
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
def sanitize_stacktrace(error)
|
76
|
+
CGI.escapeHTML(error.stack_trace)
|
77
|
+
.gsub(error.filename, '<span class="meta-var">$FILE</span>')
|
78
|
+
.gsub(/\w++(?=\(\w++\.java:\d++\))/, '<span class="stack-trace-method">\\0</span>')
|
79
|
+
end
|
80
|
+
|
81
|
+
def configerror_to_hash(configerror)
|
82
|
+
{
|
83
|
+
'rule' => configerror.rulename,
|
84
|
+
'message' => configerror.msg,
|
85
|
+
'change_type' => change_type(configerror)
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
def change_type(item)
|
90
|
+
if item.branch == BASE
|
91
|
+
'removed'
|
92
|
+
elsif item.changed?
|
93
|
+
'changed'
|
94
|
+
else
|
95
|
+
'added'
|
96
|
+
end
|
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
|
127
|
+
end
|
128
|
+
end
|