pmdtester 1.3.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3ab10c148f66f7b8d8942096da3c67260b0a50bc6f0a7671993f86035186c228
4
- data.tar.gz: 11175641f5912e24ea5a623fe105392a9507ec0ad43156e223f8cefeea92bd7f
3
+ metadata.gz: 1d00209411d45b04f8ebcfb34d4289b682a9c3d044998b1f82e15c1d08e184ed
4
+ data.tar.gz: d6d6a48c4a2c558b9058c91dc57e2043e304addc90b8e7cc7441f07c50dac624
5
5
  SHA512:
6
- metadata.gz: 543126b2c957bfefdd5d5985e0e6bbca6c7442fb8bb826abda65b86c9dd6956f4d704afb66f9b4fb2080acff72fbc6619bc76f6202919c70b99a55a98dd3a644
7
- data.tar.gz: f10d380a93eb631fa5e2c4a60fc73795cfeb3a589002da0655c19900daea6445a2cf5f0ecc449c043594d0dd0fb3b27f4203c5ac442d7539b19b4ab63bd915ea
6
+ metadata.gz: 82c0d9bce215128d2fb56f44116c7c6f5394b8a34ff8b26918974a89e2c6e67b4d76c14b3a920b372e345ffbe33e114cdacfbcdd962f1c76f5fddc24595d8998
7
+ data.tar.gz: effbb41f97137a0b6a93bc200cd8ebb3b7323dabaeef1eca50bfee3dd97b00e0418987b28661328902b1f66942843acad32bc397f1dc04590cbc9d0a19d1c4b7
@@ -15,6 +15,7 @@ on:
15
15
 
16
16
  jobs:
17
17
  build:
18
+ timeout-minutes: 120
18
19
  runs-on: ubuntu-latest
19
20
  continue-on-error: false
20
21
  if: ${{ !contains(github.event.head_commit.message, '[skip ci]') }}
@@ -27,9 +28,9 @@ jobs:
27
28
  ~/.cache
28
29
  vendor/bundle
29
30
  target/repositories
30
- key: v1-${{ runner.os }}-${{ hashFiles('pmdtester.gemspec') }}
31
+ key: v3-${{ runner.os }}-${{ hashFiles('pmdtester.gemspec') }}
31
32
  restore-keys: |
32
- v1-${{ runner.os }}-
33
+ v3-${{ runner.os }}-
33
34
  - name: Set up Ruby 2.7
34
35
  uses: ruby/setup-ruby@v1
35
36
  with:
@@ -8,6 +8,7 @@ on:
8
8
 
9
9
  jobs:
10
10
  build:
11
+ timeout-minutes: 120
11
12
  runs-on: ubuntu-latest
12
13
  continue-on-error: false
13
14
  steps:
@@ -19,9 +20,9 @@ jobs:
19
20
  ~/.cache
20
21
  vendor/bundle
21
22
  target/repositories
22
- key: v1-${{ runner.os }}-${{ hashFiles('pmdtester.gemspec') }}
23
+ key: v3-${{ runner.os }}-${{ hashFiles('pmdtester.gemspec') }}
23
24
  restore-keys: |
24
- v1-${{ runner.os }}-
25
+ v3-${{ runner.os }}-
25
26
  - name: Set up Ruby 2.7
26
27
  uses: ruby/setup-ruby@v1
27
28
  with:
