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
data/README.rdoc
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
= pmdtester
|
|
2
2
|
|
|
3
|
+
home :: https://pmd.github.io
|
|
3
4
|
code :: https://github.com/pmd/pmd-regression-tester
|
|
4
5
|
bugs :: https://github.com/pmd/pmd-regression-tester/issues
|
|
5
6
|
|
|
6
|
-
build-status :: {<img src="https://
|
|
7
|
+
build-status :: {<img src="https://github.com/pmd/pmd-regression-tester/workflows/build/badge.svg?branch=master" alt="Build Status" />}[https://github.com/pmd/pmd-regression-tester/actions?query=workflow%3Abuild]
|
|
8
|
+
|
|
9
|
+
gem-version :: {<img src="https://badge.fury.io/rb/pmdtester.svg" alt="Gem Version" />}[https://rubygems.org/gems/pmdtester]
|
|
7
10
|
|
|
8
11
|
== DESCRIPTION:
|
|
9
12
|
|
|
@@ -16,18 +19,138 @@ on a list of standard projects(Spring Framework, Hibernate, Solr, etc.)
|
|
|
16
19
|
|
|
17
20
|
== SYNOPSIS:
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
=== Options:
|
|
23
|
+
-r, --local-git-repo path to the local PMD repository
|
|
24
|
+
-b, --base-branch name of the base branch in local PMD repository
|
|
25
|
+
-p, --patch-branch name of the patch branch in local PMD repository
|
|
26
|
+
-bc, --base-config path to the base PMD configuration file default:PMDTESTER_INSTALLED_DIR/config/all-java.xml
|
|
27
|
+
-pc, --patch-config path to the patch PMD configuration file default:PMDTESTER_INSTALLED_DIR/config/all-java.xml
|
|
28
|
+
-c, --config path to the base and patch PMD configuration file
|
|
29
|
+
-l, --list-of-project path to the file which contains the list of standard projects default:PMDTESTER_INSTALLED_DIR/config/project-list.xml
|
|
30
|
+
-m, --mode the mode of the tool: 'local', 'online' or 'single'
|
|
31
|
+
single: Set this option to 'single' if your patch branch contains changes
|
|
32
|
+
for any option that can't work on master/base branch
|
|
33
|
+
online: Set this option to 'online' if you want to download
|
|
34
|
+
the PMD report of master/base branch rather than generating it locally
|
|
35
|
+
local: Default option is 'local', PMD reports for the base and patch branches are generated locally.
|
|
36
|
+
|
|
37
|
+
-t, --threads Sets the number of threads used by PMD. Set threads to 0 to disable multi-threading processing. default:1
|
|
38
|
+
-f, --html-flag whether to not generate the html diff report in single mode
|
|
39
|
+
-a, --auto-gen-config whether to generate configurations automatically based on branch differences,this option only works in online and local mode
|
|
40
|
+
--keep-reports whether to keep old reports and skip running PMD again if possible
|
|
41
|
+
-d, --debug whether change log level to DEBUG to see more information
|
|
42
|
+
--error-recovery enable error recovery mode when executing PMD. Might help to analyze errors.
|
|
43
|
+
--baseline-download-url download url prefix from where to download the baseline in online mode default:https://sourceforge.net/projects/pmd/files/pmd-regression-tester/
|
|
44
|
+
-v, --version
|
|
45
|
+
-h, --help
|
|
46
|
+
|
|
47
|
+
=== Quick start
|
|
48
|
+
|
|
49
|
+
==== Run local mode
|
|
50
|
+
|
|
51
|
+
pmdtester -b master -p YOUR_DEVELOPMENT_BRANCH -r PATH_TO_LOCAL_PMD_REPO -a
|
|
52
|
+
|
|
53
|
+
==== Run single mode
|
|
54
|
+
|
|
55
|
+
pmdtester -p YOUR_DEVELOPMENT_BRANCH -pc CONFIG_ONLY_CONTAINS_NEW_PMD_JAVA_RULE -m single
|
|
56
|
+
|
|
57
|
+
==== Run online mode
|
|
58
|
+
|
|
59
|
+
pmdtester -b master -p YOUR_DEVELOPMENT_BRANCH -r PATH_TO_LOCAL_PMD_REPO -m online -a
|
|
60
|
+
|
|
61
|
+
=== Output
|
|
62
|
+
The tool creates the following folders:
|
|
63
|
+
target
|
|
64
|
+
├── repositories <- the analyzed projects are cloned here
|
|
65
|
+
│ ├── PROJECT_NAME_1
|
|
66
|
+
│ ├── ......
|
|
67
|
+
│ └── PROJECT_NAME_n
|
|
68
|
+
├── reports
|
|
69
|
+
│ ├── BASE_BRANCH_NAME <- the base baseline is placed here
|
|
70
|
+
│ ├── PATCH_BRANCH_NAME <- the patch baseline is placed here
|
|
71
|
+
│ └── diff
|
|
72
|
+
│ ├── index.html <- the summary report of diff reports
|
|
73
|
+
│ ├── base_config.xml <- pmd config from the base branch
|
|
74
|
+
│ ├── patch_config.xml <- pmd config from the patch branch
|
|
75
|
+
│ ├── css <- css resources are placed here
|
|
76
|
+
│ ├── js <- js resources
|
|
77
|
+
│ ├── PROJECT_NAME_1
|
|
78
|
+
│ │ ├── project_data.js <- contains the violations as js/json
|
|
79
|
+
│ │ └── index.html <- the diff report of PROJECT_1
|
|
80
|
+
│ ├── .......
|
|
81
|
+
│ └── PROJECT_NAME_n
|
|
82
|
+
│ ├── project_data.js <- contains the violations as js/json
|
|
83
|
+
│ └── index.xml <- the diff report of PROJECT_N
|
|
84
|
+
├── pmd-bin-<version>-<branch_name>-<sha1> <- cached pmd builds that are reused
|
|
85
|
+
└── pmd-bin-....
|
|
86
|
+
|
|
87
|
+
==== The baseline format
|
|
88
|
+
branch_name
|
|
89
|
+
├── branch_info.json
|
|
90
|
+
├── config.xml
|
|
91
|
+
├── STANDARD_PROJECT_NAME_1
|
|
92
|
+
│ ├── report_info.json
|
|
93
|
+
│ └── pmd_report.xml
|
|
94
|
+
├── ......................
|
|
95
|
+
│ ├── report_info.json
|
|
96
|
+
│ └── pmd_report.xml
|
|
97
|
+
└── STANDARD_PROJECT_NAME_n
|
|
98
|
+
├── report_info.info
|
|
99
|
+
└── pmd_report.xml
|
|
20
100
|
|
|
21
101
|
== REQUIREMENTS:
|
|
22
102
|
|
|
23
|
-
*
|
|
103
|
+
* Ruby 2.7 or higher
|
|
24
104
|
|
|
25
|
-
|
|
105
|
+
=== Runtime dependency
|
|
26
106
|
|
|
27
|
-
|
|
107
|
+
nokogiri >= 1.11.0.rc4
|
|
108
|
+
slop ~> 4.6
|
|
109
|
+
differ ~> 0.1
|
|
110
|
+
rufus-scheduler ~> 3.5
|
|
111
|
+
logger-colors ~> 1.0
|
|
112
|
+
liquid >= 4.0
|
|
28
113
|
|
|
29
|
-
|
|
114
|
+
=== Development dependency
|
|
30
115
|
|
|
31
|
-
|
|
116
|
+
hoe ~> 3.22
|
|
117
|
+
hoe-bundler ~> 1.5
|
|
118
|
+
hoe-git ~> 1.6
|
|
119
|
+
minitest ~> 5.10
|
|
120
|
+
mocha ~> 1.5
|
|
121
|
+
rdoc < 7, >= 4.0
|
|
122
|
+
rubocop ~> 0.81
|
|
123
|
+
test-unit ~> 3.2
|
|
32
124
|
|
|
125
|
+
== INSTALL:
|
|
126
|
+
|
|
127
|
+
gem install pmdtester --pre
|
|
33
128
|
|
|
129
|
+
== DEVELOPERS:
|
|
130
|
+
git clone https://github.com/pmd/pmd-regression-tester.git
|
|
131
|
+
cd pmd-regression-tester
|
|
132
|
+
gem install bundler
|
|
133
|
+
bundle config set path "vendor/cache"
|
|
134
|
+
bundle install # once
|
|
135
|
+
bundle exec rake verify # run this command before commit your changes
|
|
136
|
+
bundle exec pmdtester ... # run this to directly execute pmdtester from source
|
|
137
|
+
|
|
138
|
+
Run a single test class, e.g.:
|
|
139
|
+
bundle exec ruby -I test test/test_diff_report_builder.rb
|
|
140
|
+
|
|
141
|
+
Run a single test, e.g.:
|
|
142
|
+
bundle exec ruby -I test test/test_diff_report_builder.rb -n test_diff_report_builder
|
|
143
|
+
|
|
144
|
+
=== Releasing
|
|
145
|
+
|
|
146
|
+
* Update +History.md+ (version and date)
|
|
147
|
+
* Update +lib/pmdtester.rb+ (version)
|
|
148
|
+
* Run "bundle exec rake verify" and add new +pmdtester.gemspec+ (new version)
|
|
149
|
+
* Commit ("Prepare release x.y.z").
|
|
150
|
+
* Tag this commit ("git tag vx.y.z").
|
|
151
|
+
* Update History.md and lib/pmdtester.rb for the next development version,
|
|
152
|
+
run again "bundle exec rake verify"
|
|
153
|
+
* Commit ("Prepare next development version x.y.z-SNAPSHOT").
|
|
154
|
+
* Push to master.
|
|
155
|
+
* Push the tag. Github Actions will build and publish the new gem
|
|
156
|
+
* Update the release notes on https://github.com/pmd/pmd-regression-tester/releases
|
data/Rakefile
CHANGED
|
@@ -3,40 +3,43 @@
|
|
|
3
3
|
# -*- ruby -*-
|
|
4
4
|
require 'rake/testtask'
|
|
5
5
|
require 'rubocop/rake_task'
|
|
6
|
-
require_relative '
|
|
6
|
+
require_relative 'lib/pmdtester'
|
|
7
7
|
|
|
8
8
|
gem 'hoe'
|
|
9
9
|
require 'hoe'
|
|
10
|
+
|
|
10
11
|
Hoe.plugin :bundler
|
|
11
|
-
Hoe.plugin :gemspec
|
|
12
12
|
Hoe.plugin :git
|
|
13
13
|
|
|
14
14
|
hoe = Hoe.spec 'pmdtester' do
|
|
15
|
-
self.version = PmdTester::
|
|
15
|
+
self.version = PmdTester::VERSION
|
|
16
16
|
|
|
17
|
-
developer 'Andreas Dangel', 'andreas.dangel@
|
|
17
|
+
developer 'Andreas Dangel', 'andreas.dangel@pmd-code.org'
|
|
18
18
|
developer 'Binguo Bao', 'djydewang@gmail.com'
|
|
19
|
+
developer 'Clément Fournier', 'clement.fournier76@gmail.com'
|
|
19
20
|
|
|
20
|
-
self.clean_globs = %w[target/reports/**/* target/test/**/*]
|
|
21
|
-
self.extra_deps += [['nokogiri', '
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
self.clean_globs = %w[target/reports/**/* target/test/**/* target/dynamic-config.xml Gemfile.lock]
|
|
22
|
+
self.extra_deps += [['nokogiri', '>= 1.11.0.rc4'], ['slop', '~> 4.6'], ['differ', '~> 0.1'],
|
|
23
|
+
['rufus-scheduler', '~> 3.5'], ['logger-colors', '~> 1.0'],
|
|
24
|
+
['liquid', '>= 4.0']]
|
|
25
|
+
self.extra_dev_deps += [
|
|
26
|
+
['hoe-bundler', '~> 1.5'],
|
|
25
27
|
['hoe-git', '~> 1.6'],
|
|
26
|
-
['minitest', '~> 5.10
|
|
27
|
-
['mocha', '~> 1.5
|
|
28
|
-
|
|
29
|
-
['
|
|
28
|
+
['minitest', '~> 5.10'],
|
|
29
|
+
['mocha', '~> 1.5'],
|
|
30
|
+
# use the same version of rubocop as codacy
|
|
31
|
+
['rubocop', '~> 0.81'],
|
|
32
|
+
['test-unit', '~> 3.2'],
|
|
33
|
+
['rdoc', ['>= 4.0', '< 7']]
|
|
30
34
|
]
|
|
35
|
+
spec_extras[:required_ruby_version] = '>= 2.7'
|
|
31
36
|
|
|
32
37
|
license 'BSD-2-Clause'
|
|
33
38
|
end
|
|
34
39
|
|
|
35
40
|
# Refers to
|
|
36
41
|
# http://rubocop.readthedocs.io/en/latest/integration_with_other_tools/#rake-integration
|
|
37
|
-
RuboCop::RakeTask.new(:rubocop)
|
|
38
|
-
task.patterns = %w[lib/**/*.rb test/**/*.rb]
|
|
39
|
-
end
|
|
42
|
+
RuboCop::RakeTask.new(:rubocop)
|
|
40
43
|
|
|
41
44
|
# Run integration test cases
|
|
42
45
|
Rake::TestTask.new('integration-test') do |task|
|
|
@@ -48,7 +51,18 @@ end
|
|
|
48
51
|
|
|
49
52
|
desc 'generate the pmdtester.gemspec file'
|
|
50
53
|
task 'hoe:spec' do
|
|
51
|
-
|
|
54
|
+
attention_message = '# DO NOT EDIT THIS FILE. Instead, edit Rakefile, and run `rake hoe:spec`.'
|
|
55
|
+
File.open("#{hoe.name}.gemspec", 'w') do |f|
|
|
56
|
+
f.puts attention_message
|
|
57
|
+
f.puts
|
|
58
|
+
f.write hoe.spec.to_ruby
|
|
59
|
+
f.puts
|
|
60
|
+
f.puts attention_message
|
|
61
|
+
end
|
|
52
62
|
end
|
|
53
63
|
|
|
64
|
+
desc 'verify code quality before committing changes'
|
|
65
|
+
task 'verify' => ['clean', 'test', 'rubocop', 'git:manifest', 'hoe:spec', 'check_manifest'] do
|
|
66
|
+
Rake.application.invoke_task('bundler:gemfile[,true]')
|
|
67
|
+
end
|
|
54
68
|
# vim: syntax=ruby
|
data/bin/pmdtester
CHANGED
data/config/all-java.xml
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<ruleset name="All Java Rules"
|
|
4
4
|
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
|
|
5
5
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
6
|
-
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0
|
|
6
|
+
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
|
|
7
7
|
<description>Every Java Rule in PMD</description>
|
|
8
8
|
|
|
9
9
|
<rule ref="category/java/bestpractices.xml" />
|
data/config/design.xml
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<ruleset name="Design"
|
|
4
4
|
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
|
|
5
5
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
6
|
-
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0
|
|
6
|
+
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
|
|
7
7
|
|
|
8
8
|
<description>
|
|
9
9
|
The Design ruleset contains rules that flag suboptimal code implementations. Alternate approaches
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<?xml version="1.0" ?>
|
|
2
|
+
<!-- version 1.0.0 -->
|
|
2
3
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
|
3
4
|
<xs:element name="projectlist">
|
|
4
5
|
<xs:complexType>
|
|
@@ -25,4 +26,4 @@
|
|
|
25
26
|
<xs:element name="exclude-pattern" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
|
|
26
27
|
</xs:sequence>
|
|
27
28
|
</xs:complexType>
|
|
28
|
-
</xs:schema>
|
|
29
|
+
</xs:schema>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<?xml version="1.0" ?>
|
|
2
|
+
<!-- version 1.1.0 -->
|
|
3
|
+
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
|
4
|
+
<xs:element name="projectlist">
|
|
5
|
+
<xs:complexType>
|
|
6
|
+
<xs:sequence>
|
|
7
|
+
<xs:element name="description" type="xs:string" minOccurs="1" maxOccurs="1"/>
|
|
8
|
+
<xs:element name="project" type="project" minOccurs="1" maxOccurs="unbounded"/>
|
|
9
|
+
</xs:sequence>
|
|
10
|
+
</xs:complexType>
|
|
11
|
+
</xs:element>
|
|
12
|
+
<xs:complexType name="project">
|
|
13
|
+
<xs:sequence>
|
|
14
|
+
<xs:element name="name" type="xs:string" minOccurs="1" maxOccurs="1"/>
|
|
15
|
+
<xs:element name="type" minOccurs="1" maxOccurs="1">
|
|
16
|
+
<xs:simpleType>
|
|
17
|
+
<xs:restriction base="xs:string">
|
|
18
|
+
<xs:enumeration value="git"/>
|
|
19
|
+
<xs:enumeration value="hg"/>
|
|
20
|
+
</xs:restriction>
|
|
21
|
+
</xs:simpleType>
|
|
22
|
+
</xs:element>
|
|
23
|
+
<xs:element name="connection" type="xs:string" minOccurs="1" maxOccurs="1"/>
|
|
24
|
+
<xs:element name="webview-url" type="xs:string" minOccurs="0" maxOccurs="1"/>
|
|
25
|
+
<xs:element name="tag" type="xs:string" minOccurs="0" maxOccurs="1"/>
|
|
26
|
+
<xs:element name="exclude-pattern" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
|
|
27
|
+
<xs:element name="build-command" type="xs:string" minOccurs="0" maxOccurs="1"/>
|
|
28
|
+
<xs:element name="auxclasspath-command" type="xs:string" minOccurs="0" maxOccurs="1"/>
|
|
29
|
+
</xs:sequence>
|
|
30
|
+
</xs:complexType>
|
|
31
|
+
</xs:schema>
|
data/lib/pmdtester.rb
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'logger'
|
|
4
|
+
require 'logger/colors'
|
|
5
|
+
|
|
6
|
+
require_relative 'pmdtester/cmd'
|
|
7
|
+
require_relative 'pmdtester/collection_by_file'
|
|
8
|
+
require_relative 'pmdtester/pmd_branch_detail'
|
|
9
|
+
require_relative 'pmdtester/pmd_configerror'
|
|
10
|
+
require_relative 'pmdtester/pmd_error'
|
|
11
|
+
require_relative 'pmdtester/pmd_report_detail'
|
|
12
|
+
require_relative 'pmdtester/pmd_tester_utils'
|
|
13
|
+
require_relative 'pmdtester/pmd_violation'
|
|
14
|
+
require_relative 'pmdtester/project'
|
|
15
|
+
require_relative 'pmdtester/report_diff'
|
|
16
|
+
require_relative 'pmdtester/resource_locator'
|
|
17
|
+
require_relative 'pmdtester/runner'
|
|
18
|
+
|
|
19
|
+
require_relative 'pmdtester/builders/simple_progress_logger'
|
|
20
|
+
require_relative 'pmdtester/builders/project_builder'
|
|
21
|
+
require_relative 'pmdtester/builders/project_hasher'
|
|
22
|
+
require_relative 'pmdtester/builders/pmd_report_builder'
|
|
23
|
+
require_relative 'pmdtester/builders/liquid_renderer'
|
|
24
|
+
require_relative 'pmdtester/builders/rule_set_builder'
|
|
25
|
+
require_relative 'pmdtester/builders/summary_report_builder'
|
|
26
|
+
|
|
27
|
+
require_relative 'pmdtester/parsers/options'
|
|
28
|
+
require_relative 'pmdtester/parsers/pmd_report_document'
|
|
29
|
+
require_relative 'pmdtester/parsers/projects_parser'
|
|
30
|
+
|
|
31
|
+
# PmdTester is a regression testing tool ensure that new problems
|
|
32
|
+
# and unexpected behaviors will not be introduced to PMD project
|
|
33
|
+
# after fixing an issue and new rules can work as expected.
|
|
34
|
+
module PmdTester
|
|
35
|
+
VERSION = '1.1.1'
|
|
36
|
+
BASE = 'base'
|
|
37
|
+
PATCH = 'patch'
|
|
38
|
+
PR_NUM_ENV_VAR = 'PMD_CI_PULL_REQUEST_NUMBER' # see PmdBranchDetail
|
|
39
|
+
|
|
40
|
+
def logger
|
|
41
|
+
PmdTester.logger
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Global, memoized, lazy initialized instance of a logger
|
|
45
|
+
def self.logger
|
|
46
|
+
@logger ||= Logger.new(STDOUT)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'liquid'
|
|
4
|
+
require 'json'
|
|
5
|
+
|
|
6
|
+
module PmdTester
|
|
7
|
+
# A module to include in classes that use a Liquid template
|
|
8
|
+
# to generate content.
|
|
9
|
+
module LiquidRenderer
|
|
10
|
+
include PmdTester
|
|
11
|
+
|
|
12
|
+
def render_liquid(template_path, env)
|
|
13
|
+
to_render = File.read(ResourceLocator.resource(template_path))
|
|
14
|
+
includes = Liquid::LocalFileSystem.new(ResourceLocator.resource('_includes'), '%s.html')
|
|
15
|
+
Liquid::Template.file_system = includes
|
|
16
|
+
template = Liquid::Template.parse(to_render, error_mode: :strict)
|
|
17
|
+
template.render!(env, { strict_variables: true })
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def render_and_write(template_path, target_file, env)
|
|
21
|
+
write_file(target_file, render_liquid(template_path, env))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def write_file(target_file, contents)
|
|
25
|
+
dir = File.dirname(target_file)
|
|
26
|
+
FileUtils.mkdir_p(dir) unless File.directory?(dir)
|
|
27
|
+
|
|
28
|
+
index = File.new(target_file, 'w')
|
|
29
|
+
index&.puts contents # may be nil when stubbing
|
|
30
|
+
logger&.info "Written #{target_file}"
|
|
31
|
+
ensure
|
|
32
|
+
index&.close
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def copy_resource(dir, to_root)
|
|
36
|
+
src = ResourceLocator.resource(dir)
|
|
37
|
+
dest = "#{to_root}/#{dir}"
|
|
38
|
+
FileUtils.copy_entry(src, dest)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Renders the index of a project diff report.
|
|
43
|
+
class LiquidProjectRenderer
|
|
44
|
+
include PmdTester
|
|
45
|
+
include ProjectHasher
|
|
46
|
+
include LiquidRenderer
|
|
47
|
+
|
|
48
|
+
def write_project_index(project, root)
|
|
49
|
+
liquid_env = {
|
|
50
|
+
'diff' => report_diff_to_h(project.report_diff),
|
|
51
|
+
'error_diffs' => errors_to_h(project),
|
|
52
|
+
'configerror_diffs' => configerrors_to_h(project),
|
|
53
|
+
'project_name' => project.name
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# Renders index.html using liquid
|
|
57
|
+
write_file("#{root}/index.html", render_liquid('project_diff_report.html', liquid_env))
|
|
58
|
+
# generate array of violations in json
|
|
59
|
+
write_file("#{root}/project_data.js", dump_violations_json(project))
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def dump_violations_json(project)
|
|
63
|
+
h = {
|
|
64
|
+
'source_link_base' => project.webview_url,
|
|
65
|
+
'source_link_template' => link_template(project),
|
|
66
|
+
**violations_to_hash(project)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
project_data = JSON.fast_generate(h)
|
|
70
|
+
"let project = #{project_data}"
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -1,82 +1,90 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'fileutils'
|
|
4
|
-
|
|
5
|
-
require_relative '../project'
|
|
6
|
-
require_relative '../pmd_branch_detail'
|
|
7
|
-
require_relative '../pmd_report_detail'
|
|
4
|
+
require 'rufus-scheduler'
|
|
8
5
|
|
|
9
6
|
module PmdTester
|
|
10
7
|
# Building pmd xml reports according to a list of standard
|
|
11
8
|
# projects and branch of pmd source code
|
|
12
9
|
class PmdReportBuilder
|
|
13
10
|
include PmdTester
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
|
|
12
|
+
def initialize(projects, options, branch_config, branch_name)
|
|
16
13
|
@projects = projects
|
|
17
|
-
@local_git_repo = local_git_repo
|
|
18
|
-
@
|
|
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
|
|
19
19
|
@pwd = Dir.getwd
|
|
20
20
|
|
|
21
|
-
@pmd_branch_details = PmdBranchDetail.new(pmd_branch_name)
|
|
21
|
+
@pmd_branch_details = PmdBranchDetail.new(@pmd_branch_name)
|
|
22
|
+
@project_builder = ProjectBuilder.new(@projects)
|
|
22
23
|
end
|
|
23
24
|
|
|
24
|
-
def
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
when 'hg'
|
|
29
|
-
reset_cmd = "hg up #{tag}"
|
|
30
|
-
end
|
|
25
|
+
def get_pmd_binary_file
|
|
26
|
+
logger.info "#{@pmd_branch_name}: Start packaging PMD"
|
|
27
|
+
Dir.chdir(@local_git_repo) do
|
|
28
|
+
build_branch_sha = Cmd.execute("git rev-parse #{@pmd_branch_name}^{commit}")
|
|
31
29
|
|
|
32
|
-
|
|
33
|
-
end
|
|
30
|
+
checkout_build_branch # needs a clean working tree, otherwise fails
|
|
34
31
|
|
|
35
|
-
|
|
36
|
-
logger.info 'Cloning projects started'
|
|
32
|
+
raise "Wrong branch #{get_last_commit_sha}" unless build_branch_sha == get_last_commit_sha
|
|
37
33
|
|
|
38
|
-
|
|
39
|
-
logger.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if File.
|
|
43
|
-
logger.
|
|
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}"
|
|
44
41
|
else
|
|
45
|
-
|
|
42
|
+
build_pmd(into_dir: distro_path)
|
|
46
43
|
end
|
|
47
44
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
logger.info "Cloning #{project.name} completed"
|
|
45
|
+
# we're still on the build branch
|
|
46
|
+
@pmd_branch_details.branch_last_sha = build_branch_sha
|
|
47
|
+
@pmd_branch_details.branch_last_message = get_last_commit_message
|
|
52
48
|
end
|
|
49
|
+
logger.info "#{@pmd_branch_name}: Packaging PMD completed"
|
|
53
50
|
end
|
|
54
51
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
+
' -T1C'
|
|
66
72
|
Cmd.execute(package_cmd)
|
|
73
|
+
end
|
|
67
74
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
75
|
+
logger.info "#{@pmd_branch_name}: Extracting the zip"
|
|
76
|
+
Cmd.execute("unzip -qo #{pmd_dist_target} -d pmd-dist/target/exploded")
|
|
77
|
+
Cmd.execute("mv pmd-dist/target/exploded/pmd-bin-#{@pmd_version} #{into_dir}")
|
|
78
|
+
end
|
|
71
79
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
80
|
+
def determine_pmd_version
|
|
81
|
+
version_cmd = "./mvnw -q -Dexec.executable=\"echo\" -Dexec.args='${project.version}' " \
|
|
82
|
+
'--non-recursive org.codehaus.mojo:exec-maven-plugin:1.5.0:exec'
|
|
83
|
+
Cmd.execute(version_cmd)
|
|
76
84
|
end
|
|
77
85
|
|
|
78
86
|
def get_last_commit_sha
|
|
79
|
-
get_last_commit_sha_cmd = 'git rev-parse HEAD'
|
|
87
|
+
get_last_commit_sha_cmd = 'git rev-parse HEAD^{commit}'
|
|
80
88
|
Cmd.execute(get_last_commit_sha_cmd)
|
|
81
89
|
end
|
|
82
90
|
|
|
@@ -85,31 +93,55 @@ module PmdTester
|
|
|
85
93
|
Cmd.execute(get_last_commit_message_cmd)
|
|
86
94
|
end
|
|
87
95
|
|
|
88
|
-
def generate_pmd_report(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
96
|
+
def generate_pmd_report(project)
|
|
97
|
+
error_recovery_options = @error_recovery ? 'PMD_JAVA_OPTS="-Dpmd.error_recovery -ea" ' : ''
|
|
98
|
+
run_path = "#{saved_distro_path(@pmd_branch_details.branch_last_sha)}/bin/run.sh"
|
|
99
|
+
pmd_cmd = "#{error_recovery_options}" \
|
|
100
|
+
"#{run_path} pmd -d #{project.local_source_path} -f xml " \
|
|
101
|
+
"-R #{project.get_config_path(@pmd_branch_name)} " \
|
|
102
|
+
"-r #{project.get_pmd_report_path(@pmd_branch_name)} " \
|
|
103
|
+
"-failOnViolation false -t #{@threads} " \
|
|
104
|
+
"#{project.auxclasspath}"
|
|
92
105
|
start_time = Time.now
|
|
93
|
-
|
|
106
|
+
if File.exist?(project.get_pmd_report_path(@pmd_branch_name))
|
|
107
|
+
logger.warn "#{@pmd_branch_name}: Skipping PMD run - report " \
|
|
108
|
+
"#{project.get_pmd_report_path(@pmd_branch_name)} already exists"
|
|
109
|
+
else
|
|
110
|
+
Cmd.execute(pmd_cmd)
|
|
111
|
+
end
|
|
94
112
|
end_time = Time.now
|
|
95
113
|
[end_time - start_time, end_time]
|
|
96
114
|
end
|
|
97
115
|
|
|
116
|
+
def generate_config_for(project)
|
|
117
|
+
logger.debug "Generating ruleset with excludes from #{@branch_config}"
|
|
118
|
+
doc = Nokogiri::XML(File.read(@branch_config))
|
|
119
|
+
ruleset = doc.at_css('ruleset')
|
|
120
|
+
ruleset.add_child("\n")
|
|
121
|
+
project.exclude_pattern.each do |exclude_pattern|
|
|
122
|
+
ruleset.add_child(" <exclude-pattern>#{exclude_pattern}</exclude-pattern>\n")
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
File.open(project.get_config_path(@pmd_branch_name), 'w') do |x|
|
|
126
|
+
x << doc.to_s
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
logger.debug "Created file #{project.get_config_path(@pmd_branch_name)}"
|
|
130
|
+
end
|
|
131
|
+
|
|
98
132
|
def generate_pmd_reports
|
|
99
133
|
logger.info "Generating PMD report started -- branch #{@pmd_branch_name}"
|
|
100
|
-
get_pmd_binary_file
|
|
101
134
|
|
|
102
135
|
sum_time = 0
|
|
103
136
|
@projects.each do |project|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
137
|
+
progress_logger = SimpleProgressLogger.new("generating #{project.name}'s PMD report")
|
|
138
|
+
progress_logger.start
|
|
139
|
+
generate_config_for(project)
|
|
140
|
+
execution_time, end_time = generate_pmd_report(project)
|
|
141
|
+
progress_logger.stop
|
|
108
142
|
sum_time += execution_time
|
|
109
143
|
|
|
110
|
-
report_details = PmdReportDetail.new
|
|
111
|
-
report_details.execution_time = execution_time
|
|
112
|
-
report_details.timestamp = end_time
|
|
144
|
+
report_details = PmdReportDetail.new(execution_time: execution_time, timestamp: end_time)
|
|
113
145
|
report_details.save(project.get_report_info_path(@pmd_branch_name))
|
|
114
146
|
logger.info "#{project.name}'s PMD report was generated successfully"
|
|
115
147
|
end
|
|
@@ -120,9 +152,46 @@ module PmdTester
|
|
|
120
152
|
@pmd_branch_details
|
|
121
153
|
end
|
|
122
154
|
|
|
155
|
+
# returns the branch details
|
|
123
156
|
def build
|
|
124
|
-
|
|
157
|
+
@project_builder.clone_projects
|
|
158
|
+
@project_builder.build_projects
|
|
159
|
+
get_pmd_binary_file
|
|
125
160
|
generate_pmd_reports
|
|
126
161
|
end
|
|
162
|
+
|
|
163
|
+
private
|
|
164
|
+
|
|
165
|
+
def checkout_build_branch
|
|
166
|
+
logger.info "#{@pmd_branch_name}: Checking out the branch"
|
|
167
|
+
# note that this would fail if the tree is dirty
|
|
168
|
+
Cmd.execute("git checkout #{@pmd_branch_name}")
|
|
169
|
+
|
|
170
|
+
# determine the version
|
|
171
|
+
@pmd_version = determine_pmd_version
|
|
172
|
+
|
|
173
|
+
return unless wd_has_dirty_git_changes
|
|
174
|
+
|
|
175
|
+
# working dir is dirty....
|
|
176
|
+
# we don't allow this because we need the SHA to address the zip file
|
|
177
|
+
logger.error "#{@pmd_branch_name}: Won\'t build without a clean working tree, " \
|
|
178
|
+
'commit your changes'
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def work_dir
|
|
182
|
+
"#{@pwd}/target"
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# path to the unzipped distribution
|
|
186
|
+
# e.g. <cwd>/pmd-bin-<version>-<branch>-<sha>
|
|
187
|
+
def saved_distro_path(build_sha)
|
|
188
|
+
"#{work_dir}/pmd-bin-#{@pmd_version}" \
|
|
189
|
+
"-#{PmdBranchDetail.branch_filename(@pmd_branch_name)}" \
|
|
190
|
+
"-#{build_sha}"
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def wd_has_dirty_git_changes
|
|
194
|
+
!Cmd.execute('git status --porcelain').empty?
|
|
195
|
+
end
|
|
127
196
|
end
|
|
128
197
|
end
|