pmdtester 1.0.0.pre.beta2 → 1.1.1
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 +67 -0
- data/.ci/files/env.gpg +1 -0
- data/.ci/inc/install-openjdk.inc +26 -0
- data/.ci/manual-integration-tests.sh +20 -0
- data/.github/workflows/build.yml +39 -0
- data/.github/workflows/manual-integration-tests.yml +32 -0
- data/.gitignore +9 -0
- data/.hoerc +1 -0
- data/.rubocop.yml +21 -2
- data/.rubocop_todo.yml +7 -8
- data/.ruby-version +1 -0
- data/Gemfile +1 -13
- data/History.md +108 -0
- data/Manifest.txt +60 -0
- data/README.rdoc +130 -7
- data/Rakefile +31 -17
- data/bin/pmdtester +1 -1
- data/config/all-java.xml +1 -1
- data/config/design.xml +1 -1
- data/config/projectlist_1_0_0.xsd +2 -1
- data/config/projectlist_1_1_0.xsd +31 -0
- data/lib/pmdtester.rb +48 -0
- data/lib/pmdtester/builders/liquid_renderer.rb +73 -0
- data/lib/pmdtester/builders/pmd_report_builder.rb +133 -64
- data/lib/pmdtester/builders/project_builder.rb +100 -0
- data/lib/pmdtester/builders/project_hasher.rb +126 -0
- data/lib/pmdtester/builders/rule_set_builder.rb +95 -51
- data/lib/pmdtester/builders/simple_progress_logger.rb +27 -0
- data/lib/pmdtester/builders/summary_report_builder.rb +62 -120
- data/lib/pmdtester/cmd.rb +15 -1
- data/lib/pmdtester/collection_by_file.rb +55 -0
- data/lib/pmdtester/parsers/options.rb +33 -10
- data/lib/pmdtester/parsers/pmd_report_document.rb +79 -29
- data/lib/pmdtester/parsers/projects_parser.rb +2 -6
- data/lib/pmdtester/pmd_branch_detail.rb +36 -13
- data/lib/pmdtester/pmd_configerror.rb +62 -0
- data/lib/pmdtester/pmd_error.rb +34 -34
- data/lib/pmdtester/pmd_report_detail.rb +10 -13
- data/lib/pmdtester/pmd_tester_utils.rb +57 -0
- data/lib/pmdtester/pmd_violation.rb +66 -26
- data/lib/pmdtester/project.rb +28 -25
- data/lib/pmdtester/report_diff.rb +194 -70
- data/lib/pmdtester/resource_locator.rb +4 -0
- data/lib/pmdtester/runner.rb +82 -57
- data/pmdtester.gemspec +64 -0
- 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 +131 -0
- data/resources/js/bootstrap.min.js +7 -0
- data/resources/js/code-snippets.js +66 -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 +136 -0
- data/resources/project_diff_report.html +205 -0
- data/resources/project_index.html +102 -0
- metadata +117 -44
- data/.travis.yml +0 -22
- data/lib/pmdtester/builders/diff_builder.rb +0 -35
- data/lib/pmdtester/builders/diff_report_builder.rb +0 -226
- data/lib/pmdtester/builders/html_report_builder.rb +0 -34
- data/lib/pmdtester/pmdtester.rb +0 -17
- data/resources/css/maven-base.css +0 -155
- data/resources/css/maven-theme.css +0 -171
|
@@ -1,149 +1,91 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative './html_report_builder'
|
|
4
|
-
require_relative '../pmd_branch_detail'
|
|
5
|
-
|
|
6
3
|
module PmdTester
|
|
7
4
|
# Building summary report to show the details about projects and pmd branchs
|
|
8
|
-
class SummaryReportBuilder
|
|
5
|
+
class SummaryReportBuilder
|
|
9
6
|
include PmdTester
|
|
10
|
-
|
|
11
|
-
|
|
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')
|
|
7
|
+
include LiquidRenderer
|
|
8
|
+
include ProjectHasher
|
|
22
9
|
|
|
23
|
-
|
|
24
|
-
|
|
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
|
|
10
|
+
REPORT_DIR = 'target/reports/diff'
|
|
11
|
+
BASE_CONFIG_NAME = 'base_config.xml'
|
|
12
|
+
PATCH_CONFIG_NAME = 'patch_config.xml'
|
|
42
13
|
|
|
43
|
-
def
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
build_branch_details_table(doc)
|
|
14
|
+
def write_all_projects(projects, base_details, patch_details)
|
|
15
|
+
projects.each do |project|
|
|
16
|
+
process_project(project, "#{REPORT_DIR}/#{project.name}")
|
|
47
17
|
end
|
|
48
|
-
|
|
18
|
+
logger.info 'Built all difference reports successfully!'
|
|
49
19
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
20
|
+
FileUtils.mkdir_p(REPORT_DIR)
|
|
21
|
+
write_structure(REPORT_DIR)
|
|
22
|
+
copy_configs(REPORT_DIR, base_details, patch_details)
|
|
23
|
+
write_index(REPORT_DIR, base_details, patch_details, projects)
|
|
24
|
+
logger.info "Built summary report successfully in #{REPORT_DIR}!"
|
|
55
25
|
end
|
|
56
26
|
|
|
57
|
-
|
|
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
|
|
27
|
+
private
|
|
66
28
|
|
|
67
|
-
def
|
|
68
|
-
|
|
69
|
-
|
|
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
|
|
29
|
+
def process_project(project, dir)
|
|
30
|
+
logger.info "Rendering #{project.name}..."
|
|
31
|
+
LiquidProjectRenderer.new.write_project_index(project, dir)
|
|
79
32
|
end
|
|
80
33
|
|
|
81
|
-
def
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
|
34
|
+
def write_structure(target_root)
|
|
35
|
+
logger.info 'Copying resources...'
|
|
36
|
+
copy_resource('css', target_root)
|
|
37
|
+
copy_resource('js', target_root)
|
|
95
38
|
end
|
|
96
39
|
|
|
97
|
-
def
|
|
98
|
-
|
|
40
|
+
def copy_configs(target_root, base_details, patch_details)
|
|
41
|
+
copy_file(base_details.target_branch_config_path, "#{target_root}/#{BASE_CONFIG_NAME}")
|
|
42
|
+
copy_file(patch_details.target_branch_config_path, "#{target_root}/#{PATCH_CONFIG_NAME}")
|
|
99
43
|
end
|
|
100
44
|
|
|
101
|
-
def
|
|
102
|
-
|
|
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
|
|
45
|
+
def copy_file(src, dest)
|
|
46
|
+
FileUtils.cp(src, dest) if File.exist?(src)
|
|
107
47
|
end
|
|
108
48
|
|
|
109
|
-
def
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
49
|
+
def write_index(target_root, base_details, patch_details, projects)
|
|
50
|
+
projects = projects.map do |p|
|
|
51
|
+
{
|
|
52
|
+
'name' => p.name,
|
|
53
|
+
'tag' => p.tag,
|
|
54
|
+
'report_url' => "./#{p.name}/index.html",
|
|
55
|
+
**report_diff_to_h(p.report_diff)
|
|
56
|
+
}
|
|
113
57
|
end
|
|
114
|
-
end
|
|
115
58
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
59
|
+
env = {
|
|
60
|
+
'comparison_url' => create_comparison_url(base_details, patch_details),
|
|
61
|
+
'base' => to_liquid(base_details, BASE_CONFIG_NAME),
|
|
62
|
+
'patch' => to_liquid(patch_details, PATCH_CONFIG_NAME),
|
|
63
|
+
'projects' => projects
|
|
64
|
+
}
|
|
65
|
+
logger.info 'Writing /index.html...'
|
|
66
|
+
render_and_write('project_index.html', "#{target_root}/index.html", env)
|
|
121
67
|
end
|
|
122
68
|
|
|
123
|
-
def
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
doc.th 'project branch/tag'
|
|
128
|
-
doc.th 'diff exist?'
|
|
129
|
-
doc.th 'introduce new errors?'
|
|
130
|
-
end
|
|
131
|
-
end
|
|
69
|
+
def create_comparison_url(base_details, patch_details)
|
|
70
|
+
base = CGI.escape(base_details.branch_name)
|
|
71
|
+
patch = CGI.escape(patch_details.branch_last_sha)
|
|
72
|
+
"https://github.com/pmd/pmd/compare/#{base}...#{patch}"
|
|
132
73
|
end
|
|
133
74
|
|
|
134
|
-
def
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
75
|
+
def to_liquid(details, config_name)
|
|
76
|
+
{
|
|
77
|
+
'tree_url' => "https://github.com/pmd/pmd/tree/#{CGI.escape(details.branch_last_sha)}",
|
|
78
|
+
'name' => details.branch_name,
|
|
79
|
+
'tip' => {
|
|
80
|
+
'sha' => details.branch_last_sha,
|
|
81
|
+
'message' => details.branch_last_message
|
|
82
|
+
},
|
|
83
|
+
'execution_time' => PmdReportDetail.convert_seconds(details.execution_time),
|
|
84
|
+
'jdk_info' => details.jdk_version,
|
|
85
|
+
'locale' => details.language,
|
|
86
|
+
'config_url' => config_name,
|
|
87
|
+
'pr_number' => details.pull_request
|
|
88
|
+
}
|
|
147
89
|
end
|
|
148
90
|
end
|
|
149
91
|
end
|
data/lib/pmdtester/cmd.rb
CHANGED
|
@@ -7,20 +7,34 @@ module PmdTester
|
|
|
7
7
|
class Cmd
|
|
8
8
|
extend PmdTester
|
|
9
9
|
def self.execute(cmd)
|
|
10
|
+
stdout, _stderr, _status = internal_execute(cmd)
|
|
11
|
+
stdout&.chomp!
|
|
12
|
+
stdout
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.stderr_of(cmd)
|
|
16
|
+
_stdout, stderr, _status = internal_execute(cmd)
|
|
17
|
+
stderr
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.internal_execute(cmd)
|
|
10
21
|
logger.debug "execute command '#{cmd}'"
|
|
11
22
|
|
|
12
23
|
stdout, stderr, status = Open3.capture3("#{cmd};")
|
|
13
24
|
|
|
14
25
|
logger.debug stdout
|
|
15
26
|
unless status.success?
|
|
27
|
+
logger.error stdout
|
|
16
28
|
logger.error stderr
|
|
17
29
|
raise CmdException.new(cmd, stderr)
|
|
18
30
|
end
|
|
19
31
|
|
|
20
32
|
stdout&.chomp!
|
|
21
33
|
|
|
22
|
-
stdout
|
|
34
|
+
[stdout, stderr, status]
|
|
23
35
|
end
|
|
36
|
+
|
|
37
|
+
private_class_method :internal_execute
|
|
24
38
|
end
|
|
25
39
|
|
|
26
40
|
# The exception should be raised when the shell command failed.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PmdTester
|
|
4
|
+
# A collection of things, grouped by file.
|
|
5
|
+
#
|
|
6
|
+
# (Note: this replaces PmdErrors and PmdViolations)
|
|
7
|
+
class CollectionByFile
|
|
8
|
+
def initialize
|
|
9
|
+
# a hash of filename -> [list of items]
|
|
10
|
+
@hash = Hash.new([])
|
|
11
|
+
@total = 0
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def add_all(filename, values)
|
|
15
|
+
return if values.empty?
|
|
16
|
+
|
|
17
|
+
if @hash.key?(filename)
|
|
18
|
+
@hash[filename].concat(values)
|
|
19
|
+
else
|
|
20
|
+
@hash[filename] = values
|
|
21
|
+
end
|
|
22
|
+
@total += values.size
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def total_size
|
|
26
|
+
@total
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def all_files
|
|
30
|
+
@hash.keys
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def num_files
|
|
34
|
+
@hash.size
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def all_values
|
|
38
|
+
@hash.values.flatten
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def each_value(&block)
|
|
42
|
+
@hash.each_value do |vs|
|
|
43
|
+
vs.each(&block)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def [](fname)
|
|
48
|
+
@hash[fname]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def to_h
|
|
52
|
+
@hash
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'slop'
|
|
4
|
-
require_relative '../pmdtester'
|
|
5
4
|
|
|
6
5
|
module PmdTester
|
|
7
6
|
class MissRequiredOptionError < StandardError; end
|
|
@@ -15,7 +14,9 @@ module PmdTester
|
|
|
15
14
|
LOCAL = 'local'
|
|
16
15
|
ONLINE = 'online'
|
|
17
16
|
SINGLE = 'single'
|
|
18
|
-
|
|
17
|
+
DEFAULT_CONFIG_PATH = ResourceLocator.locate('config/all-java.xml')
|
|
18
|
+
DEFAULT_LIST_PATH = ResourceLocator.locate('config/project-list.xml')
|
|
19
|
+
DEFAULT_BASELINE_URL_PREFIX = 'https://sourceforge.net/projects/pmd/files/pmd-regression-tester/'
|
|
19
20
|
|
|
20
21
|
attr_reader :local_git_repo
|
|
21
22
|
attr_reader :base_branch
|
|
@@ -25,10 +26,14 @@ module PmdTester
|
|
|
25
26
|
attr_reader :config
|
|
26
27
|
attr_reader :project_list
|
|
27
28
|
attr_reader :mode
|
|
29
|
+
attr_reader :threads
|
|
28
30
|
attr_reader :html_flag
|
|
29
31
|
attr_reader :auto_config_flag
|
|
30
32
|
attr_reader :debug_flag
|
|
31
33
|
attr_accessor :filter_set
|
|
34
|
+
attr_reader :keep_reports
|
|
35
|
+
attr_reader :error_recovery
|
|
36
|
+
attr_reader :baseline_download_url_prefix
|
|
32
37
|
|
|
33
38
|
def initialize(argv)
|
|
34
39
|
options = parse(argv)
|
|
@@ -40,10 +45,19 @@ module PmdTester
|
|
|
40
45
|
@config = options[:c]
|
|
41
46
|
@project_list = options[:l]
|
|
42
47
|
@mode = options[:m]
|
|
48
|
+
@threads = options[:t]
|
|
43
49
|
@html_flag = options[:f]
|
|
44
50
|
@auto_config_flag = options[:a]
|
|
45
51
|
@debug_flag = options[:d]
|
|
46
52
|
@filter_set = nil
|
|
53
|
+
@keep_reports = options.keep_reports?
|
|
54
|
+
@error_recovery = options.error_recovery?
|
|
55
|
+
url = options[:baseline_download_url]
|
|
56
|
+
@baseline_download_url_prefix = if url[-1] == '/'
|
|
57
|
+
url
|
|
58
|
+
else
|
|
59
|
+
"#{url}/"
|
|
60
|
+
end
|
|
47
61
|
|
|
48
62
|
# if the 'config' option is selected then `config` overrides `base_config` and `patch_config`
|
|
49
63
|
@base_config = @config if !@config.nil? && @mode == 'local'
|
|
@@ -61,8 +75,8 @@ module PmdTester
|
|
|
61
75
|
single: Set this option to 'single' if your patch branch contains changes
|
|
62
76
|
for any option that can't work on master/base branch
|
|
63
77
|
online: Set this option to 'online' if you want to download
|
|
64
|
-
|
|
65
|
-
local: Default option is 'local'
|
|
78
|
+
the PMD report of master/base branch rather than generating it locally
|
|
79
|
+
local: Default option is 'local', PMD reports for the base and patch branches are generated locally.
|
|
66
80
|
DOC
|
|
67
81
|
|
|
68
82
|
Slop.parse argv do |o|
|
|
@@ -70,19 +84,31 @@ module PmdTester
|
|
|
70
84
|
o.string '-b', '--base-branch', 'name of the base branch in local PMD repository'
|
|
71
85
|
o.string '-p', '--patch-branch',
|
|
72
86
|
'name of the patch branch in local PMD repository'
|
|
73
|
-
o.string '-bc', '--base-config', 'path to the base PMD configuration file'
|
|
74
|
-
|
|
87
|
+
o.string '-bc', '--base-config', 'path to the base PMD configuration file',
|
|
88
|
+
default: DEFAULT_CONFIG_PATH
|
|
89
|
+
o.string '-pc', '--patch-config', 'path to the patch PMD configuration file',
|
|
90
|
+
default: DEFAULT_CONFIG_PATH
|
|
75
91
|
o.string '-c', '--config', 'path to the base and patch PMD configuration file'
|
|
76
92
|
o.string '-l', '--list-of-project',
|
|
77
|
-
'path to the file which contains the list of standard projects'
|
|
93
|
+
'path to the file which contains the list of standard projects',
|
|
94
|
+
default: DEFAULT_LIST_PATH
|
|
78
95
|
o.string '-m', '--mode', mode_message, default: 'local'
|
|
96
|
+
o.integer '-t', '--threads', 'Sets the number of threads used by PMD.' \
|
|
97
|
+
' Set threads to 0 to disable multi-threading processing.', default: 1
|
|
79
98
|
o.bool '-f', '--html-flag',
|
|
80
99
|
'whether to not generate the html diff report in single mode'
|
|
81
100
|
o.bool '-a', '--auto-gen-config',
|
|
82
101
|
'whether to generate configurations automatically based on branch differences,' \
|
|
83
102
|
'this option only works in online and local mode'
|
|
103
|
+
o.bool '--keep-reports',
|
|
104
|
+
'whether to keep old reports and skip running PMD again if possible'
|
|
84
105
|
o.bool '-d', '--debug',
|
|
85
106
|
'whether change log level to DEBUG to see more information'
|
|
107
|
+
o.bool '--error-recovery',
|
|
108
|
+
'enable error recovery mode when executing PMD. Might help to analyze errors.'
|
|
109
|
+
o.string '--baseline-download-url',
|
|
110
|
+
'download url prefix from where to download the baseline in online mode',
|
|
111
|
+
default: DEFAULT_BASELINE_URL_PREFIX
|
|
86
112
|
o.on '-v', '--version' do
|
|
87
113
|
puts VERSION
|
|
88
114
|
exit
|
|
@@ -113,20 +139,17 @@ module PmdTester
|
|
|
113
139
|
def check_local_options
|
|
114
140
|
check_option(LOCAL, 'base branch name', @base_branch)
|
|
115
141
|
check_option(LOCAL, 'base branch config path', @base_config) unless @auto_config_flag
|
|
116
|
-
check_option(LOCAL, 'patch branch name', @patch_branch)
|
|
117
142
|
check_option(LOCAL, 'patch branch config path', @patch_config) unless @auto_config_flag
|
|
118
143
|
check_option(LOCAL, 'list of projects file path', @project_list)
|
|
119
144
|
end
|
|
120
145
|
|
|
121
146
|
def check_single_options
|
|
122
|
-
check_option(SINGLE, 'patch branch name', @patch_branch)
|
|
123
147
|
check_option(SINGLE, 'patch branch config path', @patch_config)
|
|
124
148
|
check_option(SINGLE, 'list of projects file path', @project_list)
|
|
125
149
|
end
|
|
126
150
|
|
|
127
151
|
def check_online_options
|
|
128
152
|
check_option(ONLINE, 'base branch name', @base_branch)
|
|
129
|
-
check_option(ONLINE, 'patch branch name', @patch_branch)
|
|
130
153
|
end
|
|
131
154
|
|
|
132
155
|
def check_common_options
|
|
@@ -1,82 +1,132 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'nokogiri'
|
|
4
|
-
require_relative '../pmd_violation'
|
|
5
|
-
require_relative '../pmd_error'
|
|
6
4
|
module PmdTester
|
|
7
5
|
# This class is used for registering types of events you are interested in handling.
|
|
8
6
|
# Also see: https://www.rubydoc.info/github/sparklemotion/nokogiri/Nokogiri/XML/SAX/Document
|
|
9
7
|
class PmdReportDocument < Nokogiri::XML::SAX::Document
|
|
10
8
|
attr_reader :violations
|
|
11
9
|
attr_reader :errors
|
|
10
|
+
attr_reader :configerrors
|
|
11
|
+
attr_reader :infos_by_rules
|
|
12
|
+
|
|
12
13
|
def initialize(branch_name, working_dir, filter_set = nil)
|
|
13
|
-
@violations =
|
|
14
|
-
@errors =
|
|
14
|
+
@violations = CollectionByFile.new
|
|
15
|
+
@errors = CollectionByFile.new
|
|
16
|
+
@configerrors = Hash.new { |hash, key| hash[key] = [] }
|
|
17
|
+
|
|
18
|
+
@infos_by_rules = {}
|
|
15
19
|
@current_violations = []
|
|
16
20
|
@current_violation = nil
|
|
17
21
|
@current_error = nil
|
|
18
|
-
@
|
|
19
|
-
@filename = ''
|
|
22
|
+
@current_configerror = nil
|
|
20
23
|
@filter_set = filter_set
|
|
21
24
|
@working_dir = working_dir
|
|
22
25
|
@branch_name = branch_name
|
|
26
|
+
|
|
27
|
+
@cur_text = String.new(capacity: 200)
|
|
23
28
|
end
|
|
24
29
|
|
|
25
30
|
def start_element(name, attrs = [])
|
|
26
31
|
attrs = attrs.to_h
|
|
27
|
-
@current_element = name
|
|
28
32
|
|
|
29
33
|
case name
|
|
30
34
|
when 'file'
|
|
31
|
-
|
|
32
|
-
@current_filename = remove_work_dir!(attrs['name'])
|
|
35
|
+
handle_start_file attrs
|
|
33
36
|
when 'violation'
|
|
34
|
-
|
|
37
|
+
handle_start_violation attrs
|
|
35
38
|
when 'error'
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
handle_start_error attrs
|
|
40
|
+
when 'configerror'
|
|
41
|
+
handle_start_configerror attrs
|
|
39
42
|
end
|
|
40
43
|
end
|
|
41
44
|
|
|
42
|
-
def
|
|
43
|
-
|
|
45
|
+
def characters(string)
|
|
46
|
+
@cur_text << remove_work_dir!(string)
|
|
44
47
|
end
|
|
45
48
|
|
|
46
|
-
def
|
|
47
|
-
@
|
|
49
|
+
def cdata_block(string)
|
|
50
|
+
@cur_text << remove_work_dir!(string)
|
|
48
51
|
end
|
|
49
52
|
|
|
50
53
|
def end_element(name)
|
|
51
54
|
case name
|
|
52
55
|
when 'file'
|
|
53
|
-
|
|
54
|
-
@violations.add_violations_by_filename(@current_filename, @current_violations)
|
|
55
|
-
end
|
|
56
|
+
@violations.add_all(@current_filename, @current_violations)
|
|
56
57
|
@current_filename = nil
|
|
57
58
|
when 'violation'
|
|
58
|
-
|
|
59
|
+
if match_filter_set?(@current_violation)
|
|
60
|
+
@current_violation.message = finish_text!
|
|
61
|
+
@current_violations.push(@current_violation)
|
|
62
|
+
end
|
|
59
63
|
@current_violation = nil
|
|
60
64
|
when 'error'
|
|
61
|
-
@
|
|
65
|
+
@current_error.stack_trace = finish_text!
|
|
66
|
+
@errors.add_all(@current_filename, [@current_error])
|
|
62
67
|
@current_filename = nil
|
|
63
68
|
@current_error = nil
|
|
69
|
+
when 'configerror'
|
|
70
|
+
@configerrors[@current_configerror.rulename].push(@current_configerror)
|
|
71
|
+
@current_configerror = nil
|
|
64
72
|
end
|
|
73
|
+
@cur_text.clear
|
|
65
74
|
end
|
|
66
75
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
# Modifies the string in place and returns it
|
|
79
|
+
# (this is what sub! does, except it returns nil if no replacement occurred)
|
|
80
|
+
def remove_work_dir!(str)
|
|
81
|
+
str.sub!(/#{@working_dir}/, '')
|
|
82
|
+
str
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def finish_text!
|
|
86
|
+
res = @cur_text.strip!.dup.freeze
|
|
87
|
+
@cur_text.clear
|
|
88
|
+
res
|
|
70
89
|
end
|
|
71
90
|
|
|
72
91
|
def match_filter_set?(violation)
|
|
73
92
|
return true if @filter_set.nil?
|
|
74
93
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
94
|
+
ruleset_attr = violation.ruleset_name.delete(' ').downcase! << '.xml'
|
|
95
|
+
return true if @filter_set.include?(ruleset_attr)
|
|
96
|
+
|
|
97
|
+
rule_ref = "#{ruleset_attr}/#{violation.rule_name}"
|
|
98
|
+
|
|
99
|
+
@filter_set.include?(rule_ref)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def handle_start_file(attrs)
|
|
103
|
+
@current_filename = remove_work_dir!(attrs['name'])
|
|
104
|
+
@current_violations = []
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def handle_start_violation(attrs)
|
|
108
|
+
@current_violation = PmdViolation.new(
|
|
109
|
+
branch: @branch_name,
|
|
110
|
+
fname: @current_filename,
|
|
111
|
+
info_url: attrs['externalInfoUrl'],
|
|
112
|
+
bline: attrs['beginline'].to_i,
|
|
113
|
+
rule_name: attrs['rule'],
|
|
114
|
+
ruleset_name: attrs['ruleset'].freeze
|
|
115
|
+
)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def handle_start_error(attrs)
|
|
119
|
+
@current_filename = remove_work_dir!(attrs['filename'])
|
|
120
|
+
|
|
121
|
+
@current_error = PmdError.new(
|
|
122
|
+
branch: @branch_name,
|
|
123
|
+
filename: @current_filename,
|
|
124
|
+
short_message: remove_work_dir!(attrs['msg'])
|
|
125
|
+
)
|
|
126
|
+
end
|
|
78
127
|
|
|
79
|
-
|
|
128
|
+
def handle_start_configerror(attrs)
|
|
129
|
+
@current_configerror = PmdConfigError.new(attrs, @branch_name)
|
|
80
130
|
end
|
|
81
131
|
end
|
|
82
132
|
end
|