data/History.md CHANGED
@@ -1,3 +1,26 @@
1
+ # 1.5.0 / 2022-05-06
2
+
3
+ ## Enhancements
4
+
5
+ * [#108](https://github.com/pmd/pmd-regression-tester/issues/108): Disable progress bar for PMD 7
6
+ * [#109](https://github.com/pmd/pmd-regression-tester/issues/109): Make stdout/stderr and exit code available
7
+
8
+ ## Fixed Issues
9
+
10
+ * [#107](https://github.com/pmd/pmd-regression-tester/issues/107): Deprecated command line options is used: `-auxclasspath`
11
+
12
+ # 1.4.1 / 2022-04-12
13
+
14
+ ## Fixed Issues
15
+
16
+ * [#104](https://github.com/pmd/pmd-regression-tester/issues/104): Baseline filtering is not working anymore
17
+
18
+ # 1.4.0 / 2022-03-24
19
+
20
+ ## Enhancements
21
+
22
+ * [#103](https://github.com/pmd/pmd-regression-tester/pull/103): Support other languages besides java
23
+
1
24
  # 1.3.0 / 2021-12-17
2
25
 
3
26
  ## Enhancements
data/README.rdoc CHANGED
@@ -105,23 +105,22 @@ The tool creates the following folders:
105
105
 
106
106
  === Runtime dependency
107
107
 
108
- nokogiri >= 1.11.0.rc4
108
+ nokogiri ~> 1.13
109
109
  slop ~> 4.6
110
110
  differ ~> 0.1
111
- rufus-scheduler ~> 3.5
111
+ rufus-scheduler ~> 3.8
112
112
  logger-colors ~> 1.0
113
- liquid >= 4.0
113
+ liquid ~> 5.2
114
114
 
115
115
  === Development dependency
116
116
 
117
- hoe ~> 3.22
118
117
  hoe-bundler ~> 1.5
119
118
  hoe-git ~> 1.6
120
119
  minitest ~> 5.10
121
120
  mocha ~> 1.5
122
- rdoc < 7, >= 4.0
123
- rubocop ~> 0.81
124
- test-unit ~> 3.2
121
+ rubocop ~> 0.93
122
+ test-unit ~> 3.5
123
+ rdoc ~> 6.4
125
124
 
126
125
  == INSTALL:
127
126
 
data/Rakefile CHANGED
@@ -19,18 +19,23 @@ hoe = Hoe.spec 'pmdtester' do
19
19
  developer 'Clément Fournier', 'clement.fournier76@gmail.com'
20
20
 
21
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']]
22
+ self.extra_deps += [
23
+ ['nokogiri', '~> 1.13'],
24
+ ['slop', '~> 4.6'],
25
+ ['differ', '~> 0.1'],
26
+ ['rufus-scheduler', '~> 3.8'],
27
+ ['logger-colors', '~> 1.0'],
28
+ ['liquid', '~> 5.2']
29
+ ]
25
30
  self.extra_dev_deps += [
26
31
  ['hoe-bundler', '~> 1.5'],
27
32
  ['hoe-git', '~> 1.6'],
28
33
  ['minitest', '~> 5.10'],
29
34
  ['mocha', '~> 1.5'],
30
35
  # use the same version of rubocop as codacy
31
- ['rubocop', '~> 0.81'],
32
- ['test-unit', '~> 3.2'],
33
- ['rdoc', ['>= 4.0', '< 7']]
36
+ ['rubocop', '~> 0.93'],
37
+ ['test-unit', '~> 3.5'],
38
+ ['rdoc', '~> 6.4']
34
39
  ]
35
40
  spec_extras[:required_ruby_version] = '>= 2.7'
36
41
 
@@ -60,6 +60,11 @@ module PmdTester
60
60
  # copy original pmd reports
61
61
  copy_file("#{root}/base_pmd_report.xml", project.report_diff.base_report.file)
62
62
  copy_file("#{root}/patch_pmd_report.xml", project.report_diff.patch_report.file)
63
+ # copy stdout and stderr outputs
64
+ copy_file("#{root}/base_stdout.txt", "#{project.report_diff.base_report.report_folder}/stdout.txt")
65
+ copy_file("#{root}/base_stderr.txt", "#{project.report_diff.base_report.report_folder}/stderr.txt")
66
+ copy_file("#{root}/patch_stdout.txt", "#{project.report_diff.patch_report.report_folder}/stdout.txt")
67
+ copy_file("#{root}/patch_stderr.txt", "#{project.report_diff.patch_report.report_folder}/stderr.txt")
63
68
  # render full pmd reports
64
69
  write_file("#{root}/base_pmd_report.html",
65
70
  render_liquid('project_pmd_report.html', pmd_report_liquid_env(project, BASE)))
@@ -120,6 +125,7 @@ module PmdTester
120
125
 
121
126
  'execution_time' => PmdReportDetail.convert_seconds(report.exec_time),
122
127
  'timestamp' => report.timestamp,
128
+ 'exit_code' => report.exit_code,
123
129
 
124
130
  'rules' => report.rule_summaries,
125
131
  'errors' => report.errors_by_file.all_values.map { |e| error_to_hash(e, project) },
@@ -25,7 +25,7 @@ module PmdTester
25
25
  def get_pmd_binary_file
26
26
  logger.info "#{@pmd_branch_name}: Start packaging PMD"
27
27
  Dir.chdir(@local_git_repo) do
28
- build_branch_sha = Cmd.execute("git rev-parse #{@pmd_branch_name}^{commit}")
28
+ build_branch_sha = Cmd.execute_successfully("git rev-parse #{@pmd_branch_name}^{commit}")
29
29
 
30
30
  checkout_build_branch # needs a clean working tree, otherwise fails
31
31
 
@@ -70,49 +70,53 @@ module PmdTester
70
70
  ' -Dcheckstyle.skip=true' \
71
71
  ' -Dpmd.skip=true' \
72
72
  ' -T1C -B'
73
- Cmd.execute(package_cmd)
73
+ Cmd.execute_successfully(package_cmd)
74
74
  end
75
75
 
76
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}")
77
+ Cmd.execute_successfully("unzip -qo #{pmd_dist_target} -d pmd-dist/target/exploded")
78
+ Cmd.execute_successfully("mv pmd-dist/target/exploded/pmd-bin-#{@pmd_version} #{into_dir}")
79
79
  end
80
80
 
81
81
  def determine_pmd_version
82
82
  version_cmd = "./mvnw -q -Dexec.executable=\"echo\" -Dexec.args='${project.version}' " \
83
83
  '--non-recursive org.codehaus.mojo:exec-maven-plugin:1.5.0:exec'
84
- Cmd.execute(version_cmd)
84
+ Cmd.execute_successfully(version_cmd)
85
85
  end
86
86
 
87
87
  def get_last_commit_sha
88
88
  get_last_commit_sha_cmd = 'git rev-parse HEAD^{commit}'
89
- Cmd.execute(get_last_commit_sha_cmd)
89
+ Cmd.execute_successfully(get_last_commit_sha_cmd)
90
90
  end
91
91
 
92
92
  def get_last_commit_message
93
93
  get_last_commit_message_cmd = 'git log -1 --pretty=%B'
94
- Cmd.execute(get_last_commit_message_cmd)
94
+ Cmd.execute_successfully(get_last_commit_message_cmd)
95
95
  end
96
96
 
97
97
  def generate_pmd_report(project)
98
98
  error_recovery_options = @error_recovery ? 'PMD_JAVA_OPTS="-Dpmd.error_recovery -ea" ' : ''
99
99
  run_path = "#{saved_distro_path(@pmd_branch_details.branch_last_sha)}/bin/run.sh"
100
100
  fail_on_violation = should_use_long_cli_options ? '--fail-on-violation false' : '-failOnViolation false'
101
+ auxclasspath_option = create_auxclasspath_option(project)
101
102
  pmd_cmd = "#{error_recovery_options}" \
102
103
  "#{run_path} pmd -d #{project.local_source_path} -f xml " \
103
104
  "-R #{project.get_config_path(@pmd_branch_name)} " \
104
105
  "-r #{project.get_pmd_report_path(@pmd_branch_name)} " \
105
106
  "#{fail_on_violation} -t #{@threads} " \
106
- "#{project.auxclasspath}"
107
+ "#{auxclasspath_option}" \
108
+ "#{pmd7? ? ' --no-progress' : ''}"
107
109
  start_time = Time.now
110
+ exit_code = nil
108
111
  if File.exist?(project.get_pmd_report_path(@pmd_branch_name))
109
112
  logger.warn "#{@pmd_branch_name}: Skipping PMD run - report " \
110
113
  "#{project.get_pmd_report_path(@pmd_branch_name)} already exists"
111
114
  else
112
- Cmd.execute(pmd_cmd)
115
+ status = Cmd.execute(pmd_cmd, project.get_project_target_dir(@pmd_branch_name))
116
+ exit_code = status.exitstatus
113
117
  end
114
118
  end_time = Time.now
115
- [end_time - start_time, end_time]
119
+ [end_time - start_time, end_time, exit_code]
116
120
  end
117
121
 
118
122
  def generate_config_for(project)
@@ -139,12 +143,12 @@ module PmdTester
139
143
  progress_logger = SimpleProgressLogger.new("generating #{project.name}'s PMD report")
140
144
  progress_logger.start
141
145
  generate_config_for(project)
142
- execution_time, end_time = generate_pmd_report(project)
146
+ execution_time, end_time, exit_code = generate_pmd_report(project)
143
147
  progress_logger.stop
144
148
  sum_time += execution_time
145
149
 
146
- report_details = PmdReportDetail.new(execution_time: execution_time, timestamp: end_time)
147
- report_details.save(project.get_report_info_path(@pmd_branch_name))
150
+ PmdReportDetail.create(execution_time: execution_time, timestamp: end_time,
151
+ exit_code: exit_code, report_info_path: project.get_report_info_path(@pmd_branch_name))
148
152
  logger.info "#{project.name}'s PMD report was generated successfully"
149
153
  end
150
154
 
@@ -167,7 +171,7 @@ module PmdTester
167
171
  def checkout_build_branch
168
172
  logger.info "#{@pmd_branch_name}: Checking out the branch"
169
173
  # note that this would fail if the tree is dirty
170
- Cmd.execute("git checkout #{@pmd_branch_name}")
174
+ Cmd.execute_successfully("git checkout #{@pmd_branch_name}")
171
175
 
172
176
  # determine the version
173
177
  @pmd_version = determine_pmd_version
@@ -193,12 +197,25 @@ module PmdTester
193
197
  end
194
198
 
195
199
  def wd_has_dirty_git_changes
196
- !Cmd.execute('git status --porcelain').empty?
200
+ !Cmd.execute_successfully('git status --porcelain').empty?
197
201
  end
198
202
 
199
203
  def should_use_long_cli_options
200
204
  logger.debug "PMD Version: #{@pmd_version}"
201
205
  Semver.compare(@pmd_version, '6.41.0') >= 0
202
206
  end
207
+
208
+ def create_auxclasspath_option(project)
209
+ auxclasspath_option = ''
210
+ unless project.auxclasspath.empty?
211
+ auxclasspath_option = should_use_long_cli_options ? '--aux-classpath ' : '-auxclasspath '
212
+ auxclasspath_option += project.auxclasspath
213
+ end
214
+ auxclasspath_option
215
+ end
216
+
217
+ def pmd7?
218
+ Semver.compare(@pmd_version, '7.0.0-SNAPSHOT') >= 0
219
+ end
203
220
  end
204
221
  end
@@ -30,7 +30,7 @@ module PmdTester
30
30
  # once but may be used with several tags.
31
31
  clone_cmd = "git clone --no-single-branch --depth 1 #{project.connection} #{path}"
32
32
 
33
- Cmd.execute(clone_cmd)
33
+ Cmd.execute_successfully(clone_cmd)
34
34
  end
35
35
 
36
36
  Dir.chdir(path) do
@@ -71,7 +71,7 @@ module PmdTester
71
71
  if project.auxclasspath_command
72
72
  logger.debug "Executing auxclasspath-command: #{project.auxclasspath_command}"
73
73
  auxclasspath = run_as_script(Dir.getwd, project.auxclasspath_command)
74
- project.auxclasspath = "-auxclasspath #{auxclasspath}"
74
+ project.auxclasspath = auxclasspath
75
75
  else
76
76
  project.auxclasspath = ''
77
77
  end
@@ -87,7 +87,7 @@ module PmdTester
87
87
  if command.start_with?('#!')
88
88
  shell = command.lines[0].chomp[2..] # remove leading "#!"
89
89
  end
90
- stdout = Cmd.execute("#{shell} #{script.path}")
90
+ stdout = Cmd.execute_successfully("#{shell} #{script.path}")
91
91
  ensure
92
92
  script.unlink
93
93
  end
@@ -99,7 +99,7 @@ module PmdTester
99
99
 
100
100
  reset_cmd = "git checkout #{tag}; git reset --hard #{tag}"
101
101
 
102
- Cmd.execute(reset_cmd)
102
+ Cmd.execute_successfully(reset_cmd)
103
103
  end
104
104
  end
105
105
  end
@@ -21,6 +21,9 @@ module PmdTester
21
21
  'base_timestamp' => rdiff.base_report.timestamp,
22
22
  'patch_timestamp' => rdiff.patch_report.timestamp,
23
23
 
24
+ 'base_exit_code' => rdiff.base_report.exit_code,
25
+ 'patch_exit_code' => rdiff.patch_report.exit_code,
26
+
24
27
  'rule_diffs' => rdiff.rule_summaries
25
28
  }
26
29
  end
@@ -9,73 +9,70 @@ module PmdTester
9
9
  # Attention: we only consider java rulesets now.
10
10
  class RuleSetBuilder
11
11
  include PmdTester
12
- ALL_CATEGORIES = Set['bestpractices.xml', 'codestyle.xml', 'design.xml', 'documentation.xml',
13
- 'errorprone.xml', 'multithreading.xml', 'performance.xml',
14
- 'security.xml'].freeze
15
- PATH_TO_PMD_JAVA_BASED_RULES =
16
- 'pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule'
17
- PATH_TO_PMD_XPATH_BASED_RULES = 'pmd-java/src/main/resources/category/java'
18
- PATH_TO_ALL_JAVA_RULES =
19
- ResourceLocator.locate('config/all-java.xml')
20
12
  PATH_TO_DYNAMIC_CONFIG = 'target/dynamic-config.xml'
21
- NO_JAVA_RULES_CHANGED_MESSAGE = 'No java rules have been changed!'
13
+ NO_RULES_CHANGED_MESSAGE = 'No regression tested rules have been changed!'
22
14
 
23
15
  def initialize(options)
24
16
  @options = options
25
17
  end
26
18
 
27
- def build
28
- filenames = diff_filenames
29
- rule_refs = get_rule_refs(filenames)
30
- output_filter_set(rule_refs)
31
- build_config_file(rule_refs)
32
- logger.debug "Dynamic configuration: #{[rule_refs]}"
33
- rule_refs
19
+ #
20
+ # Creates a dynamic ruleset based on the changed sources.
21
+ # Returns true, when rules are affected by the changed sources.
22
+ # Returns false, when no rules are affected and regression tester can be skipped.
23
+ #
24
+ def build?
25
+ languages = determine_languages
26
+ filenames = diff_filenames(languages)
27
+ run_required, rule_refs = get_rule_refs(filenames)
28
+ if run_required
29
+ output_filter_set(rule_refs)
30
+ build_config_file(rule_refs)
31
+ logger.debug "Dynamic configuration: #{[rule_refs]}"
32
+ else
33
+ logger.info NO_RULES_CHANGED_MESSAGE
34
+ end
35
+ run_required
34
36
  end
35
37
 
36
38
  def calculate_filter_set
37
- output_filter_set(ALL_CATEGORIES)
39
+ output_filter_set([])
38
40
  end
39
41
 
40
42
  def output_filter_set(rule_refs)
41
- if rule_refs == ALL_CATEGORIES
42
- if @options.mode == Options::ONLINE
43
+ if rule_refs.empty?
44
+ if @options.mode == Options::ONLINE && @options.filter_with_patch_config
43
45
  @options.filter_set = Set[]
44
46
  doc = File.open(@options.patch_config) { |f| Nokogiri::XML(f) }
45
47
  rules = doc.css('ruleset rule')
46
48
  rules.each do |r|
47
49
  ref = r.attributes['ref'].content
48
- ref.delete_prefix!('category/java/')
50
+ ref.delete_prefix!('category/')
49
51
  @options.filter_set.add(ref)
50
52
  end
51
53
 
52
- logger.debug "Using filter based on patch config #{@options.patch_config}: " \
54
+ logger.info "Using filter based on patch config #{@options.patch_config}: " \
53
55
  "#{@options.filter_set}"
54
56
  else
55
- # if `rule_refs` contains all categories, then no need to filter the baseline
56
- logger.debug 'No filter when comparing patch to baseline'
57
+ # if `rule_refs` is empty, then no filter can be used when comparing to the baseline
58
+ logger.info 'No filter when comparing patch to baseline'
57
59
  @options.filter_set = nil
58
60
  end
59
61
  else
60
- logger.debug "Filter is now #{rule_refs}"
62
+ logger.info "Filter is now #{rule_refs}"
61
63
  @options.filter_set = rule_refs
62
64
  end
63
65
  end
64
66
 
65
- def diff_filenames
66
- filenames = nil
67
- Dir.chdir(@options.local_git_repo) do
68
- base = @options.base_branch
69
- patch = @options.patch_branch
70
- # We only need to support git here, since PMD's repo is using git.
71
- diff_cmd = "git diff --name-only #{base}..#{patch} -- pmd-core/src/main pmd-java/src/main"
72
- filenames = Cmd.execute(diff_cmd)
73
- end
74
- filenames.split("\n")
75
- end
76
-
67
+ #
68
+ # Determines the rules or category rulesets, that are potentially affected by the change.
69
+ # Returns an empty set, if all rules are affected and there is no
70
+ # filtering possible or if no rules are affected.
71
+ # Whether to run the regression test is returned as an additional boolean flag.
72
+ #
77
73
  def get_rule_refs(filenames)
78
- categories, rules = determine_categories_rules(filenames)
74
+ run_required, categories, rules = determine_categories_rules(filenames)
75
+ logger.debug "Regression test required: #{run_required}"
79
76
  logger.debug "Categories: #{categories}"
80
77
  logger.debug "Rules: #{rules}"
81
78
 
@@ -87,48 +84,11 @@ module PmdTester
87
84
  refs = Set[]
88
85
  refs.merge(categories)
89
86
  refs.merge(rules)
90
- refs
91
- end
92
-
93
- def determine_categories_rules(filenames)
94
- categories = Set[]
95
- rules = Set[]
96
- filenames.each do |filename|
97
- match_data = check_single_filename(filename)
98
-
99
- unless match_data.nil?
100
- if match_data.size == 2
101
- categories.add("#{match_data[1]}.xml")
102
- else
103
- rules.add("#{match_data[1]}.xml/#{match_data[2]}")
104
- end
105
- end
106
-
107
- next unless match_data.nil?
108
-
109
- logger.debug "Change doesn't match specific rule/category - enable all rules"
110
- categories = ALL_CATEGORIES
111
- rules.clear
112
- break
113
- end
114
- [categories, rules]
115
- end
116
-
117
- def check_single_filename(filename)
118
- logger.debug "Checking #{filename}"
119
- match_data = %r{#{PATH_TO_PMD_JAVA_BASED_RULES}/([^/]+)/([^/]+)Rule.java}.match(filename)
120
- match_data = %r{#{PATH_TO_PMD_XPATH_BASED_RULES}/([^/]+).xml}.match(filename) if match_data.nil?
121
- logger.debug "Matches: #{match_data.inspect}"
122
- match_data
87
+ [run_required, refs]
123
88
  end
124
89
 
125
90
  def build_config_file(rule_refs)
126
91
  if rule_refs.empty?
127
- logger.info NO_JAVA_RULES_CHANGED_MESSAGE
128
- return
129
- end
130
-
131
- if rule_refs == ALL_CATEGORIES
132
92
  logger.debug 'All rules are used. Not generating a dynamic ruleset.'
133
93
  logger.debug "Using the configured/default ruleset base_config=#{@options.base_config} "\
134
94
  "patch_config=#{@options.patch_config}"
@@ -147,7 +107,7 @@ module PmdTester
147
107
  'name' => 'Dynamic PmdTester Ruleset') do
148
108
  xml.description 'The ruleset generated by PmdTester dynamically'
149
109
  rule_refs.each do |entry|
150
- xml.rule('ref' => "category/java/#{entry}")
110
+ xml.rule('ref' => "category/#{entry}")
151
111
  end
152
112
  end
153
113
  end
@@ -158,5 +118,82 @@ module PmdTester
158
118
  @options.base_config = PATH_TO_DYNAMIC_CONFIG
159
119
  @options.patch_config = PATH_TO_DYNAMIC_CONFIG
160
120
  end
121
+
122
+ private
123
+
124
+ def determine_categories_rules(filenames)
125
+ regression_test_required = false
126
+ categories = Set[]
127
+ rules = Set[]
128
+ filenames.each do |filename|
129
+ matched = check_single_filename(filename, categories, rules)
130
+ regression_test_required = true if matched
131
+
132
+ next if matched
133
+
134
+ logger.info "Change in file #{filename} doesn't match specific rule/category - enable all rules"
135
+ regression_test_required = true
136
+ categories.clear
137
+ rules.clear
138
+ break
139
+ end
140
+ [regression_test_required, categories, rules]
141
+ end
142
+
143
+ def check_single_filename(filename, categories, rules)
144
+ logger.debug "Checking #{filename}"
145
+
146
+ # matches Java-based rule implementations
147
+ match_data = %r{.+/src/main/java/.+/lang/([^/]+)/rule/([^/]+)/([^/]+)Rule.java}.match(filename)
148
+ unless match_data.nil?
149
+ logger.debug "Matches: #{match_data.inspect}"
150
+ rules.add("#{match_data[1]}/#{match_data[2]}.xml/#{match_data[3]}")
151
+ return true
152
+ end
153
+
154
+ # matches xpath rules
155
+ match_data = %r{.+/src/main/resources/category/([^/]+)/([^/]+).xml}.match(filename)
156
+ unless match_data.nil?
157
+ logger.debug "Matches: #{match_data.inspect}"
158
+ categories.add("#{match_data[1]}/#{match_data[2]}.xml")
159
+ return true
160
+ end
161
+
162
+ false
163
+ end
164
+
165
+ def diff_filenames(languages)
166
+ filenames = nil
167
+ Dir.chdir(@options.local_git_repo) do
168
+ base = @options.base_branch
169
+ patch = @options.patch_branch
170
+
171
+ filepath_filter = ''
172
+ unless languages.empty?
173
+ filepath_filter = '-- pmd-core/src/main'
174
+ languages.each { |l| filepath_filter = "#{filepath_filter} pmd-#{l}/src/main" }
175
+ end
176
+
177
+ # We only need to support git here, since PMD's repo is using git.
178
+ diff_cmd = "git diff --name-only #{base}..#{patch} #{filepath_filter}"
179
+ filenames = Cmd.execute_successfully(diff_cmd)
180
+ end
181
+ filenames.split("\n")
182
+ end
183
+
184
+ #
185
+ # Determines all languages, that are part of the regression test.
186
+ # This is based on the configured rules/rulesets.
187
+ #
188
+ def determine_languages
189
+ languages = Set[]
190
+ doc = File.open(@options.patch_config) { |f| Nokogiri::XML(f) }
191
+ rules = doc.css('ruleset rule')
192
+ rules.each do |r|
193
+ ref = r.attributes['ref'].content
194
+ languages.add(ref.split('/')[1])
195
+ end
196
+ languages
197
+ end
161
198
  end
162
199
  end
data/lib/pmdtester/cmd.rb CHANGED
@@ -6,9 +6,35 @@ module PmdTester
6
6
  # Containing the common method for executing shell command
7
7
  class Cmd
8
8
  extend PmdTester
9
- def self.execute(cmd)
10
- stdout, _stderr, _status = internal_execute(cmd)
11
- stdout&.chomp!
9
+
10
+ #
11
+ # Executes the given command and returns the process status.
12
+ # stdout and stderr are written to the files "stdout.txt" and "stderr.txt"
13
+ # in path.
14
+ #
15
+ def self.execute(cmd, path)
16
+ stdout, stderr, status = internal_execute(cmd)
17
+
18
+ file = File.new("#{path}/stdout.txt", 'w')
19
+ file.puts stdout
20
+ file.close
21
+
22
+ file = File.new("#{path}/stderr.txt", 'w')
23
+ file.puts stderr
24
+ file.close
25
+
26
+ status
27
+ end
28
+
29
+ def self.execute_successfully(cmd)
30
+ stdout, stderr, status = internal_execute(cmd)
31
+
32
+ unless status.success?
33
+ logger.error stdout
34
+ logger.error stderr
35
+ raise CmdException.new(cmd, stdout, stderr, status)
36
+ end
37
+
12
38
  stdout
13
39
  end
14
40
 
@@ -22,14 +48,12 @@ module PmdTester
22
48
 
23
49
  stdout, stderr, status = Open3.capture3("#{cmd};")
24
50
 
25
- logger.debug stdout
26
- unless status.success?
27
- logger.error stdout
28
- logger.error stderr
29
- raise CmdException.new(cmd, stderr)
30
- end
51
+ logger.debug "status: #{status}"
52
+ logger.debug "stdout: #{stdout}"
53
+ logger.debug "stderr: #{stderr}"
31
54
 
32
55
  stdout&.chomp!
56
+ stderr&.chomp!
33
57
 
34
58
  [stdout, stderr, status]
35
59
  end
@@ -40,15 +64,19 @@ module PmdTester
40
64
  # The exception should be raised when the shell command failed.
41
65
  class CmdException < StandardError
42
66
  attr_reader :cmd
67
+ attr_reader :stdout
43
68
  attr_reader :error
69
+ attr_reader :status
44
70
  attr_reader :message
45
71
 
46
72
  COMMON_MSG = 'An error occurred while executing the shell command'
47
73
 
48
- def initialize(cmd, error)
74
+ def initialize(cmd, stdout, error, status)
49
75
  @cmd = cmd
76
+ @stdout = stdout
50
77
  @error = error
51
- @message = "#{COMMON_MSG} '#{cmd}'"
78
+ @status = status
79
+ @message = "#{COMMON_MSG} '#{cmd}' #{status}"
52
80
  end
53
81
  end
54
82
  end
@@ -89,11 +89,10 @@ module PmdTester
89
89
  def match_filter_set?(violation)
90
90
  return true if @filter_set.nil?
91
91
 
92
- ruleset_attr = violation.ruleset_name.delete(' ').downcase! << '.xml'
93
- return true if @filter_set.include?(ruleset_attr)
94
-
95
- rule_ref = "#{ruleset_attr}/#{violation.rule_name}"
92
+ ruleset_filter = violation.language << '/' << violation.ruleset_name.delete(' ').downcase! << '.xml'
93
+ return true if @filter_set.include?(ruleset_filter)
96
94
 
95
+ rule_ref = "#{ruleset_filter}/#{violation.rule_name}"
97
96
  @filter_set.include?(rule_ref)
98
97
  end
99
98
 
@@ -8,15 +8,22 @@ module PmdTester
8
8
  attr_accessor :execution_time
9
9
  attr_accessor :timestamp
10
10
  attr_accessor :working_dir
11
+ attr_accessor :exit_code
11
12
 
12
- def initialize(execution_time: 0, timestamp: '', working_dir: Dir.getwd)
13
+ def initialize(execution_time: 0, timestamp: '', working_dir: Dir.getwd, exit_code: nil)
13
14
  @execution_time = execution_time
14
15
  @timestamp = timestamp
15
16
  @working_dir = working_dir
17
+ @exit_code = exit_code.nil? ? '?' : exit_code.to_s
16
18
  end
17
19
 
18
20
  def save(report_info_path)
19
- hash = { execution_time: @execution_time, timestamp: @timestamp, working_dir: @working_dir }
21
+ hash = {
22
+ execution_time: @execution_time,
23
+ timestamp: @timestamp,
24
+ working_dir: @working_dir,
25
+ exit_code: @exit_code
26
+ }
20
27
  file = File.new(report_info_path, 'w')
21
28
  file.puts JSON.generate(hash)
22
29
  file.close
@@ -36,6 +43,13 @@ module PmdTester
36
43
  self.class.convert_seconds(@execution_time)
37
44
  end
38
45
 
46
+ def self.create(execution_time: 0, timestamp: '', working_dir: Dir.getwd, exit_code: nil, report_info_path:)
47
+ detail = PmdReportDetail.new(execution_time: execution_time, timestamp: timestamp,
48
+ working_dir: working_dir, exit_code: exit_code)
49
+ detail.save(report_info_path)
50
+ detail
51
+ end
52
+
39
53
  # convert seconds into HH::MM::SS
40
54
  def self.convert_seconds(seconds)
41
55
  Time.at(seconds.abs).utc.strftime('%H:%M:%S')
@@ -32,7 +32,8 @@ module PmdTester
32
32
  file: report_file,
33
33
 
34
34
  timestamp: report_details.timestamp,
35
- exec_time: report_details.execution_time
35
+ exec_time: report_details.execution_time,
36
+ exit_code: report_details.exit_code
36
37
  )
37
38
  end
38
39
 
@@ -27,7 +27,7 @@ module PmdTester
27
27
  # </xs:simpleContent>
28
28
  # </xs:complexType>
29
29
 
30
- attr_reader :fname, :info_url, :line, :old_line, :old_message, :rule_name, :ruleset_name
30
+ attr_reader :fname, :info_url, :line, :old_line, :old_message, :rule_name, :ruleset_name, :language
31
31
  attr_accessor :message
32
32
 
33
33
  # rubocop:disable Metrics/ParameterLists
@@ -43,6 +43,8 @@ module PmdTester
43
43
 
44
44
  @ruleset_name = ruleset_name
45
45
 
46
+ @language = determine_language_from_info_url
47
+
46
48
  @changed = false
47
49
  @old_message = nil
48
50
  @old_line = nil
@@ -102,5 +104,13 @@ module PmdTester
102
104
  'changed' => changed?
103
105
  }
104
106
  end
107
+
108
+ private
109
+
110
+ def determine_language_from_info_url
111
+ # @info_url is e.g. http://pmd.sourceforge.net/snapshot/pmd_rules_java_codestyle.html#fielddeclarationsshouldbeatstartofclass
112
+ m = @info_url.match(/pmd_rules_(\w+)_/)
113
+ m[1]
114
+ end
105
115
  end
106
116
  end
@@ -123,6 +123,9 @@ module PmdTester
123
123
  get_report_info_path(base_branch),
124
124
  get_report_info_path(patch_branch),
125
125
  filter_set)
126
+
127
+ report_diff.base_report.report_folder = get_project_target_dir(base_branch)
128
+ report_diff.patch_report.report_folder = get_project_target_dir(patch_branch)
126
129
  end
127
130
  end
128
131
  end
@@ -57,17 +57,22 @@ module PmdTester
57
57
  :configerrors_by_rule,
58
58
  :exec_time,
59
59
  :timestamp,
60
+ :exit_code,
60
61
  :file
61
62
 
63
+ attr_accessor :report_folder
64
+
62
65
  def initialize(report_document: nil,
63
66
  file: '',
64
67
  exec_time: 0,
65
- timestamp: '0')
68
+ timestamp: '0',
69
+ exit_code: '?')
66
70
  initialize_empty
