minitest 5.25.5 → 6.0.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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +2 -4
  3. data/History.rdoc +116 -0
  4. data/Manifest.txt +13 -4
  5. data/README.rdoc +18 -98
  6. data/Rakefile +7 -2
  7. data/bin/minitest +5 -0
  8. data/design_rationale.rb +21 -19
  9. data/lib/hoe/minitest.rb +2 -1
  10. data/lib/minitest/assertions.rb +37 -74
  11. data/lib/minitest/autorun.rb +3 -4
  12. data/lib/minitest/benchmark.rb +2 -2
  13. data/lib/minitest/bisect.rb +306 -0
  14. data/lib/minitest/complete.rb +56 -0
  15. data/lib/minitest/find_minimal_combination.rb +127 -0
  16. data/lib/minitest/hell.rb +1 -1
  17. data/lib/minitest/manual_plugins.rb +4 -16
  18. data/lib/minitest/parallel.rb +5 -3
  19. data/lib/minitest/path_expander.rb +418 -0
  20. data/lib/minitest/pride.rb +2 -2
  21. data/lib/minitest/pride_plugin.rb +1 -1
  22. data/lib/minitest/server.rb +45 -0
  23. data/lib/minitest/server_plugin.rb +84 -0
  24. data/lib/minitest/spec.rb +11 -37
  25. data/lib/minitest/sprint.rb +104 -0
  26. data/lib/minitest/sprint_plugin.rb +39 -0
  27. data/lib/minitest/test.rb +8 -13
  28. data/lib/minitest/test_task.rb +32 -17
  29. data/lib/minitest.rb +94 -107
  30. data/test/minitest/metametameta.rb +1 -1
  31. data/test/minitest/test_bisect.rb +235 -0
  32. data/test/minitest/test_find_minimal_combination.rb +138 -0
  33. data/test/minitest/test_minitest_assertions.rb +51 -108
  34. data/test/minitest/test_minitest_reporter.rb +6 -5
  35. data/test/minitest/test_minitest_spec.rb +60 -128
  36. data/test/minitest/test_minitest_test.rb +22 -101
  37. data/test/minitest/test_path_expander.rb +229 -0
  38. data/test/minitest/test_server.rb +149 -0
  39. data.tar.gz.sig +0 -0
  40. metadata +54 -21
  41. metadata.gz.sig +0 -0
  42. data/.autotest +0 -34
  43. data/lib/minitest/mock.rb +0 -347
  44. data/lib/minitest/unit.rb +0 -42
  45. data/test/minitest/test_minitest_mock.rb +0 -1218
@@ -62,11 +62,11 @@ module Minitest
62
62
  return "Expected: #{mu_pp exp}\n Actual: #{mu_pp act}" unless
63
63
  expect
64
64
 
65
- Tempfile.open "expect" do |a|
65
+ Tempfile.create "expect" do |a|
66
66
  a.puts expect
67
67
  a.flush
68
68
 
69
- Tempfile.open "butwas" do |b|
69
+ Tempfile.create "butwas" do |b|
70
70
  b.puts butwas
71
71
  b.flush
72
72
 
@@ -186,9 +186,8 @@ module Minitest
186
186
  # Fails unless +obj+ is empty.
187
187
 
188
188
  def assert_empty obj, msg = nil
189
- msg = message(msg) { "Expected #{mu_pp obj} to be empty" }
190
- assert_respond_to obj, :empty?
191
- assert obj.empty?, msg
189
+ msg = message(msg) { "Expected #{mu_pp(obj)} to be empty" }
190
+ assert_predicate obj, :empty?, msg
192
191
  end
193
192
 
194
193
  def _where # :nodoc:
@@ -196,8 +195,6 @@ module Minitest
196
195
  .split(":in ", 2).first # clean up noise
197
196
  end
198
197
 
199
- E = "" # :nodoc:
200
-
201
198
  ##
202
199
  # Fails unless <tt>exp == act</tt> printing the difference between
