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,147 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'slop'
|
4
|
+
require_relative '../pmdtester'
|
5
|
+
|
6
|
+
module PmdTester
|
7
|
+
class MissRequiredOptionError < StandardError; end
|
8
|
+
class InvalidModeError < StandardError; end
|
9
|
+
|
10
|
+
# The Options is a class responsible of parsing all the
|
11
|
+
# command line options
|
12
|
+
class Options
|
13
|
+
include PmdTester
|
14
|
+
ANY = 'any'
|
15
|
+
LOCAL = 'local'
|
16
|
+
ONLINE = 'online'
|
17
|
+
SINGLE = 'single'
|
18
|
+
VERSION = '1.0.0-beta2'
|
19
|
+
|
20
|
+
attr_reader :local_git_repo
|
21
|
+
attr_reader :base_branch
|
22
|
+
attr_reader :patch_branch
|
23
|
+
attr_accessor :base_config
|
24
|
+
attr_accessor :patch_config
|
25
|
+
attr_reader :config
|
26
|
+
attr_reader :project_list
|
27
|
+
attr_reader :mode
|
28
|
+
attr_reader :html_flag
|
29
|
+
attr_reader :auto_config_flag
|
30
|
+
attr_reader :debug_flag
|
31
|
+
attr_accessor :filter_set
|
32
|
+
|
33
|
+
def initialize(argv)
|
34
|
+
options = parse(argv)
|
35
|
+
@local_git_repo = options[:r]
|
36
|
+
@base_branch = options[:b]
|
37
|
+
@patch_branch = options[:p]
|
38
|
+
@base_config = options[:bc]
|
39
|
+
@patch_config = options[:pc]
|
40
|
+
@config = options[:c]
|
41
|
+
@project_list = options[:l]
|
42
|
+
@mode = options[:m]
|
43
|
+
@html_flag = options[:f]
|
44
|
+
@auto_config_flag = options[:a]
|
45
|
+
@debug_flag = options[:d]
|
46
|
+
@filter_set = nil
|
47
|
+
|
48
|
+
# if the 'config' option is selected then `config` overrides `base_config` and `patch_config`
|
49
|
+
@base_config = @config if !@config.nil? && @mode == 'local'
|
50
|
+
@patch_config = @config if !@config.nil? && @mode == 'local'
|
51
|
+
|
52
|
+
logger.level = @debug_flag ? Logger::DEBUG : Logger::INFO
|
53
|
+
check_options
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def parse(argv)
|
59
|
+
mode_message = <<-DOC
|
60
|
+
the mode of the tool: 'local', 'online' or 'single'
|
61
|
+
single: Set this option to 'single' if your patch branch contains changes
|
62
|
+
for any option that can't work on master/base branch
|
63
|
+
online: Set this option to 'online' if you want to download
|
64
|
+
'the PMD report of master/base branch rather than generating it locally
|
65
|
+
local: Default option is 'local'
|
66
|
+
DOC
|
67
|
+
|
68
|
+
Slop.parse argv do |o|
|
69
|
+
o.string '-r', '--local-git-repo', 'path to the local PMD repository'
|
70
|
+
o.string '-b', '--base-branch', 'name of the base branch in local PMD repository'
|
71
|
+
o.string '-p', '--patch-branch',
|
72
|
+
'name of the patch branch in local PMD repository'
|
73
|
+
o.string '-bc', '--base-config', 'path to the base PMD configuration file'
|
74
|
+
o.string '-pc', '--patch-config', 'path to the patch PMD configuration file'
|
75
|
+
o.string '-c', '--config', 'path to the base and patch PMD configuration file'
|
76
|
+
o.string '-l', '--list-of-project',
|
77
|
+
'path to the file which contains the list of standard projects'
|
78
|
+
o.string '-m', '--mode', mode_message, default: 'local'
|
79
|
+
o.bool '-f', '--html-flag',
|
80
|
+
'whether to not generate the html diff report in single mode'
|
81
|
+
o.bool '-a', '--auto-gen-config',
|
82
|
+
'whether to generate configurations automatically based on branch differences,' \
|
83
|
+
'this option only works in online and local mode'
|
84
|
+
o.bool '-d', '--debug',
|
85
|
+
'whether change log level to DEBUG to see more information'
|
86
|
+
o.on '-v', '--version' do
|
87
|
+
puts VERSION
|
88
|
+
exit
|
89
|
+
end
|
90
|
+
o.on '-h', '--help' do
|
91
|
+
puts o
|
92
|
+
exit
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def check_options
|
98
|
+
check_common_options
|
99
|
+
case @mode
|
100
|
+
when LOCAL
|
101
|
+
check_local_options
|
102
|
+
when SINGLE
|
103
|
+
check_single_options
|
104
|
+
when ONLINE
|
105
|
+
check_online_options
|
106
|
+
else
|
107
|
+
msg = "The mode '#{@mode}' is invalid!"
|
108
|
+
logger.error msg
|
109
|
+
raise InvalidModeError, msg
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def check_local_options
|
114
|
+
check_option(LOCAL, 'base branch name', @base_branch)
|
115
|
+
check_option(LOCAL, 'base branch config path', @base_config) unless @auto_config_flag
|
116
|
+
check_option(LOCAL, 'patch branch name', @patch_branch)
|
117
|
+
check_option(LOCAL, 'patch branch config path', @patch_config) unless @auto_config_flag
|
118
|
+
check_option(LOCAL, 'list of projects file path', @project_list)
|
119
|
+
end
|
120
|
+
|
121
|
+
def check_single_options
|
122
|
+
check_option(SINGLE, 'patch branch name', @patch_branch)
|
123
|
+
check_option(SINGLE, 'patch branch config path', @patch_config)
|
124
|
+
check_option(SINGLE, 'list of projects file path', @project_list)
|
125
|
+
end
|
126
|
+
|
127
|
+
def check_online_options
|
128
|
+
check_option(ONLINE, 'base branch name', @base_branch)
|
129
|
+
check_option(ONLINE, 'patch branch name', @patch_branch)
|
130
|
+
end
|
131
|
+
|
132
|
+
def check_common_options
|
133
|
+
check_option(ANY, 'local git repository path', @local_git_repo)
|
134
|
+
check_option(ANY, 'patch branch name', @patch_branch)
|
135
|
+
end
|
136
|
+
|
137
|
+
def check_option(mode, option_name, option)
|
138
|
+
if option.nil?
|
139
|
+
msg = "#{option_name} is required in #{mode} mode."
|
140
|
+
logger.error msg
|
141
|
+
raise MissRequiredOptionError, msg
|
142
|
+
else
|
143
|
+
logger.info "#{option_name}: #{option}"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'nokogiri'
|
4
|
+
require_relative '../pmd_violation'
|
5
|
+
require_relative '../pmd_error'
|
6
|
+
module PmdTester
|
7
|
+
# This class is used for registering types of events you are interested in handling.
|
8
|
+
# Also see: https://www.rubydoc.info/github/sparklemotion/nokogiri/Nokogiri/XML/SAX/Document
|
9
|
+
class PmdReportDocument < Nokogiri::XML::SAX::Document
|
10
|
+
attr_reader :violations
|
11
|
+
attr_reader :errors
|
12
|
+
def initialize(branch_name, working_dir, filter_set = nil)
|
13
|
+
@violations = PmdViolations.new
|
14
|
+
@errors = PmdErrors.new
|
15
|
+
@current_violations = []
|
16
|
+
@current_violation = nil
|
17
|
+
@current_error = nil
|
18
|
+
@current_element = ''
|
19
|
+
@filename = ''
|
20
|
+
@filter_set = filter_set
|
21
|
+
@working_dir = working_dir
|
22
|
+
@branch_name = branch_name
|
23
|
+
end
|
24
|
+
|
25
|
+
def start_element(name, attrs = [])
|
26
|
+
attrs = attrs.to_h
|
27
|
+
@current_element = name
|
28
|
+
|
29
|
+
case name
|
30
|
+
when 'file'
|
31
|
+
@current_violations = []
|
32
|
+
@current_filename = remove_work_dir!(attrs['name'])
|
33
|
+
when 'violation'
|
34
|
+
@current_violation = PmdViolation.new(attrs, @branch_name)
|
35
|
+
when 'error'
|
36
|
+
@current_filename = remove_work_dir!(attrs['filename'])
|
37
|
+
remove_work_dir!(attrs['msg'])
|
38
|
+
@current_error = PmdError.new(attrs, @branch_name)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def remove_work_dir!(str)
|
43
|
+
str.sub!(/#{@working_dir}/, '')
|
44
|
+
end
|
45
|
+
|
46
|
+
def characters(string)
|
47
|
+
@current_violation.text = string unless @current_violation.nil?
|
48
|
+
end
|
49
|
+
|
50
|
+
def end_element(name)
|
51
|
+
case name
|
52
|
+
when 'file'
|
53
|
+
unless @current_violations.empty?
|
54
|
+
@violations.add_violations_by_filename(@current_filename, @current_violations)
|
55
|
+
end
|
56
|
+
@current_filename = nil
|
57
|
+
when 'violation'
|
58
|
+
@current_violations.push(@current_violation) if match_filter_set?(@current_violation)
|
59
|
+
@current_violation = nil
|
60
|
+
when 'error'
|
61
|
+
@errors.add_error_by_filename(@current_filename, @current_error)
|
62
|
+
@current_filename = nil
|
63
|
+
@current_error = nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def cdata_block(string)
|
68
|
+
remove_work_dir!(string)
|
69
|
+
@current_error.text = string unless @current_error.nil?
|
70
|
+
end
|
71
|
+
|
72
|
+
def match_filter_set?(violation)
|
73
|
+
return true if @filter_set.nil?
|
74
|
+
|
75
|
+
@filter_set.each do |ruleset|
|
76
|
+
return true if ruleset.eql?(violation.attrs['ruleset'].delete(' ').downcase)
|
77
|
+
end
|
78
|
+
|
79
|
+
false
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'nokogiri'
|
4
|
+
require_relative '../project'
|
5
|
+
require_relative '../resource_locator'
|
6
|
+
|
7
|
+
module PmdTester
|
8
|
+
# The ProjectsParser is a class responsible of parsing
|
9
|
+
# the projects XML file to get the Project object array
|
10
|
+
class ProjectsParser
|
11
|
+
def parse(list_file)
|
12
|
+
schema = Nokogiri::XML::Schema(File.read(schema_file_path))
|
13
|
+
document = Nokogiri::XML(File.read(list_file))
|
14
|
+
|
15
|
+
errors = schema.validate(document)
|
16
|
+
unless errors.empty?
|
17
|
+
raise ProjectsParserException.new(errors), "Schema validate failed: In #{list_file}"
|
18
|
+
end
|
19
|
+
|
20
|
+
projects = []
|
21
|
+
document.xpath('//project').each do |project|
|
22
|
+
projects.push(Project.new(project))
|
23
|
+
end
|
24
|
+
projects
|
25
|
+
end
|
26
|
+
|
27
|
+
def schema_file_path
|
28
|
+
ResourceLocator.locate('config/projectlist_1_0_0.xsd')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# When this exception is raised, it means that
|
33
|
+
# schema validate of 'project-list' xml file failed
|
34
|
+
class ProjectsParserException < RuntimeError
|
35
|
+
attr_reader :errors
|
36
|
+
|
37
|
+
def initialize(errors)
|
38
|
+
@errors = errors
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require_relative './pmd_report_detail'
|
5
|
+
|
6
|
+
module PmdTester
|
7
|
+
# This class represents all details about branch of pmd
|
8
|
+
class PmdBranchDetail
|
9
|
+
attr_accessor :branch_last_sha
|
10
|
+
attr_accessor :branch_last_message
|
11
|
+
attr_accessor :branch_name
|
12
|
+
# The branch's execution time on all standard projects
|
13
|
+
attr_accessor :execution_time
|
14
|
+
|
15
|
+
def self.branch_filename(branch_name)
|
16
|
+
branch_name&.tr('/', '_')
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(branch_name)
|
20
|
+
@branch_last_sha = ''
|
21
|
+
@branch_last_message = ''
|
22
|
+
@branch_name = branch_name
|
23
|
+
branch_filename = PmdBranchDetail.branch_filename(branch_name)
|
24
|
+
@base_branch_dir = "target/reports/#{branch_filename}" unless @branch_name.nil?
|
25
|
+
@execution_time = 0
|
26
|
+
end
|
27
|
+
|
28
|
+
def load
|
29
|
+
if File.exist?(branch_details_path)
|
30
|
+
hash = JSON.parse(File.read(branch_details_path))
|
31
|
+
@branch_last_sha = hash['branch_last_sha']
|
32
|
+
@branch_last_message = hash['branch_last_message']
|
33
|
+
@branch_name = hash['branch_name']
|
34
|
+
@execution_time = hash['execution_time']
|
35
|
+
hash
|
36
|
+
else
|
37
|
+
{}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def save
|
42
|
+
hash = { branch_last_sha: @branch_last_sha,
|
43
|
+
branch_last_message: @branch_last_message,
|
44
|
+
branch_name: @branch_name,
|
45
|
+
execution_time: @execution_time }
|
46
|
+
file = File.new(branch_details_path, 'w')
|
47
|
+
file.puts JSON.generate(hash)
|
48
|
+
file.close
|
49
|
+
end
|
50
|
+
|
51
|
+
def branch_details_path
|
52
|
+
"#{@base_branch_dir}/branch_info.json"
|
53
|
+
end
|
54
|
+
|
55
|
+
def target_branch_config_path
|
56
|
+
"#{@base_branch_dir}/config.xml"
|
57
|
+
end
|
58
|
+
|
59
|
+
def target_branch_project_list_path
|
60
|
+
"#{@base_branch_dir}/project-list.xml"
|
61
|
+
end
|
62
|
+
|
63
|
+
def format_execution_time
|
64
|
+
PmdReportDetail.convert_seconds(@execution_time)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PmdTester
|
4
|
+
# This class is used to store pmd errors and its size.
|
5
|
+
class PmdErrors
|
6
|
+
attr_reader :errors
|
7
|
+
attr_reader :errors_size
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
# key:filename as String => value:PmdError Array
|
11
|
+
@errors = {}
|
12
|
+
@errors_size = 0
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_error_by_filename(filename, error)
|
16
|
+
if @errors.key?(filename)
|
17
|
+
@errors[filename].push(error)
|
18
|
+
else
|
19
|
+
@errors.store(filename, [error])
|
20
|
+
end
|
21
|
+
@errors_size += 1
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# This class represents a 'error' element of Pmd xml report
|
26
|
+
# and which Pmd branch the 'error' is from
|
27
|
+
class PmdError
|
28
|
+
# The pmd branch type, 'base' or 'patch'
|
29
|
+
attr_reader :branch
|
30
|
+
|
31
|
+
# The schema of 'error' node:
|
32
|
+
# <xs:complexType name="error">
|
33
|
+
# <xs:simpleContent>
|
34
|
+
# <xs:extension base="xs:string">
|
35
|
+
# <xs:attribute name="filename" type="xs:string" use="required"/>
|
36
|
+
# <xs:attribute name="msg" type="xs:string" use="required"/>
|
37
|
+
# </xs:extension>
|
38
|
+
# </xs:simpleContent>
|
39
|
+
# </xs:complexType>
|
40
|
+
attr_reader :attrs
|
41
|
+
attr_accessor :text
|
42
|
+
|
43
|
+
def initialize(attrs, branch)
|
44
|
+
@attrs = attrs
|
45
|
+
|
46
|
+
@branch = branch
|
47
|
+
@text = ''
|
48
|
+
end
|
49
|
+
|
50
|
+
def filename
|
51
|
+
@attrs['filename']
|
52
|
+
end
|
53
|
+
|
54
|
+
def msg
|
55
|
+
@attrs['msg']
|
56
|
+
end
|
57
|
+
|
58
|
+
def eql?(other)
|
59
|
+
filename.eql?(other.filename) && msg.eql?(other.msg) &&
|
60
|
+
@text.eql?(other.text)
|
61
|
+
end
|
62
|
+
|
63
|
+
def hash
|
64
|
+
[filename, msg, @text].hash
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module PmdTester
|
6
|
+
# This class represents all details about report of pmd
|
7
|
+
class PmdReportDetail
|
8
|
+
attr_accessor :execution_time
|
9
|
+
attr_accessor :timestamp
|
10
|
+
attr_reader :working_dir
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@execution_time = 0
|
14
|
+
@timestamp = ''
|
15
|
+
@working_dir = Dir.getwd
|
16
|
+
end
|
17
|
+
|
18
|
+
def save(report_info_path)
|
19
|
+
hash = { execution_time: @execution_time, timestamp: @timestamp, working_dir: @working_dir }
|
20
|
+
file = File.new(report_info_path, 'w')
|
21
|
+
file.puts JSON.generate(hash)
|
22
|
+
file.close
|
23
|
+
end
|
24
|
+
|
25
|
+
def load(report_info_path)
|
26
|
+
if File.exist?(report_info_path)
|
27
|
+
hash = JSON.parse(File.read(report_info_path))
|
28
|
+
@execution_time = hash['execution_time']
|
29
|
+
@timestamp = hash['timestamp']
|
30
|
+
@working_dir = hash['working_dir']
|
31
|
+
hash
|
32
|
+
else
|
33
|
+
puts "#{report_info_path} doesn't exist"
|
34
|
+
{}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def format_execution_time
|
39
|
+
self.class.convert_seconds(@execution_time)
|
40
|
+
end
|
41
|
+
|
42
|
+
# convert seconds into HH::MM::SS
|
43
|
+
def self.convert_seconds(seconds)
|
44
|
+
Time.at(seconds.abs).utc.strftime('%H:%M:%S')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PmdTester
|
4
|
+
# This class is used to store pmd violations and its size.
|
5
|
+
class PmdViolations
|
6
|
+
attr_reader :violations
|
7
|
+
attr_reader :violations_size
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
# key:filename as String => value:PmdViolation Array
|
11
|
+
@violations = {}
|
12
|
+
@violations_size = 0
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_violations_by_filename(filename, violations)
|
16
|
+
@violations.store(filename, violations)
|
17
|
+
@violations_size += violations.size
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# This class represents a 'violation' element of Pmd xml report
|
22
|
+
# and which pmd branch the 'violation' is from
|
23
|
+
class PmdViolation
|
24
|
+
# The pmd branch type, 'base' or 'patch'
|
25
|
+
attr_reader :branch
|
26
|
+
|
27
|
+
# The schema of 'violation' element:
|
28
|
+
# <xs:complexType name="violation">
|
29
|
+
# <xs:simpleContent>
|
30
|
+
# <xs:extension base="xs:string">
|
31
|
+
# <xs:attribute name="beginline" type="xs:integer" use="required" />
|
32
|
+
# <xs:attribute name="endline" type="xs:integer" use="required" />
|
33
|
+
# <xs:attribute name="begincolumn" type="xs:integer" use="required" />
|
34
|
+
# <xs:attribute name="endcolumn" type="xs:integer" use="required" />
|
35
|
+
# <xs:attribute name="rule" type="xs:string" use="required" />
|
36
|
+
# <xs:attribute name="ruleset" type="xs:string" use="required" />
|
37
|
+
# <xs:attribute name="package" type="xs:string" use="optional" />
|
38
|
+
# <xs:attribute name="class" type="xs:string" use="optional" />
|
39
|
+
# <xs:attribute name="method" type="xs:string" use="optional" />
|
40
|
+
# <xs:attribute name="variable" type="xs:string" use="optional" />
|
41
|
+
# <xs:attribute name="externalInfoUrl" type="xs:string" use="optional" />
|
42
|
+
# <xs:attribute name="priority" type="xs:string" use="required" />
|
43
|
+
# </xs:extension>
|
44
|
+
# </xs:simpleContent>
|
45
|
+
# </xs:complexType>
|
46
|
+
|
47
|
+
attr_reader :attrs
|
48
|
+
attr_accessor :text
|
49
|
+
|
50
|
+
def initialize(attrs, branch)
|
51
|
+
@attrs = attrs
|
52
|
+
@branch = branch
|
53
|
+
@text = ''
|
54
|
+
end
|
55
|
+
|
56
|
+
def eql?(other)
|
57
|
+
@attrs['beginline'].eql?(other.attrs['beginline']) &&
|
58
|
+
@attrs['rule'].eql?(other.attrs['rule']) &&
|
59
|
+
@text.eql?(other.text)
|
60
|
+
end
|
61
|
+
|
62
|
+
def hash
|
63
|
+
[@attrs['beginline'], @attrs['rule'], @text].hash
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
# PmdTester is a regression testing tool ensure that new problems
|
6
|
+
# and unexpected behaviors will not be introduced to PMD project
|
7
|
+
# after fixing an issue , and new rules can work as expected.
|
8
|
+
module PmdTester
|
9
|
+
def logger
|
10
|
+
PmdTester.logger
|
11
|
+
end
|
12
|
+
|
13
|
+
# Global, memoized, lazy initialized instance of a logger
|
14
|
+
def self.logger
|
15
|
+
@logger ||= Logger.new(STDOUT)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './pmd_branch_detail'
|
4
|
+
|
5
|
+
module PmdTester
|
6
|
+
# This class represents all the information about the project
|
7
|
+
class Project
|
8
|
+
REPOSITORIES_PATH = 'target/repositories'
|
9
|
+
|
10
|
+
attr_reader :name
|
11
|
+
attr_reader :type
|
12
|
+
attr_reader :connection
|
13
|
+
attr_reader :webview_url
|
14
|
+
attr_reader :tag
|
15
|
+
attr_reader :exclude_pattern
|
16
|
+
attr_accessor :report_diff
|
17
|
+
# key: pmd branch name as String => value: local path of pmd report
|
18
|
+
|
19
|
+
def initialize(project)
|
20
|
+
@name = project.at_xpath('name').text
|
21
|
+
@type = project.at_xpath('type').text
|
22
|
+
@connection = project.at_xpath('connection').text
|
23
|
+
|
24
|
+
@tag = 'master'
|
25
|
+
tag_element = project.at_xpath('tag')
|
26
|
+
@tag = tag_element.text unless tag_element.nil?
|
27
|
+
|
28
|
+
webview_url_element = project.at_xpath('webview-url')
|
29
|
+
@webview_url = default_webview_url
|
30
|
+
@webview_url = webview_url_element.text unless webview_url_element.nil?
|
31
|
+
|
32
|
+
@exclude_pattern = []
|
33
|
+
project.xpath('exclude-pattern').each do |ep|
|
34
|
+
@exclude_pattern.push(ep.text)
|
35
|
+
end
|
36
|
+
|
37
|
+
@report_diff = nil
|
38
|
+
end
|
39
|
+
|
40
|
+
# Generate the default webview url for the projects
|
41
|
+
# stored on github.
|
42
|
+
# For other projects return value is `connection`.
|
43
|
+
def default_webview_url
|
44
|
+
if @type.eql?('git') && @connection.include?('github.com')
|
45
|
+
"#{@connection}/tree/#{@tag}"
|
46
|
+
else
|
47
|
+
@connection
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Change the file path from 'LOCAL_DIR/SOURCE_CODE_PATH' to
|
52
|
+
# 'WEB_VIEW_URL/SOURCE_CODE_PATH'
|
53
|
+
def get_webview_url(file_path)
|
54
|
+
file_path.gsub(%r{/#{local_source_path}}, @webview_url)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Change the file path from 'LOCAL_DIR/SOURCE_CODE_PATH' to
|
58
|
+
# 'PROJECT_NAME/SOURCE_CODE_PATH'
|
59
|
+
def get_path_inside_project(file_path)
|
60
|
+
file_path.gsub(%r{/#{local_source_path}}, @name)
|
61
|
+
end
|
62
|
+
|
63
|
+
def get_pmd_report_path(branch_name)
|
64
|
+
if branch_name.nil?
|
65
|
+
nil
|
66
|
+
else
|
67
|
+
"#{get_project_target_dir(branch_name)}/pmd_report.xml"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def get_report_info_path(branch_name)
|
72
|
+
if branch_name.nil?
|
73
|
+
nil
|
74
|
+
else
|
75
|
+
"#{get_project_target_dir(branch_name)}/report_info.json"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def get_project_target_dir(branch_name)
|
80
|
+
branch_filename = PmdBranchDetail.branch_filename(branch_name)
|
81
|
+
dir = "target/reports/#{branch_filename}/#{@name}"
|
82
|
+
FileUtils.mkdir_p(dir) unless File.directory?(dir)
|
83
|
+
dir
|
84
|
+
end
|
85
|
+
|
86
|
+
def local_source_path
|
87
|
+
"#{REPOSITORIES_PATH}/#{@name}"
|
88
|
+
end
|
89
|
+
|
90
|
+
def target_diff_report_path
|
91
|
+
dir = "target/reports/diff/#{@name}"
|
92
|
+
FileUtils.mkdir_p(dir) unless File.directory?(dir)
|
93
|
+
dir
|
94
|
+
end
|
95
|
+
|
96
|
+
def diff_report_index_path
|
97
|
+
"#{target_diff_report_path}/index.html"
|
98
|
+
end
|
99
|
+
|
100
|
+
def diff_report_index_ref_path
|
101
|
+
"./#{name}/index.html"
|
102
|
+
end
|
103
|
+
|
104
|
+
def diffs_exist?
|
105
|
+
@report_diff.diffs_exist?
|
106
|
+
end
|
107
|
+
|
108
|
+
def introduce_new_errors?
|
109
|
+
@report_diff.introduce_new_errors?
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|