67
71
  initialize_with_report_document report_document unless report_document.nil?
68
72
  @exec_time = exec_time
69
73
  @timestamp = timestamp
70
74
  @file = file
75
+ @exit_code = exit_code
71
76
  end
72
77
 
73
78
  def self.empty
@@ -108,6 +113,7 @@ module PmdTester
108
113
  @violations_by_file = CollectionByFile.new
109
114
  @errors_by_file = CollectionByFile.new
110
115
  @configerrors_by_rule = {}
116
+ @report_folder = ''
111
117
  end
112
118
  end
113
119
 
@@ -33,8 +33,11 @@ module PmdTester
33
33
  def run_local_mode
34
34
  logger.info "Mode: #{@options.mode}"
35
35
  get_projects(@options.project_list) unless @options.nil?
36
- rule_sets = RuleSetBuilder.new(@options).build if @options.auto_config_flag
37
- return if rule_sets&.empty?
36
+ if @options.auto_config_flag
37
+ run_required = RuleSetBuilder.new(@options).build?
38
+ logger.debug "Run required: #{run_required}"
39
+ return unless run_required
40
+ end
38
41
 
39
42
  base_branch_details = create_pmd_report(config: @options.base_config, branch: @options.base_branch)
40
43
  patch_branch_details = create_pmd_report(config: @options.patch_config, branch: @options.patch_branch)