203
200
  # the two, if possible.
@@ -212,18 +209,11 @@ module Minitest
212
209
  # See also: Minitest::Assertions.diff
213
210
 
214
211
  def assert_equal exp, act, msg = nil
215
- msg = message(msg, E) { diff exp, act }
216
- result = assert exp == act, msg
212
+ msg = message(msg, nil) { diff exp, act }
217
213
 
218
- if nil == exp then
219
- if Minitest::VERSION >= "6" then
220
- refute_nil exp, "Use assert_nil if expecting nil."
221
- else
222
- warn "DEPRECATED: Use assert_nil if expecting nil from #{_where}. This will fail in Minitest 6."
223
- end
224
- end
214
+ refute_nil exp, message { "Use assert_nil if expecting nil" } if exp.nil? # don't count
225
215
 
226
- result
216
+ assert exp == act, msg
227
217
  end
228
218
 
229
219
  ##
@@ -255,8 +245,7 @@ module Minitest
255
245
  msg = message(msg) {
256
246
  "Expected #{mu_pp collection} to include #{mu_pp obj}"
257
247
  }
258
- assert_respond_to collection, :include?
259
- assert collection.include?(obj), msg
248
+ assert_operator collection, :include?, obj, msg
260
249
  end
261
250
 
262
251
  ##
@@ -308,6 +297,7 @@ module Minitest
308
297
 
309
298
  def assert_operator o1, op, o2 = UNDEFINED, msg = nil
310
299
  return assert_predicate o1, op, msg if UNDEFINED == o2
300
+ assert_respond_to o1, op
311
301
  msg = message(msg) { "Expected #{mu_pp o1} to be #{op} #{mu_pp o2}" }
312
302
  assert o1.__send__(op, o2), msg
313
303
  end
@@ -368,15 +358,12 @@ module Minitest
368
358
  # error.
369
359
 
370
360
  def assert_pattern
371
- raise NotImplementedError, "only available in Ruby 3.0+" unless RUBY_VERSION >= "3.0"
372
361
  flunk "assert_pattern requires a block to capture errors." unless block_given?
373
362
 
374
- begin # TODO: remove after ruby 2.6 dropped
375
- yield
376
- pass
377
- rescue NoMatchingPatternError => e
378
- flunk e.message
379
- end
363
+ yield
364
+ pass
365
+ rescue NoMatchingPatternError => e
366
+ flunk e.message
380
367
  end
381
368
 
382
369
  ##
@@ -389,6 +376,7 @@ module Minitest
389
376
  # str.must_be :empty?
390
377
 
391
378
  def assert_predicate o1, op, msg = nil
379
+ assert_respond_to o1, op, include_all:true
392
380
  msg = message(msg) { "Expected #{mu_pp o1} to be #{op}" }
393
381
  assert o1.__send__(op), msg
394
382
  end
@@ -448,9 +436,7 @@ module Minitest
448
436
  # include_all defaults to false to match Object#respond_to?
449
437
 
450
438
  def assert_respond_to obj, meth, msg = nil, include_all: false
451
- msg = message(msg) {
452
- "Expected #{mu_pp obj} (#{obj.class}) to respond to ##{meth}"
453
- }
439
+ msg = message(msg) { "Expected #{mu_pp obj} (#{obj.class}) to respond to ##{meth}" }
454
440
  assert obj.respond_to?(meth, include_all), msg
455
441
  end
456
442
 
@@ -465,21 +451,6 @@ module Minitest
465
451
  assert exp.equal?(act), msg
466
452
  end
467
453
 
468
- ##
469
- # +send_ary+ is a receiver, message and arguments.
470
- #
471
- # Fails unless the call returns a true value
472
-
473
- def assert_send send_ary, m = nil
474
- warn "DEPRECATED: assert_send. From #{_where}"
475
-
476
- recv, msg, *args = send_ary
477
- m = message(m) {
478
- "Expected #{mu_pp recv}.#{msg}(*#{mu_pp args}) to return true"
479
- }
480
- assert recv.__send__(msg, *args), m
481
- end
482
-
483
454
  ##
