pmdtester 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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