pmdtester 1.0.0 → 1.2.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 +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
|