484
455
  # Fails if the block outputs anything to stderr or stdout.
485
456
  #
@@ -500,14 +471,9 @@ module Minitest
500
471
  value = catch sym do
501
472
  begin
502
473
  yield
503
- rescue ThreadError => e # wtf?!? 1.8 + threads == suck
504
- default += ", not :#{e.message[/uncaught throw \`(\w+?)\'/, 1]}"
505
- rescue ArgumentError => e # 1.9 exception
474
+ rescue ArgumentError => e # 1.9+ exception
506
475
  raise e unless e.message.include? "uncaught throw"
507
476
  default += ", not #{e.message.split(/ /).last}"
508
- rescue NameError => e # 1.8 exception
509
- raise e unless e.name == sym
510
- default += ", not #{e.name.inspect}"
511
477
  end
512
478
  caught = false
513
479
  end
@@ -629,13 +595,16 @@ module Minitest
629
595
  end
630
596
 
631
597
  ##
632
- # Returns a proc that will output +msg+ along with the default message.
598
+ # Returns a proc that delays generation of an output message. If
599
+ # +msg+ is a proc (eg, from another +message+ call) return +msg+
600
+ # as-is. Otherwise, return a proc that will output +msg+ along
601
+ # with the value of the result of the block passed to +message+.
633
602
 
634
- def message msg = nil, ending = nil, &default
603
+ def message msg = nil, ending = ".", &default
604
+ return msg if Proc === msg
635
605
  proc {
636
- msg = msg.call.chomp(".") if Proc === msg
637
606
  custom_message = "#{msg}.\n" unless msg.nil? or msg.to_s.empty?
638
- "#{custom_message}#{default.call}#{ending || "."}"
607
+ "#{custom_message}#{default.call}#{ending}"
639
608
  }
640
609
  end
641
610
 
@@ -659,8 +628,7 @@ module Minitest
659
628
 
660
629
  def refute_empty obj, msg = nil
661
630
  msg = message(msg) { "Expected #{mu_pp obj} to not be empty" }
662
- assert_respond_to obj, :empty?
663
- refute obj.empty?, msg
631
+ refute_predicate obj, :empty?, msg
664
632
  end
665
633
 
666
634
  ##
@@ -692,19 +660,16 @@ module Minitest
692
660
  # For comparing Floats. Fails if +exp+ and +act+ have a relative error
693
661
  # less than +epsilon+.
694
662
 
695
- def refute_in_epsilon a, b, epsilon = 0.001, msg = nil
696
- refute_in_delta a, b, a * epsilon, msg
663
+ def refute_in_epsilon exp, act, epsilon = 0.001, msg = nil
664
+ refute_in_delta exp, act, [exp.abs, act.abs].min * epsilon, msg
697
665
  end
698
666
 
699
667
  ##
700
- # Fails if +collection+ includes +obj+.
668
+ # Fails if +obj+ includes +sub+.
701
669
 
702
- def refute_includes collection, obj, msg = nil
703
- msg = message(msg) {
704
- "Expected #{mu_pp collection} to not include #{mu_pp obj}"
705
- }
706
- assert_respond_to collection, :include?
707
- refute collection.include?(obj), msg
670
+ def refute_includes obj, sub, msg = nil
671
+ msg = message(msg) { "Expected #{mu_pp obj} to not include #{mu_pp sub}" }
672
+ refute_operator obj, :include?, sub, msg
708
673
  end
709
674
 
710
675
  ##
@@ -730,9 +695,8 @@ module Minitest
730
695
 
731
696
  def refute_match matcher, obj, msg = nil
732
697
  msg = message(msg) { "Expected #{mu_pp matcher} to not match #{mu_pp obj}" }
733
- assert_respond_to matcher, :=~
734
698
  matcher = Regexp.new Regexp.escape matcher if String === matcher
