pdd 0.24.1 → 0.24.2

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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +4 -2
  3. data/Gemfile.lock +75 -54
  4. data/LICENSE.txt +1 -1
  5. data/LICENSES/MIT.txt +1 -1
  6. data/README.md +46 -61
  7. data/REUSE.toml +0 -2
  8. data/Rakefile +1 -1
  9. data/assets/puzzles.xsd +1 -1
  10. data/assets/puzzles.xsl +1 -1
  11. data/assets/puzzles_json.xsl +1 -1
  12. data/bin/pdd +3 -4
  13. data/cucumber.yml +1 -1
  14. data/features/applies_rules.feature +1 -1
  15. data/features/avoiding_duplicates.feature +1 -1
  16. data/features/catches_broken_puzzles.feature +1 -1
  17. data/features/cli.feature +1 -1
  18. data/features/gem_package.feature +1 -1
  19. data/features/html_output.feature +1 -1
  20. data/features/json_output.feature +1 -1
  21. data/features/parsing.feature +1 -1
  22. data/features/rake.feature +1 -1
  23. data/features/remove.feature +2 -1
  24. data/features/step_definitions/steps.rb +1 -1
  25. data/features/support/env.rb +1 -1
  26. data/features/unicode.feature +1 -1
  27. data/features/uses_config.feature +1 -1
  28. data/lib/pdd/puzzle.rb +1 -1
  29. data/lib/pdd/rake_task.rb +1 -2
  30. data/lib/pdd/rule/duplicates.rb +1 -1
  31. data/lib/pdd/rule/estimates.rb +1 -1
  32. data/lib/pdd/rule/roles.rb +1 -1
  33. data/lib/pdd/rule/text.rb +1 -1
  34. data/lib/pdd/source.rb +1 -1
  35. data/lib/pdd/sources.rb +1 -1
  36. data/lib/pdd/version.rb +3 -3
  37. data/lib/pdd.rb +2 -2
  38. data/pdd.gemspec +2 -2
  39. data/utils/glob.rb +1 -1
  40. metadata +2 -31
  41. data/.0pdd.yml +0 -12
  42. data/.gitattributes +0 -12
  43. data/.github/workflows/actionlint.yml +0 -25
  44. data/.github/workflows/codecov.yml +0 -29
  45. data/.github/workflows/copyrights.yml +0 -19
  46. data/.github/workflows/markdown-lint.yml +0 -19
  47. data/.github/workflows/pdd.yml +0 -19
  48. data/.github/workflows/rake.yml +0 -29
  49. data/.github/workflows/reuse.yml +0 -19
  50. data/.github/workflows/typos.yml +0 -19
  51. data/.github/workflows/xcop.yml +0 -19
  52. data/.github/workflows/yamllint.yml +0 -19
  53. data/.gitignore +0 -15
  54. data/.markdownlint.yml +0 -6
  55. data/.pdd +0 -26
  56. data/.rubocop.yml +0 -42
  57. data/.rultor.yml +0 -26
  58. data/.simplecov +0 -18
  59. data/test/test__helper.rb +0 -44
  60. data/test/test_duplicates.rb +0 -31
  61. data/test/test_estimates.rb +0 -30
  62. data/test/test_many.rb +0 -25
  63. data/test/test_pdd.rb +0 -94
  64. data/test/test_rake_task.rb +0 -21
  65. data/test/test_roles.rb +0 -39
  66. data/test/test_source.rb +0 -379
  67. data/test/test_source_todo.rb +0 -278
  68. data/test/test_sources.rb +0 -120
  69. data/test/test_text.rb +0 -22