@@ -51,7 +54,8 @@ module PmdTester
51
54
  get_projects(project_list)
52
55
 
53
56
  if @options.auto_config_flag
54
- return if RuleSetBuilder.new(@options).build.empty?
57
+ logger.info 'Autogenerating a dynamic ruleset based on source changes'
58
+ return unless RuleSetBuilder.new(@options).build?
55
59
  elsif @options.patch_config == Options::DEFAULT_CONFIG_PATH
56
60
  # patch branch build pmd reports with same configuration as base branch
57
61
  # if not specified otherwise. This allows to use a different config (e.g. less rules)
@@ -93,8 +97,8 @@ module PmdTester
93
97
  unzip_cmd = "unzip -qo #{zip_filename}"
94
98
 
95
99
  Dir.chdir(target_path) do
96
- Cmd.execute(wget_cmd) unless File.exist?(zip_filename)
97
- Cmd.execute(unzip_cmd)
100
+ Cmd.execute_successfully(wget_cmd) unless File.exist?(zip_filename)
101
+ Cmd.execute_successfully(unzip_cmd)
98
102
  end
99
103
 
100
104
  "#{target_path}/#{branch_filename}"
@@ -4,6 +4,12 @@ module PmdTester
4
4
  # Utility to deal with semantic versions
5
5
  class Semver