735
- refute matcher =~ obj, msg
699
+ refute_operator matcher, :=~, obj, msg
736
700
  end
737
701
 
738
702
  ##
@@ -756,15 +720,12 @@ module Minitest
756
720
  # other exceptions will be raised as normal and generate a test error.
757
721
 
758
722
  def refute_pattern
759
- raise NotImplementedError, "only available in Ruby 3.0+" unless RUBY_VERSION >= "3.0"
760
723
  flunk "refute_pattern requires a block to capture errors." unless block_given?
761
724
 
762
- begin
763
- yield
764
- flunk "NoMatchingPatternError expected, but nothing was raised."
765
- rescue NoMatchingPatternError
766
- pass
767
- end
725
+ yield
726
+ flunk "NoMatchingPatternError expected, but nothing was raised."
727
+ rescue NoMatchingPatternError
728
+ pass
768
729
  end
769
730
 
770
731
  ##
@@ -775,6 +736,7 @@ module Minitest
775
736
 
776
737
  def refute_operator o1, op, o2 = UNDEFINED, msg = nil
777
738
  return refute_predicate o1, op, msg if UNDEFINED == o2
739
+ assert_respond_to o1, op
778
740
  msg = message(msg) { "Expected #{mu_pp o1} to not be #{op} #{mu_pp o2}" }
779
741
  refute o1.__send__(op, o2), msg
780
742
  end
@@ -797,6 +759,7 @@ module Minitest
797
759
  # str.wont_be :empty?
798
760
 
799
761
  def refute_predicate o1, op, msg = nil
762
+ assert_respond_to o1, op
800
763
  msg = message(msg) { "Expected #{mu_pp o1} to not be #{op}" }
801
764
  refute o1.__send__(op), msg
802
765
  end
@@ -1,6 +1,5 @@
1
- require "minitest"
2
- require "minitest/spec"
3
- require "minitest/mock"
4
- require "minitest/hell" if ENV["MT_HELL"]
1
+ require_relative "../minitest"
2
+ require_relative "spec"
3
+ require_relative "hell" if ENV["MT_HELL"]
5
4
 
6
5
  Minitest.autorun
@@ -1,5 +1,5 @@
1
- require "minitest/test"
2
- require "minitest/spec"
1
+ require_relative "test"
2
+ require_relative "spec"
3
3
 
4
4
  module Minitest
5
5
  ##