data/test/test_many.rb DELETED
@@ -1,25 +0,0 @@
1
- # SPDX-FileCopyrightText: Copyright (c) 2014-2025 Yegor Bugayenko
2
- # SPDX-License-Identifier: MIT
3
-
4
- require 'minitest/autorun'
5
- require 'tmpdir'
6
- require_relative '../lib/pdd'
7
- require_relative '../lib/pdd/sources'
8
-
9
- # Test many puzzles to make sure their IDs are correct.
10
- # Author:: Yegor Bugayenko (yegor256@gmail.com)
11
- # Copyright:: Copyright (c) 2014-2025 Yegor Bugayenko
12
- # License:: MIT
13
- class TestMany < Minitest::Test
14
- def test_parsing
15
- Dir['./test_assets/puzzles/**'].each do |p|
16
- name = File.basename(p)
17
- list = PDD::Source.new("./test_assets/puzzles/#{name}", 'hey').puzzles
18
- assert_equal 1, list.size
19
- puzzle = list.first
20
- puts "#{name}: \"#{puzzle.props[:body]}\""
21
- next if name.start_with?('_')
22
- assert_equal name, puzzle.props[:id]
23
- end
24
- end
25
- end
data/test/test_pdd.rb DELETED
@@ -1,94 +0,0 @@
1
- # SPDX-FileCopyrightText: Copyright (c) 2014-2025 Yegor Bugayenko
2
- # SPDX-License-Identifier: MIT
3
-
4
- require 'minitest/autorun'
5
- require 'nokogiri'
6
- require 'tmpdir'
7
- require 'slop'
8
- require_relative '../lib/pdd'
9
-
10
- # PDD main module test.
11
- # Author:: Yegor Bugayenko (yegor256@gmail.com)
12
- # Copyright:: Copyright (c) 2014-2025 Yegor Bugayenko
13
- # License:: MIT
14
- class TestPDD < Minitest::Test
15
- def test_basic
16
- Dir.mktmpdir 'test' do |dir|
17
- opts = opts(['-q', '-s', dir, '-e', '**/*.png', '-r', 'max-estimate:15'])
18
- File.write(File.join(dir, 'a.txt'), "\x40todo #55 hello!")
19
- matches(
20
- Nokogiri::XML(PDD::Base.new(opts).xml),
21
- [
22
- '/processing-instruction("xml-stylesheet")[contains(.,".xsl")]',
23
- '/puzzles/@version',
24
- '/puzzles/@date',
25
- '/puzzles[count(puzzle)=1]',
26
- '/puzzles/puzzle[file="a.txt"]'
27
- ]
28
- )
29
- end
30
- end
31
-
32
- def test_rules_failure
33
- Dir.mktmpdir 'test' do |dir|
34
- opts = opts(['-q', '-s', dir, '-e', '**/*.png', '-r', 'min-estimate:30'])
35
- File.write(File.join(dir, 'a.txt'), "\x40todo #90 hello!")
36
- assert_raises PDD::Error do
37
- PDD::Base.new(opts).xml
38
- end
39
- end
40
- end
41
-
42
- def test_git_repo
43
- skip if Gem.win_platform?
44
- Dir.mktmpdir 'test' do |dir|
45
- opts = opts(['-q', '-s', dir])
46
- raise unless system("
47
- set -e
48
- cd '#{dir}'
49
- git init --quiet .
50
- git config user.email test@teamed.io
51
- git config user.name 'Mr. Tester'
52
- mkdir 'a long dir name'
53
- cd 'a long dir name'
54
- mkdir 'a kid'
55
- cd 'a kid'
56
- echo '\x40todo #1 this is some puzzle' > '.это файл.txt'
57
- cd ../..
58
- git add -f .
59
- git commit --no-verify --quiet -am 'first version'
60
- ")
61
-
62
- matches(
63
- Nokogiri::XML(PDD::Base.new(opts).xml),
64
- [
65
- '/puzzles[count(puzzle)=1]',
66
- '/puzzles/puzzle[id]',
67
- '/puzzles/puzzle[file="a long dir name/a kid/.это файл.txt"]',
68
- '/puzzles/puzzle[author="Mr. Tester"]',
69
- '/puzzles/puzzle[email="test@teamed.io"]',
70
- '/puzzles/puzzle[time]'
71
- ]
72
- )
73
- end
74
- end
75
-
76
- private
77
-
78
- def opts(args)
79
- Slop.parse args do |o|
80
- o.bool '-v', '--verbose'
81
- o.bool '-q', '--quiet'
82
- o.bool '--skip-errors'
83
- o.string '-s', '--source'
84
- o.array '-e', '--exclude'
85
- o.array '-r', '--rule'
86
- end
87
- end
88
-
89
- def matches(xml, xpaths)
90
- xpaths.each do |xpath|
91
- raise "doesn't match '#{xpath}': #{xml}" unless xml.xpath(xpath).size == 1
92
- end
93
- end
94
- end
@@ -1,21 +0,0 @@
1
- # SPDX-FileCopyrightText: Copyright (c) 2014-2025 Yegor Bugayenko
2
- # SPDX-License-Identifier: MIT
3
-
4
- require 'minitest/autorun'
5
- require 'tmpdir'
6
- require 'rake'
7
- require_relative '../lib/pdd/rake_task'
8
-
9
- # Test for RakeTask
10
- class TestRakeTask < Minitest::Test
11
- def test_basic
12
- Dir.mktmpdir 'test' do |dir|
13
- file = File.join(dir, 'a.txt')
14
- File.write(file, "\x40todo #55 hello!")
15
- PDD::RakeTask.new(:pdd1) do |task|
16
- task.quiet = true
17
- end
18
- Rake::Task['pdd1'].invoke
19
- end
20
- end
21
- end
data/test/test_roles.rb DELETED
@@ -1,39 +0,0 @@
1
- # SPDX-FileCopyrightText: Copyright (c) 2014-2025 Yegor Bugayenko
2
- # SPDX-License-Identifier: MIT
3
-
4
- require 'minitest/autorun'
5
- require 'nokogiri'
6
- require_relative '../lib/pdd/rule/roles'
7
-
8
- # PDD::Rule::Role module tests.
9
- # Author:: Yegor Bugayenko (yegor256@gmail.com)
10
- # Copyright:: Copyright (c) 2014-2025 Yegor Bugayenko
11
- # License:: MIT
12
- class TestRoles < Minitest::Test
13
- def test_incorrect_role
14
- rule = PDD::Rule::Roles::Available.new(
15
- Nokogiri::XML::Document.parse(
16
- '<puzzles><puzzle><role>D</role></puzzle></puzzles>'
17
- ), 'A,B,C'
18
- )
19
- refute_empty rule.errors, 'why it is empty?'
20
- end
21
-
22
- def test_correct_role
23
- rule = PDD::Rule::Roles::Available.new(
24
- Nokogiri::XML::Document.parse(
25
- '<puzzles><puzzle><role>F</role></puzzle></puzzles>'
26
- ), 'F,E,G'
27
- )
28
- assert_empty rule.errors, 'why it is not empty?'
29
- end
30
-
31
- def test_empty_role
32
- rule = PDD::Rule::Roles::Available.new(
33
- Nokogiri::XML::Document.parse(
34
- '<puzzles><puzzle></puzzle></puzzles>'
35
- ), 'T,R,L'
36
- )
37
- refute_empty rule.errors, 'why it is empty?'
38
- end
39
- end
data/test/test_source.rb DELETED
@@ -1,379 +0,0 @@
1
- # SPDX-FileCopyrightText: Copyright (c) 2014-2025 Yegor Bugayenko
2
- # SPDX-License-Identifier: MIT
3
-
4
- require 'minitest/autorun'
5
- require 'tmpdir'
6
- require_relative '../lib/pdd'
7
- require_relative '../lib/pdd/sources'
8
- require_relative 'test__helper'
9
-
10
- # Source test.
11
- # Author:: Yegor Bugayenko (yegor256@gmail.com)
12
- # Copyright:: Copyright (c) 2014-2025 Yegor Bugayenko
13
- # License:: MIT
14
- class TestSource < Minitest::Test
15
- def test_parsing
16
- Dir.mktmpdir 'test' do |dir|
17
- file = File.join(dir, 'a.txt')
18
- File.write(
19
- file,
20
- "
21
- * \x40todo #44 привет,
22
- * how are you\t\tdoing?
23
- * -something else
24
- Something else
25
- ~~ \x40todo #ABC-3 this is another puzzle
26
- ~~ and it also has to work
27
- "
28
- )
29
- stub_source_find_github_user(file, 'hey') do |source|
30
- list = source.puzzles
31
- assert_equal 2, list.size
32
- puzzle = list.first
33
- assert_equal '2-3', puzzle.props[:lines]
34
- assert_equal 'привет, how are you doing?',
35
- puzzle.props[:body]
36
- assert_equal '44', puzzle.props[:ticket]
37
- assert_nil puzzle.props[:author]
38
- assert_nil puzzle.props[:email]
39
- assert_nil puzzle.props[:time]
40
- end
41
- end
42
- end
43
-
44
- def test_parsing_leading_spaces
45
- Dir.mktmpdir 'test' do |dir|
46
- file = File.join(dir, 'a.txt')
47
- File.write(
48
- file,
49
- "
50
- * \x40todo #56:30min this is a
51
- * multi-line
52
- * comment!
53
- "
54
- )
55
- stub_source_find_github_user(file, 'hey') do |source|
56
- list = source.puzzles
57
- assert_equal 1, list.size
58
- puzzle = list.first
59
- assert_equal '2-4', puzzle.props[:lines]
60
- assert_equal 'this is a multi-line comment!', puzzle.props[:body]
61
- assert_equal '56', puzzle.props[:ticket]
62
- end
63
- end
64
- end
65
-
66
- def test_no_prefix_multiline_puzzle_block
67
- Dir.mktmpdir 'test' do |dir|
68
- file = File.join(dir, 'a.txt')
69
- File.write(
70
- file,
71
- "
72
- <!--
73
- \x40todo #01:30min correctly formatted multi-line puzzle, with no
74
- comment prefix before todo marker
75
- -->
76
- "
77
- )
78
- stub_source_find_github_user(file, 'hey') do |source|
79
- PDD.opts = nil
80
- assert_equal 1, source.puzzles.size
81
- puzzle = source.puzzles.last
82
- assert_equal '3-4', puzzle.props[:lines]
83
- assert_equal 'correctly formatted multi-line puzzle, with no ' \
84
- 'comment prefix before todo marker', puzzle.props[:body]
85
- assert_equal '01', puzzle.props[:ticket]
86
- end
87
- end
88
- end
89
-
90
- def test_space_indented_multiline_puzzle_block
91
- Dir.mktmpdir 'test' do |dir|
92
- file = File.join(dir, 'a.txt')
93
- File.write(
94
- file,
95
- "
96
- # \x40todo #99:30min hello
97
- # good bye
98
- # hello again
99
- "
100
- )
101
- stub_source_find_github_user(file, 'hey') do |source|
102
- PDD.opts = nil
103
- assert_equal 1, source.puzzles.size
104
- puzzle = source.puzzles.last
105
- assert_equal '2-3', puzzle.props[:lines]
106
- assert_equal 'hello good bye', puzzle.props[:body]
107
- assert_equal '99', puzzle.props[:ticket]
108
- end
109
- end
110
- end
111
-
112
- def test_multiple_puzzles_single_comment_block
113
- Dir.mktmpdir 'test' do |dir|
114
- file = File.join(dir, 'a.txt')
115
- File.write(
116
- file,
117
- "
118
- /*
119
- * \x40todo #1 First one with
120
- * a few lines
121
- * \x40todo #1 Second one also
122
- * with a few lines of text
123
- */
124
- "
125
- )
126
- stub_source_find_github_user(file, 'hey') do |source|
127
- PDD.opts = nil
128
- assert_equal 2, source.puzzles.size
129
- puzzle = source.puzzles.last
130
- assert_equal '5-6', puzzle.props[:lines]
131
- assert_equal 'Second one also with a few lines of text', puzzle.props[:body]
132
- assert_equal '1', puzzle.props[:ticket]
133
- end
134
- end
135
- end
136
-
137
- def test_succeed_despite_bad_puzzles
138
- Dir.mktmpdir 'test' do |dir|
139
- file = File.join(dir, 'a.txt')
140
- File.write(
141
- file,
142
- "
143
- * \x40todo #44 this is a correctly formatted puzzle,
144
- * with a second line without a leading space
145
- Another badly formatted puzzle
146
- * \x40todo this bad puzzle misses ticket name/number
147
- Something else
148
- * \x40todo #123 This puzzle is correctly formatted
149
- "
150
- )
151
- PDD.opts = { 'skip-errors' => true }
152
- stub_source_find_github_user(file, 'hey') do |source|
153
- list = source.puzzles
154
- PDD.opts = nil
155
- assert_equal 2, list.size
156
- puzzle = list.first
157
- assert_equal '2-3', puzzle.props[:lines]
158
- assert_equal 'this is a correctly formatted puzzle, with a second ' \
159
- 'line without a leading space', puzzle.props[:body]
160
- assert_equal '44', puzzle.props[:ticket]
161
- end
162
- end
163
- end
164
-
165
- def test_succeed_utf8_encoded_body
166
- Dir.mktmpdir 'test' do |dir|
167
- file = File.join(dir, 'a.txt')
168
- File.write(
169
- file,
170
- "
171
- * \x40todo #44 Привет, мир, мне кофе
172
- * вторая линия
173
- "
174
- )
175
- list = PDD::VerboseSource.new(file, PDD::Source.new(file, 'hey')).puzzles
176
- assert_equal 1, list.size
177
- puzzle = list.first
178
- assert_equal '2-3', puzzle.props[:lines]
179
- assert_equal 'Привет, мир, мне кофе вторая линия', puzzle.props[:body]
180
- assert_equal '44', puzzle.props[:ticket]
181
- end
182
- end
183
-
184
- def test_failing_on_incomplete_puzzle
185
- Dir.mktmpdir 't5' do |dir|
186
- file = File.join(dir, 'ff.txt')
187
- File.write(
188
- file,
189
- "
190
- * \x40todo this puzzle misses ticket name/number
191
- "
192
- )
193
- error = assert_raises PDD::Error do
194
- stub_source_find_github_user(file, 'ff', &:puzzles)
195
- end
196
- refute_nil error.to_s.index("\x40todo is not followed by")
197
- end
198
- end
199
-
200
- def test_failing_on_broken_unicode
201
- Dir.mktmpdir 'test' do |dir|
202
- file = File.join(dir, 'xx.txt')
203
- File.write(file, " * \\x40todo #44 this is a broken unicode: #{0x92.chr}")
204
- assert_raises PDD::Error do
205
- stub_source_find_github_user(file, 'xx', &:puzzles)
206
- end
207
- end
208
- end
209
-
210
- def test_failing_on_invalid_puzzle_without_hash_sign
211
- Dir.mktmpdir 'test' do |dir|
212
- file = File.join(dir, 'a.txt')
213
- File.write(
214
- file,
215
- "
216
- * \x40todo 44 this puzzle is not formatted correctly
217
- "
218
- )
219
- error = assert_raises PDD::Error do
220
- stub_source_find_github_user(file, 'hey', &:puzzles)
221
- end
222
- refute_nil error.message.index('is not followed by a puzzle marker')
223
- end
224
- end
225
-
226
- def test_failing_on_puzzle_without_leading_space
227
- Dir.mktmpdir 'test' do |dir|
228
- file = File.join(dir, 'hey.txt')
229
- File.write(
230
- file,
231
- "
232
- *\x40todo #999 this is an incorrectly formatted puzzle!
233
- "
234
- )
235
- error = assert_raises PDD::Error do
236
- stub_source_find_github_user(file, 'x', &:puzzles)
237
- end
238
- refute_nil error.message.index("\x40todo must have a leading space")
239
- end
240
- end
241
-
242
- def test_failing_on_puzzle_with_space_after_dash
243
- Dir.mktmpdir 'test' do |dir|
244
- file = File.join(dir, 'hey-you.txt')
245
- File.write(
246
- file,
247
- "
248
- * \x40todo # 123 This puzzle has an unnecessary space before the dash
249
- "
250
- )
251
- error = assert_raises PDD::Error do
252
- stub_source_find_github_user(file, 'x', &:puzzles)
253
- end
254
- refute_nil error.message.index('an unexpected space')
255
- end
256
- end
257
-
258
- def test_reads_git_author
259
- skip if Gem.win_platform?
260
- Dir.mktmpdir 'test' do |dir|
261
- raise unless system("
262
- set -e
263
- cd '#{dir}'
264
- git init --quiet .
265
- git config user.email test@teamed.io
266
- git config user.name test_unknown
267
- echo '\x40todo #1 this is the puzzle' > a.txt
268
- git add a.txt
269
- git commit --no-verify --quiet -am 'first version'
270
- ")
271
-
272
- stub_source_find_github_user(File.join(dir, 'a.txt')) do |source|
273
- list = source.puzzles
274
- assert_equal 1, list.size
275
- puzzle = list.first
276
- assert_equal '1-de87adc8', puzzle.props[:id]
277
- assert_equal '1-1', puzzle.props[:lines]
278
- assert_equal 'this is the puzzle', puzzle.props[:body]
279
- assert_equal 'test_unknown', puzzle.props[:author]
280
- assert_equal 'test@teamed.io', puzzle.props[:email]
281
- assert_match(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z/,
282
- puzzle.props[:time])
283
- end
284
- end
285
- end
286
-
287
- def test_skips_invalid_git_mail
288
- skip if Gem.win_platform?
289
- Dir.mktmpdir 'test' do |dir|
290
- raise unless system("
291
- set -e
292
- cd '#{dir}'
293
- git init --quiet .
294
- git config user.email invalid-email
295
- git config user.name test
296
- echo '\x40todo #1 this is the puzzle' > a.txt
297
- git add a.txt
298
- git commit --no-verify --quiet -am 'first version'
299
- ")
300
-
301
- stub_source_find_github_user(File.join(dir, 'a.txt')) do |source|
302
- list = source.puzzles
303
- assert_equal 1, list.size
304
- puzzle = list.first
305
- assert_equal '1-de87adc8', puzzle.props[:id]
306
- assert_equal '1-1', puzzle.props[:lines]
307
- assert_equal 'this is the puzzle', puzzle.props[:body]
308
- assert_equal 'test', puzzle.props[:author]
309
- assert_nil puzzle.props[:email]
310
- assert_match(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z/,
311
- puzzle.props[:time])
312
- end
313
- end
314
- end
315
-
316
- def test_uses_github_login
317
- skip if Gem.win_platform?
318
- Dir.mktmpdir 'test' do |dir|
319
- raise unless system("
320
- cd '#{dir}'
321
- git init --quiet .
322
- git config user.email yegor256@gmail.com
323
- git config user.name test
324
- echo '\x40todo #1 this is the puzzle' > a.txt
325
- git add a.txt
326
- git commit --no-verify --quiet -am 'first version'
327
- ")
328
-
329
- stub_source_find_github_user(File.join(dir, 'a.txt')) do |source|
330
- list = source.puzzles
331
- assert_equal 1, list.size
332
- puzzle = list.first
333
- assert_equal '@yegor256', puzzle.props[:author]
334
- end
335
- end
336
- end
337
-
338
- def test_skips_uncommitted_changes
339
- skip if Gem.win_platform?
340
- Dir.mktmpdir 'test' do |dir|
341
- raise unless system("
342
- cd '#{dir}'
343
- git init --quiet .
344
- git config user.email yegor256@gmail.com
345
- git config user.name test
346
- echo 'hi' > a.txt
347
- git add a.txt
348
- git commit --no-verify --quiet -am 'first version'
349
- echo '\x40todo #1 this is a puzzle uncommitted' > a.txt
350
- ")
351
-
352
- stub_source_find_github_user(File.join(dir, 'a.txt')) do |source|
353
- list = source.puzzles
354
- assert_equal 1, list.size
355
- puzzle = list.first
356
- assert_nil puzzle.props[:email]
357
- assert_equal 'Not Committed Yet', puzzle.props[:author]
358
- end
359
- end
360
- end
361
-
362
- def test_skips_thymeleaf_close_tag
363
- Dir.mktmpdir 'test' do |dir|
364
- file = File.join(dir, 'a.txt')
365
- File.write(
366
- file,
367
- '<!--/* @todo #123 puzzle info */-->'
368
- )
369
- stub_source_find_github_user(file, 'hey') do |source|
370
- list = source.puzzles
371
- assert_equal 1, list.size
372
- puzzle = list.first
373
- assert_equal '1-1', puzzle.props[:lines]
374
- assert_equal 'puzzle info', puzzle.props[:body]
375
- assert_equal '123', puzzle.props[:ticket]
376
- end
377
- end
378
- end
379
- end