6
6
  def self.compare(version_a, version_b)
7
+ result = internal_compare(version_a, version_b)
8
+ PmdTester.logger.debug " result: #{result}"
9
+ result
10
+ end
11
+
12
+ private_class_method def self.internal_compare(version_a, version_b)
7
13
  PmdTester.logger.debug "Comparing #{version_a} <=> #{version_b}"
8
14
  m = /(\d+)\.(\d+)\.(\d+)(.*)/.match(version_a)
9
15
  a_major = m[1].to_i
data/lib/pmdtester.rb CHANGED
@@ -33,7 +33,7 @@ require_relative 'pmdtester/parsers/projects_parser'
33
33
  # and unexpected behaviors will not be introduced to PMD project
34
34
  # after fixing an issue and new rules can work as expected.
35
35
  module PmdTester
36
- VERSION = '1.3.0'
36
+ VERSION = '1.5.0'
37
37
  BASE = 'base'
38
38
  PATCH = 'patch'
39
39
  PR_NUM_ENV_VAR = 'PMD_CI_PULL_REQUEST_NUMBER' # see PmdBranchDetail
data/pmdtester.gemspec CHANGED
@@ -1,17 +1,17 @@
1
1
  # DO NOT EDIT THIS FILE. Instead, edit Rakefile, and run `rake hoe:spec`.
2
2
 
3
3
  # -*- encoding: utf-8 -*-
4
- # stub: pmdtester 1.3.0 ruby lib
4
+ # stub: pmdtester 1.5.0 ruby lib
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "pmdtester".freeze
8
- s.version = "1.3.0"
8
+ s.version = "1.5.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
11
11
  s.metadata = { "bug_tracker_uri" => "https://github.com/pmd/pmd-regression-tester/issues", "homepage_uri" => "https://pmd.github.io", "source_code_uri" => "https://github.com/pmd/pmd-regression-tester" } if s.respond_to? :metadata=
12
12
  s.require_paths = ["lib".freeze]
13
13
  s.authors = ["Andreas Dangel".freeze, "Binguo Bao".freeze, "Cl\u00E9ment Fournier".freeze]
14
- s.date = "2021-12-17"
14
+ s.date = "2022-05-06"
15
15
  s.description = "A regression testing tool ensure that new problems and unexpected behaviors will not be introduced to PMD project after fixing an issue , and new rules can work as expected.".freeze
16
16
  s.email = ["andreas.dangel@pmd-code.org".freeze, "djydewang@gmail.com".freeze, "clement.fournier76@gmail.com".freeze]
17
17
  s.executables = ["pmdtester".freeze]
@@ -29,34 +29,34 @@ Gem::Specification.new do |s|
29
29
  end
30
30
 
31
31
  if s.respond_to? :add_runtime_dependency then
32
- s.add_runtime_dependency(%q<nokogiri>.freeze, [">= 1.11.0.rc4"])
32
+ s.add_runtime_dependency(%q<nokogiri>.freeze, ["~> 1.13"])
33
33
  s.add_runtime_dependency(%q<slop>.freeze, ["~> 4.6"])
34
34
  s.add_runtime_dependency(%q<differ>.freeze, ["~> 0.1"])
35
- s.add_runtime_dependency(%q<rufus-scheduler>.freeze, ["~> 3.5"])
35
+ s.add_runtime_dependency(%q<rufus-scheduler>.freeze, ["~> 3.8"])
36
36
  s.add_runtime_dependency(%q<logger-colors>.freeze, ["~> 1.0"])
37
- s.add_runtime_dependency(%q<liquid>.freeze, [">= 4.0"])
37
+ s.add_runtime_dependency(%q<liquid>.freeze, ["~> 5.2"])
38
38
  s.add_development_dependency(%q<hoe-bundler>.freeze, ["~> 1.5"])
39
39
  s.add_development_dependency(%q<hoe-git>.freeze, ["~> 1.6"])
40
40
  s.add_development_dependency(%q<minitest>.freeze, ["~> 5.10"])
41
41
  s.add_development_dependency(%q<mocha>.freeze, ["~> 1.5"])
42
- s.add_development_dependency(%q<rubocop>.freeze, ["~> 0.81"])
43
- s.add_development_dependency(%q<test-unit>.freeze, ["~> 3.2"])
44
- s.add_development_dependency(%q<rdoc>.freeze, [">= 4.0", "< 7"])
42
+ s.add_development_dependency(%q<rubocop>.freeze, ["~> 0.93"])
43
+ s.add_development_dependency(%q<test-unit>.freeze, ["~> 3.5"])
44
+ s.add_development_dependency(%q<rdoc>.freeze, ["~> 6.4"])
45
45
  s.add_development_dependency(%q<hoe>.freeze, ["~> 3.23"])
46
46
  else
47
- s.add_dependency(%q<nokogiri>.freeze, [">= 1.11.0.rc4"])
47
+ s.add_dependency(%q<nokogiri>.freeze, ["~> 1.13"])
48
48
  s.add_dependency(%q<slop>.freeze, ["~> 4.6"])
49
49
  s.add_dependency(%q<differ>.freeze, ["~> 0.1"])
50
- s.add_dependency(%q<rufus-scheduler>.freeze, ["~> 3.5"])
50
+ s.add_dependency(%q<rufus-scheduler>.freeze, ["~> 3.8"])
51
51
  s.add_dependency(%q<logger-colors>.freeze, ["~> 1.0"])
52
- s.add_dependency(%q<liquid>.freeze, [">= 4.0"])
52
+ s.add_dependency(%q<liquid>.freeze, ["~> 5.2"])
53
53
  s.add_dependency(%q<hoe-bundler>.freeze, ["~> 1.5"])
54
54
  s.add_dependency(%q<hoe-git>.freeze, ["~> 1.6"])
55
55
  s.add_dependency(%q<minitest>.freeze, ["~> 5.10"])
56
56
  s.add_dependency(%q<mocha>.freeze, ["~> 1.5"])
57
- s.add_dependency(%q<rubocop>.freeze, ["~> 0.81"])
58
- s.add_dependency(%q<test-unit>.freeze, ["~> 3.2"])
59
- s.add_dependency(%q<rdoc>.freeze, [">= 4.0", "< 7"])
57
+ s.add_dependency(%q<rubocop>.freeze, ["~> 0.93"])
58
+ s.add_dependency(%q<test-unit>.freeze, ["~> 3.5"])
59
+ s.add_dependency(%q<rdoc>.freeze, ["~> 6.4"])
60
60
  s.add_dependency(%q<hoe>.freeze, ["~> 3.23"])
61
61
  end
62
62
  end
@@ -71,6 +71,12 @@
71
71
  <td class="patch">{{diff.patch_timestamp}}</td>
72
72
  <td class="diff"></td>
73
73
  </tr>
74
+ <tr>
75
+ <td class="item">Exit Code</td>
76
+ <td class="base">{{diff.base_exit_code}} <a href="base_stdout.txt">stdout</a> | <a href="base_stderr.txt">stderr</a></td>
77
+ <td class="patch">{{diff.patch_exit_code}} <a href="patch_stdout.txt">stdout</a> | <a href="patch_stderr.txt">stderr</a></td>
78
+ <td class="diff"></td>
79
+ </tr>
74
80
  <tr>
75
81
  <td class="item">Full Report</td>
76
82
  <td class="base"><a href="base_pmd_report.html">Base PMD Report</a></td>
@@ -53,6 +53,10 @@
53
53
  <td class="item">Timestamp</td>
54
54
  <td class="{{branch}}">{{report.timestamp}}</td>
55
55
  </tr>
56
+ <tr>
57
+ <td class="item">Exit Code</td>
58
+ <td class="{{branch}}">{{report.exit_code}} <a href="{{branch}}_stdout.txt">stdout</a> | <a href="{{branch}}_stderr.txt">stderr</a></td>
59
+ </tr>
56
60
  <tr>
57
61
  <td class="item">Full Report</td>
58
62
  <td class="{{branch}}"><a href="{{branch}}_pmd_report.xml">{{branch}}_pmd_report.xml</a></td>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pmdtester
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andreas Dangel
@@ -10,22 +10,22 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2021-12-17 00:00:00.000000000 Z
13
+ date: 2022-05-06 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: nokogiri
17
17
  requirement: !ruby/object:Gem::Requirement
18
18
  requirements:
19
- - - ">="
19
+ - - "~>"
20
20
  - !ruby/object:Gem::Version
21
- version: 1.11.0.rc4
21
+ version: '1.13'
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  requirements:
26
- - - ">="
26
+ - - "~>"
27
27
  - !ruby/object:Gem::Version
28
- version: 1.11.0.rc4
28
+ version: '1.13'
29
29
  - !ruby/object:Gem::Dependency
30
30
  name: slop
31
31
  requirement: !ruby/object:Gem::Requirement
@@ -60,14 +60,14 @@ dependencies:
60
60
  requirements:
61
61
  - - "~>"
62
62
  - !ruby/object:Gem::Version
63
- version: '3.5'
63
+ version: '3.8'
64
64
  type: :runtime
65
65
  prerelease: false
66
66
  version_requirements: !ruby/object:Gem::Requirement
67
67
  requirements:
68
68
  - - "~>"
69
69
  - !ruby/object:Gem::Version
70
- version: '3.5'
70
+ version: '3.8'
71
71
  - !ruby/object:Gem::Dependency
72
72
  name: logger-colors
73
73
  requirement: !ruby/object:Gem::Requirement
@@ -86,16 +86,16 @@ dependencies:
86
86
  name: liquid
87
87
  requirement: !ruby/object:Gem::Requirement
88
88
  requirements:
89
- - - ">="
89
+ - - "~>"
90
90
  - !ruby/object:Gem::Version
91
- version: '4.0'
91
+ version: '5.2'
92
92
  type: :runtime
93
93
  prerelease: false
94
94
  version_requirements: !ruby/object:Gem::Requirement
95
95
  requirements:
96
- - - ">="
96
+ - - "~>"
97
97
  - !ruby/object:Gem::Version
98
- version: '4.0'
98
+ version: '5.2'
99
99
  - !ruby/object:Gem::Dependency
100
100
  name: hoe-bundler
101
101
  requirement: !ruby/object:Gem::Requirement
@@ -158,48 +158,42 @@ dependencies:
158
158
  requirements:
159
159
  - - "~>"
160
160
  - !ruby/object:Gem::Version
161
- version: '0.81'
161
+ version: '0.93'
162
162
  type: :development
163
163
  prerelease: false
164
164
  version_requirements: !ruby/object:Gem::Requirement
165
165
  requirements:
166
166
  - - "~>"
167
167
  - !ruby/object:Gem::Version
168
- version: '0.81'
168
+ version: '0.93'
169
169
  - !ruby/object:Gem::Dependency
170
170
  name: test-unit
171
171
  requirement: !ruby/object:Gem::Requirement
172
172
  requirements:
173
173
  - - "~>"
174
174
  - !ruby/object:Gem::Version
175
- version: '3.2'
175
+ version: '3.5'
176
176
  type: :development
177
177
  prerelease: false
178
178
  version_requirements: !ruby/object:Gem::Requirement
179
179
  requirements:
180
180
  - - "~>"
181
181
  - !ruby/object:Gem::Version
182
- version: '3.2'
182
+ version: '3.5'
183
183
  - !ruby/object:Gem::Dependency
184
184
  name: rdoc
185
185
  requirement: !ruby/object:Gem::Requirement
186
186
  requirements:
187
- - - ">="
188
- - !ruby/object:Gem::Version
189
- version: '4.0'
190
- - - "<"
187
+ - - "~>"
191
188
  - !ruby/object:Gem::Version
192
- version: '7'
189
+ version: '6.4'
193
190
  type: :development
194
191
  prerelease: false
195
192
  version_requirements: !ruby/object:Gem::Requirement
196
193
  requirements:
197
- - - ">="
198
- - !ruby/object:Gem::Version
199
- version: '4.0'
200
- - - "<"
194
+ - - "~>"
201
195
  - !ruby/object:Gem::Version
202
- version: '7'
196
+ version: '6.4'
203
197
  - !ruby/object:Gem::Dependency
204
198
  name: hoe
205
199
  requirement: !ruby/object:Gem::Requirement