@@ -0,0 +1,306 @@
1
+ require_relative "find_minimal_combination"
2
+ require_relative "server"
3
+ require "shellwords"
4
+ require "rbconfig"
5
+ require_relative "path_expander" # this is gonna break some shit?
6
+
7
+ module Minitest; end # :nodoc:
8
+
9
+ ##
10
+ # Minitest::Bisect helps you isolate and debug random test failures.
11
+
12
+ class Minitest::Bisect
13
+ VERSION = "1.8.0" # :nodoc:
14
+
15
+ class PathExpander < Minitest::VendoredPathExpander # :nodoc:
16
+ TEST_GLOB = "**/{test_*,*_test,spec_*,*_spec}.rb" # :nodoc:
17
+
18
+ attr_accessor :rb_flags
19
+
20
+ def initialize args = ARGV # :nodoc:
21
+ super args, TEST_GLOB, "test"
22
+ self.rb_flags = %w[-Itest:lib]
23
+ end
24
+
25
+ ##
26
+ # Overrides PathExpander#process_flags to filter out ruby flags
27
+ # from minitest flags. Only supports -I<paths>, -d, and -w for
28
+ # ruby.
29
+
30
+ def process_flags flags
31
+ flags.reject { |flag| # all hits are truthy, so this works out well
32
+ case flag
33
+ when /^-I(.*)/ then
34
+ rb_flags << flag
35
+ when /^-d/ then
36
+ rb_flags << flag
37
+ when /^-w/ then
38
+ rb_flags << flag
39
+ else
40
+ false
41
+ end
42
+ }
43
+ end
44
+ end
45
+
46
+ mtbv = ENV["MTB_VERBOSE"].to_i
47
+ SHH = case # :nodoc:
48
+ when mtbv == 1 then " > /dev/null"
49
+ when mtbv >= 2 then nil
50
+ else " > /dev/null 2>&1"
51
+ end
52
+
53
+ # Borrowed from rake
54
+ RUBY = ENV['RUBY'] ||
55
+ File.join(RbConfig::CONFIG['bindir'],
56
+ RbConfig::CONFIG['ruby_install_name'] +
57
+ RbConfig::CONFIG['EXEEXT']).sub(/.*\s.*/m, '"\&"')
58
+
59
+ ##
60
+ # True if this run has seen a failure.
61
+
62
+ attr_accessor :tainted
63
+ alias :tainted? :tainted
64
+
65
+ ##
66
+ # Failures seen in this run. Shape:
67
+ #
68
+ # {"file.rb"=>{"Class"=>["test_method1", "test_method2"] ...} ...}
69
+
70
+ attr_accessor :failures
71
+
72
+ ##
73
+ # An array of tests seen so far. NOT cleared by #reset.
74
+
75
+ attr_accessor :culprits
76
+
77
+ attr_accessor :seen_bad # :nodoc:
78
+
79
+ ##
80
+ # Top-level runner. Instantiate and call +run+, handling exceptions.
81
+
82
+ def self.run files
83
+ new.run files
84
+ rescue => e
85
+ warn e.message
86
+ warn "Try running with MTB_VERBOSE=2 to verify."
87
+ exit 1
88
+ end
89
+
90
+ ##
91
+ # Instantiate a new Bisect.
92
+
93
+ def initialize
94
+ self.culprits = []
95
+ self.failures = Hash.new { |h, k| h[k] = Hash.new { |h2, k2| h2[k2] = [] } }
96
+ end
97
+
98
+ ##
99
+ # Reset per-bisect-run variables.
100
+
101
+ def reset
102
+ self.seen_bad = false
103
+ self.tainted = false
104
+ failures.clear
105
+ # not clearing culprits on purpose
106
+ end
107
+
108
+ ##
109
+ # Instance-level runner. Handles Minitest::Server, argument
110
+ # processing, and invoking +bisect_methods+.
111
+
112
+ def run args
113
+ Minitest::Server.run self
114
+
115
+ cmd = nil
116
+
117
+ mt_flags = args.dup
118
+ expander = Minitest::Bisect::PathExpander.new mt_flags
119
+
120
+ files = expander.process.to_a
121
+ rb_flags = expander.rb_flags
122
+ mt_flags += ["--server", $$.to_s]
123
+
124
+ cmd = bisect_methods files, rb_flags, mt_flags
125
+
126
+ puts "Final reproduction:"
127
+ puts
128
+
129
+ system cmd.sub(/--server \d+/, "")
130
+ ensure
131
+ Minitest::Server.stop
132
+ end
133
+
134
+ ##
135
+ # Normal: find "what is the minimal combination of tests to run to
136
+ # make X fail?"
137
+ #
138
+ # Run with: minitest_bisect ... --seed=N
139
+ #
140
+ # 1. Verify the failure running normally with the seed.
141
+ # 2. If no failure, punt.
142
+ # 3. If no passing tests before failure, punt. (No culprits == no debug)
143
+ # 4. Verify the failure doesn't fail in isolation.
144
+ # 5. If it still fails by itself, warn that it might not be an ordering
145
+ # issue.
146
+ # 6. Cull all tests after the failure, they're not involved.
147
+ # 7. Bisect the culprits + bad until you find a minimal combo that fails.
148
+ # 8. Display minimal combo by running one last time.
149
+ #
150
+ # Inverted: find "what is the minimal combination of tests to run to
151
+ # make this test pass?"
152
+ #
153
+ # Run with: minitest_bisect ... --seed=N -n="/failing_test_name_regexp/"
154
+ #
155
+ # 1. Verify the failure by running normally w/ the seed and -n=/.../
156
+ # 2. If no failure, punt.
157
+ # 3. Verify the passing case by running everything.
158
+ # 4. If failure, punt. This is not a false positive.
159
+ # 5. Cull all tests after the bad test from #1, they're not involved.
160
+ # 6. Bisect the culprits + bad until you find a minimal combo that passes.
161
+ # 7. Display minimal combo by running one last time.
162
+
163
+ def bisect_methods files, rb_flags, mt_flags
164
+ bad_names, mt_flags = mt_flags.partition { |s| s =~ /^(?:-n|--name)/ }
165
+ normal = bad_names.empty?
166
+ inverted = !normal
167
+
168
+ if inverted then
169
+ time_it "reproducing w/ scoped failure (inverted run!)...", build_methods_cmd(build_files_cmd(files, rb_flags, mt_flags + bad_names))
170
+ raise "No failures. Probably not a false positive. Aborting." if failures.empty?
171
+ bad = map_failures
172
+ end
173
+
174
+ cmd = build_files_cmd(files, rb_flags, mt_flags)
175
+
176
+ msg = normal ? "reproducing..." : "reproducing false positive..."
177
+ time_it msg, build_methods_cmd(cmd)
178
+
179
+ if normal then
180
+ raise "Reproduction run passed? Aborting." unless tainted?
181
+ raise "Verification failed. No culprits? Aborting." if culprits.empty? && seen_bad
182
+ else
183
+ raise "Reproduction failed? Not false positive. Aborting." if tainted?
184
+ raise "Verification failed. No culprits? Aborting." if culprits.empty? || seen_bad
185
+ end
186
+
187
+ if normal then
188
+ bad = map_failures
189
+
190
+ time_it "verifying...", build_methods_cmd(cmd, [], bad)
191
+
192
+ new_bad = map_failures
193
+
194
+ if bad == new_bad then
195
+ warn "Tests fail by themselves. This may not be an ordering issue."
196
+ end
197
+ end
198
+
199
+ idx = culprits.index bad.first
200
+ self.culprits = culprits.take idx+1 if idx # cull tests after bad
201
+
202
+ # culprits populated by initial reproduction via minitest/server
203
+ found, count = culprits.find_minimal_combination_and_count do |test|
204
+ prompt = "# of culprit methods: #{test.size}"
205
+
206
+ time_it prompt, build_methods_cmd(cmd, test, bad)
207
+
208
+ normal == tainted? # either normal and failed, or inverse and passed
209
+ end
210
+
211
+ puts
212
+ puts "Minimal methods found in #{count} steps:"
213
+ puts
214
+ puts "Culprit methods: %p" % [found + bad]
215
+ puts
216
+ cmd = build_methods_cmd cmd, found, bad
217
+ puts cmd.sub(/--server \d+/, "")
218
+ puts
219
+ cmd
220
+ end
221
+
222
+ def time_it prompt, cmd # :nodoc:
223
+ print prompt
224
+ t0 = Time.now
225
+ system "#{cmd} #{SHH}"
226
+ puts " in %.2f sec" % (Time.now - t0)
227
+ end
228
+
229
+ def map_failures # :nodoc:
230
+ # from: {"file.rb"=>{"Class"=>["test_method1", "test_method2"]}}
231
+ # to: ["Class#test_method1", "Class#test_method2"]
232
+ failures.values.map { |h|
233
+ h.map { |k,vs| vs.map { |v| "#{k}##{v}" } }
234
+ }.flatten.sort
235
+ end
236
+
237
+ def build_files_cmd culprits, rb, mt # :nodoc:
238
+ tests = culprits.flatten.compact.map { |f| %(require "./#{f}") }.join " ; "
239
+
240
+ %(#{RUBY} #{rb.shelljoin} -e '#{tests}' -- #{mt.map(&:to_s).shelljoin})
241
+ end
242
+
243
+ def build_methods_cmd cmd, culprits = [], bad = nil # :nodoc:
244
+ reset
245
+
246
+ if bad then
247
+ re = build_re culprits + bad
248
+
249
+ cmd += " -n \"#{re}\"" if bad
250
+ end
251
+
252
+ if ENV["MTB_VERBOSE"].to_i >= 1 then
253
+ puts
254
+ puts cmd
255
+ puts
256
+ end
257
+
258
+ cmd
259
+ end
260
+
261
+ def build_re bad # :nodoc:
262
+ re = []
263
+
264
+ # bad by class, you perv
265
+ bbc = bad.map { |s| s.split(/#/, 2) }.group_by(&:first)
266
+
267
+ bbc.each do |klass, methods|
268
+ methods = methods.map(&:last).flatten.uniq.map { |method|
269
+ re_escape method
270
+ }
271
+
272
+ methods = methods.join "|"
273
+ re << /#{re_escape klass}#(?:#{methods})/.to_s[7..-2] # (?-mix:...)
274
+ end
275
+
276
+ re = re.join("|").to_s.gsub(/-mix/, "")
277
+
278
+ "/^(?:#{re})$/"
279
+ end
280
+
281
+ def re_escape str # :nodoc:
282
+ str.gsub(/([`'"!?&\[\]\(\)\{\}\|\+])/, '\\\\\1')
283
+ end
284
+
285
+ ############################################################
286
+ # Server Methods:
287
+
288
+ def minitest_start # :nodoc:
289
+ self.failures.clear
290
+ end
291
+
292
+ def minitest_result file, klass, method, fails, assertions, time # :nodoc:
293
+ fails.reject! { |fail| Minitest::Skip === fail }
294
+
295
+ if fails.empty? then
296
+ culprits << "#{klass}##{method}" unless seen_bad # UGH
297
+ else
298
+ self.seen_bad = true
299
+ end
300
+
301
+ return if fails.empty?
302
+
303
+ self.tainted = true
304
+ self.failures[file][klass] << method
305
+ end
306
+ end
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env -S ruby
2
+
3
+ # :stopdoc:
4
+
5
+ require "optparse"
6
+ require "shellwords"
7
+
8
+ # complete -o bashdefault -f -C 'ruby lib/minitest/complete.rb' minitest
9
+ # using eg:
10
+ # COMP_LINE="blah test/test_file.rb -n test_pattern"
11
+ # or test directly with:
12
+ # ./lib/minitest/complete.rb test/test_file.rb -n test_pattern
13
+
14
+ argv = Shellwords.split ENV["COMP_LINE"] || ARGV.join(" ")
15
+ comp_re = nil
16
+
17
+ begin
18
+ OptionParser.new do |opts|
19
+ # part of my unofficial embedded gem "makeoptparseworkwell"
20
+ def opts.topdict(name) = (name.length > 1 ? top.long : top.short)
21
+ def opts.alias(from, to) = (dict = topdict(from) ; dict[to] = dict[from])
22
+
23
+ opts.on "-n", "--name [METHOD]", "minitest option" do |m|
24
+ comp_re = Regexp.new m
25
+ end
26
+
27
+ opts.alias "name", "include"
28
+ opts.alias "name", "exclude"
29
+ opts.alias "n", "i"
30
+ opts.alias "n", "e"
31
+ opts.alias "n", "x"
32
+ end.parse! argv
33
+ rescue
34
+ retry # ignore options passed to Ruby
35
+ end
36
+
37
+ path = argv.find_all { |f| File.file? f }.last
38
+
39
+ exit unless comp_re && path
40
+
41
+ require "prism"
42
+
43
+ names, queue = [], [Prism.parse_file(path).value]
44
+
45
+ while node = queue.shift do
46
+ if node.type == :def_node then
47
+ name = node.name
48
+ names << name if name =~ comp_re
49
+ else
50
+ queue.concat node.compact_child_nodes # no need to process def body
51
+ end
52
+ end
53
+
54
+ puts names.sort
55
+
56
+ # :startdoc: