minitest 5.18.0 → 5.22.3

Sign up to get free protection for your applications and to get access to all the features.
data/lib/minitest.rb CHANGED
@@ -1,15 +1,15 @@
1
1
  require "optparse"
2
- require "thread"
3
- require "mutex_m"
4
- require "minitest/parallel"
5
2
  require "stringio"
6
3
  require "etc"
7
4
 
5
+ require_relative "minitest/parallel"
6
+ require_relative "minitest/compress"
7
+
8
8
  ##
9
9
  # :include: README.rdoc
10
10
 
11
11
  module Minitest
12
- VERSION = "5.18.0" # :nodoc:
12
+ VERSION = "5.22.2" # :nodoc:
13
13
 
14
14
  @@installed_at_exit ||= false
15
15
  @@after_run = []
@@ -32,7 +32,7 @@ module Minitest
32
32
 
33
33
  cattr_accessor :parallel_executor
34
34
 
35
- warn "DEPRECATED: use MT_CPU instead of N for parallel test runs" if ENV["N"]
35
+ warn "DEPRECATED: use MT_CPU instead of N for parallel test runs" if ENV["N"] && ENV["N"].to_i > 0
36
36
  n_threads = (ENV["MT_CPU"] || ENV["N"] || Etc.nprocessors).to_i
37
37
 
38
38
  self.parallel_executor = Parallel::Executor.new n_threads
@@ -60,6 +60,9 @@ module Minitest
60
60
  cattr_accessor :info_signal
61
61
  self.info_signal = "INFO"
62
62
 
63
+ cattr_accessor :allow_fork
64
+ self.allow_fork = false
65
+
63
66
  ##
64
67
  # Registers Minitest to run at process exit
65
68
 
@@ -75,7 +78,7 @@ module Minitest
75
78
 
76
79
  pid = Process.pid
77
80
  at_exit {
78
- next if Process.pid != pid
81
+ next if !Minitest.allow_fork && Process.pid != pid
79
82
  @@after_run.reverse_each(&:call)
80
83
  exit exit_code || false
81
84
  }
@@ -131,7 +134,7 @@ module Minitest
131
134
  # Minitest.run(args)
132
135
  # Minitest.__run(reporter, options)
133
136
  # Runnable.runnables.each
134
- # runnable.run(reporter, options)
137
+ # runnable_klass.run(reporter, options)
135
138
  # self.runnable_methods.each
136
139
  # self.run_one_method(self, runnable_method, reporter)
137
140
  # Minitest.run_one_method(klass, runnable_method)
@@ -147,7 +150,7 @@ module Minitest
147
150
 
148
151
  reporter = CompositeReporter.new
149
152
  reporter << SummaryReporter.new(options[:io], options)
150
- reporter << ProgressReporter.new(options[:io], options)
153
+ reporter << ProgressReporter.new(options[:io], options) unless options[:quiet]
151
154
 
152
155
  self.reporter = reporter # this makes it available to plugins
153
156
  self.init_plugins options
@@ -161,11 +164,32 @@ module Minitest
161
164
  warn "Interrupted. Exiting..."
162
165
  end
163
166
  self.parallel_executor.shutdown
167
+
168
+ # might have been removed/replaced during init_plugins:
169
+ summary = reporter.reporters.grep(SummaryReporter).first
170
+ return empty_run! options if summary && summary.count == 0
171
+
164
172
  reporter.report
165
173
 
166
174
  reporter.passed?
167
175
  end
168
176
 
177
+ def self.empty_run! options # :nodoc:
178
+ filter = options[:filter]
179
+ return true unless filter # no filter, but nothing ran == success
180
+
181
+ warn "Nothing ran for filter: %s" % [filter]
182
+
183
+ require "did_you_mean" # soft dependency, punt if it doesn't load
184
+
185
+ ms = Runnable.runnables.flat_map(&:runnable_methods)
186
+ cs = DidYouMean::SpellChecker.new(dictionary: ms).correct filter
187
+
188
+ warn DidYouMean::Formatter.message_for cs unless cs.empty?
189
+ rescue LoadError
190
+ # do nothing
191
+ end
192
+
169
193
  ##
170
194
  # Internal run method. Responsible for telling all Runnable
171
195
  # sub-classes to run.
@@ -209,6 +233,10 @@ module Minitest
209
233
  options[:verbose] = true
210
234
  end
211
235
 
236
+ opts.on "-q", "--quiet", "Quiet. Show no progress processing files." do
237
+ options[:quiet] = true
238
+ end
239
+
212
240
  opts.on "--show-skips", "Show skipped at the end of run." do
213
241
  options[:show_skips] = true
214
242
  end
@@ -331,19 +359,15 @@ module Minitest
331
359
  # reporter to record.
332
360
 
333
361
  def self.run reporter, options = {}
334
- filter = options[:filter] || "/./"
335
- filter = Regexp.new $1 if filter.is_a?(String) && filter =~ %r%/(.*)/%
362
+ pos = options[:filter]
363
+ neg = options[:exclude]
336
364
 
337
- filtered_methods = self.runnable_methods.find_all { |m|
338
- filter === m || filter === "#{self}##{m}"
339
- }
340
-
341
- exclude = options[:exclude]
342
- exclude = Regexp.new $1 if exclude =~ %r%/(.*)/%
365
+ pos = Regexp.new $1 if pos.is_a?(String) && pos =~ %r%/(.*)/%
366
+ neg = Regexp.new $1 if neg.is_a?(String) && neg =~ %r%/(.*)/%
343
367
 
344
- filtered_methods.delete_if { |m|
345
- exclude === m || exclude === "#{self}##{m}"
346
- }
368
+ filtered_methods = self.runnable_methods
369
+ .select { |m| !pos || pos === m || pos === "#{self}##{m}" }
370
+ .reject { |m| neg && (neg === m || neg === "#{self}##{m}") }
347
371
 
348
372
  return if filtered_methods.empty?
349
373
 
@@ -365,6 +389,14 @@ module Minitest
365
389
  reporter.record Minitest.run_one_method(klass, method_name)
366
390
  end
367
391
 
392
+ ##
393
+ # Defines the order to run tests (:random by default). Override
394
+ # this or use a convenience method to change it for your tests.
395
+
396
+ def self.test_order
397
+ :random
398
+ end
399
+
368
400
  def self.with_info_handler reporter, &block # :nodoc:
369
401
  handler = lambda do
370
402
  unless reporter.passed? then
@@ -432,6 +464,31 @@ module Minitest
432
464
  self.name = name
433
465
  self.failures = []
434
466
  self.assertions = 0
467
+ # lazy initializer for metadata
468
+ end
469
+
470
+ ##
471
+ # Metadata you attach to the test results that get sent to the reporter.
472
+ #
473
+ # Lazily initializes to a hash, to keep memory down.
474
+ #
475
+ # NOTE: this data *must* be plain (read: marshal-able) data!
476
+ # Hashes! Arrays! Strings!
477
+
478
+ def metadata
479
+ @metadata ||= {}
480
+ end
481
+
482
+ ##
483
+ # Sets metadata, mainly used for +Result.from+.
484
+
485
+ attr_writer :metadata
486
+
487
+ ##
488
+ # Returns true if metadata exists.
489
+
490
+ def metadata?
491
+ defined? @metadata
435
492
  end
436
493
 
437
494
  ##
@@ -483,12 +540,14 @@ module Minitest
483
540
  not self.failure
484
541
  end
485
542
 
543
+ BASE_DIR = "#{Dir.pwd}/" # :nodoc:
544
+
486
545
  ##
487
546
  # The location identifier of this test. Depends on a method
488
547
  # existing called class_name.
489
548
 
490
549
  def location
491
- loc = " [#{self.failure.location}]" unless passed? or error?
550
+ loc = " [#{self.failure.location.delete_prefix BASE_DIR}]" unless passed? or error?
492
551
  "#{self.class_name}##{self.name}#{loc}"
493
552
  end
494
553
 
@@ -552,6 +611,7 @@ module Minitest
552
611
  r.assertions = o.assertions
553
612
  r.failures = o.failures.dup
554
613
  r.time = o.time
614
+ r.metadata = o.metadata if o.metadata?
555
615
 
556
616
  r.source_location = o.method(o.name).source_location rescue ["unknown", -1]
557
617
 
@@ -576,7 +636,10 @@ module Minitest
576
636
  # you want. Go nuts.
577
637
 
578
638
  class AbstractReporter
579
- include Mutex_m
639
+
640
+ def initialize # :nodoc:
641
+ @mutex = Mutex.new
642
+ end
580
643
 
581
644
  ##
582
645
  # Starts reporting on the run.
@@ -612,6 +675,10 @@ module Minitest
612
675
  def passed?
613
676
  true
614
677
  end
678
+
679
+ def synchronize(&block) # :nodoc:
680
+ @mutex.synchronize(&block)
681
+ end
615
682
  end
616
683
 
617
684
  class Reporter < AbstractReporter # :nodoc:
@@ -785,7 +852,7 @@ module Minitest
785
852
  io.puts "# Running:"
786
853
  io.puts
787
854
 
788
- self.sync = io.respond_to? :"sync=" # stupid emacs
855
+ self.sync = io.respond_to? :"sync="
789
856
  self.old_sync, io.sync = io.sync, true if self.sync
790
857
  end
791
858
 
@@ -893,6 +960,8 @@ module Minitest
893
960
  # Represents run failures.
894
961
 
895
962
  class Assertion < Exception
963
+ RE = /in [`'](?:[^']+[#.])?(?:assert|refute|flunk|pass|fail|raise|must|wont)/ # :nodoc:
964
+
896
965
  def error # :nodoc:
897
966
  self
898
967
  end
@@ -901,12 +970,11 @@ module Minitest
901
970
  # Where was this run before an assertion was raised?
902
971
 
903
972
  def location
904
- last_before_assertion = ""
905
- self.backtrace.reverse_each do |s|
906
- break if s =~ /in .(assert|refute|flunk|pass|fail|raise|must|wont)/
907
- last_before_assertion = s
908
- end
909
- last_before_assertion.sub(/:in .*$/, "")
973
+ bt = Minitest.filter_backtrace self.backtrace
974
+ idx = bt.rindex { |s| s.match? RE } || -1 # fall back to first item
975
+ loc = bt[idx+1] || bt.last || "unknown:-1"
976
+
977
+ loc.sub(/:in .*$/, "")
910
978
  end
911
979
 
912
980
  def result_code # :nodoc:
@@ -931,11 +999,21 @@ module Minitest
931
999
  # Assertion wrapping an unexpected error that was raised during a run.
932
1000
 
933
1001
  class UnexpectedError < Assertion
1002
+ include Minitest::Compress
1003
+
934
1004
  # TODO: figure out how to use `cause` instead
935
1005
  attr_accessor :error # :nodoc:
936
1006
 
937
1007
  def initialize error # :nodoc:
938
1008
  super "Unexpected exception"
1009
+
1010
+ if SystemStackError === error then
1011
+ bt = error.backtrace
1012
+ new_bt = compress bt
1013
+ error = error.exception "#{bt.size} -> #{new_bt.size}"
1014
+ error.set_backtrace new_bt
1015
+ end
1016
+
939
1017
  self.error = error
940
1018
  end
941
1019
 
@@ -943,8 +1021,11 @@ module Minitest
943
1021
  self.error.backtrace
944
1022
  end
945
1023
 
1024
+ BASE_RE = %r%#{Dir.pwd}/% # :nodoc:
1025
+
946
1026
  def message # :nodoc:
947
- bt = Minitest.filter_backtrace(self.backtrace).join "\n "
1027
+ bt = Minitest.filter_backtrace(self.backtrace).join("\n ")
1028
+ .gsub(BASE_RE, "")
948
1029
  "#{self.error.class}: #{self.error.message}\n #{bt}"
949
1030
  end
950
1031
 
@@ -1028,6 +1109,12 @@ module Minitest
1028
1109
 
1029
1110
  MT_RE = %r%lib/minitest% #:nodoc:
1030
1111
 
1112
+ attr_accessor :regexp
1113
+
1114
+ def initialize regexp = MT_RE
1115
+ self.regexp = regexp
1116
+ end
1117
+
1031
1118
  ##
1032
1119
  # Filter +bt+ to something useful. Returns the whole thing if
1033
1120
  # $DEBUG (ruby) or $MT_DEBUG (env).
@@ -1037,9 +1124,9 @@ module Minitest
1037
1124
 
1038
1125
  return bt.dup if $DEBUG || ENV["MT_DEBUG"]
1039
1126
 
1040
- new_bt = bt.take_while { |line| line !~ MT_RE }
1041
- new_bt = bt.select { |line| line !~ MT_RE } if new_bt.empty?
1042
- new_bt = bt.dup if new_bt.empty?
1127
+ new_bt = bt.take_while { |line| line.to_s !~ regexp }
1128
+ new_bt = bt.select { |line| line.to_s !~ regexp } if new_bt.empty?
1129
+ new_bt = bt.dup if new_bt.empty?
1043
1130
 
1044
1131
  new_bt
1045
1132
  end
@@ -8,16 +8,17 @@ class Minitest::Test
8
8
  end
9
9
 
10
10
  def with_empty_backtrace_filter
11
- original = Minitest.backtrace_filter
12
-
13
- obj = Minitest::BacktraceFilter.new
14
- def obj.filter _bt
15
- []
11
+ with_backtrace_filter Minitest::BacktraceFilter.new %r%.% do
12
+ yield
16
13
  end
14
+ end
15
+
16
+ def with_backtrace_filter filter
17
+ original = Minitest.backtrace_filter
17
18
 
18
19
  Minitest::Test.io_lock.synchronize do # try not to trounce in parallel
19
20
  begin
20
- Minitest.backtrace_filter = obj
21
+ Minitest.backtrace_filter = filter
21
22
  yield
22
23
  ensure
23
24
  Minitest.backtrace_filter = original
@@ -105,15 +106,20 @@ class MetaMetaMetaTestCase < Minitest::Test
105
106
  output.gsub!(/0x[A-Fa-f0-9]+/, "0xXXX")
106
107
  output.gsub!(/ +$/, "")
107
108
 
109
+ file = ->(s) { s.start_with?("/") ? "FULLFILE" : "FILE" }
110
+
108
111
  if windows? then
109
112
  output.gsub!(/\[(?:[A-Za-z]:)?[^\]:]+:\d+\]/, "[FILE:LINE]")
110
- output.gsub!(/^(\s+)(?:[A-Za-z]:)?[^:]+:\d+:in/, '\1FILE:LINE:in')
113
+ output.gsub!(/^(\s+)(?:[A-Za-z]:)?[^:]+:\d+:in [`']/, '\1FILE:LINE:in \'')
111
114
  else
112
- output.gsub!(/\[[^\]:]+:\d+\]/, "[FILE:LINE]")
113
- output.gsub!(/^(\s+)[^:]+:\d+:in/, '\1FILE:LINE:in')
115
+ output.gsub!(/\[([^\]:]+):\d+\]/) { "[#{file[$1]}:LINE]" }
116
+ output.gsub!(/^(\s+)([^:]+):\d+:in [`']/) { "#{$1}#{file[$2]}:LINE:in '" }
114
117
  end
115
118
 
116
- output.gsub!(/( at )[^:]+:\d+/, '\1[FILE:LINE]')
119
+ output.gsub!(/in [`']block in (?:([^']+)[#.])?/, "in 'block in")
120
+ output.gsub!(/in [`'](?:([^']+)[#.])?/, "in '")
121
+
122
+ output.gsub!(/( at )[^:]+:\d+/) { "#{$1}[#{file[$2]}:LINE]" } # eval?
117
123
 
118
124
  output
119
125
  end
@@ -762,12 +762,13 @@ class TestMinitestAssertions < Minitest::Test
762
762
  Class: <SomeError>
763
763
  Message: <\"blah\">
764
764
  ---Backtrace---
765
- FILE:LINE:in \`block in test_assert_raises_default_triggered\'
765
+ FILE:LINE:in \'block in test_assert_raises_default_triggered\'
766
766
  ---------------
767
767
  EOM
768
768
 
769
769
  actual = e.message.gsub(/^.+:\d+/, "FILE:LINE")
770
770
  actual.gsub!(RE_LEVELS, "") unless jruby?
771
+ actual.gsub!(/[`']block in (?:TestMinitestAssertions#)?/, "'block in ")
771
772
 
772
773
  assert_equal expected, actual
773
774
  end
@@ -841,12 +842,13 @@ class TestMinitestAssertions < Minitest::Test
841
842
  Class: <AnError>
842
843
  Message: <\"some message\">
843
844
  ---Backtrace---
844
- FILE:LINE:in \`block in test_assert_raises_subclass_triggered\'
845
+ FILE:LINE:in \'block in test_assert_raises_subclass_triggered\'
845
846
  ---------------
846
847
  EOM
847
848
 
848
849
  actual = e.message.gsub(/^.+:\d+/, "FILE:LINE")
849
850
  actual.gsub!(RE_LEVELS, "") unless jruby?
851
+ actual.gsub!(/[`']block in (?:TestMinitestAssertions#)?/, "'block in ")
850
852
 
851
853
  assert_equal expected.chomp, actual
852
854
  end
@@ -863,12 +865,13 @@ class TestMinitestAssertions < Minitest::Test
863
865
  Class: <SyntaxError>
864
866
  Message: <\"icky\">
865
867
  ---Backtrace---
866
- FILE:LINE:in \`block in test_assert_raises_triggered_different\'
868
+ FILE:LINE:in \'block in test_assert_raises_triggered_different\'
867
869
  ---------------
868
870
  EOM
869
871
 
870
872
  actual = e.message.gsub(/^.+:\d+/, "FILE:LINE")
871
873
  actual.gsub!(RE_LEVELS, "") unless jruby?
874
+ actual.gsub!(/[`']block in (?:TestMinitestAssertions#)?/, "'block in ")
872
875
 
873
876
  assert_equal expected, actual
874
877
  end
@@ -886,12 +889,13 @@ class TestMinitestAssertions < Minitest::Test
886
889
  Class: <SyntaxError>
887
890
  Message: <\"icky\">
888
891
  ---Backtrace---
889
- FILE:LINE:in \`block in test_assert_raises_triggered_different_msg\'
892
+ FILE:LINE:in \'block in test_assert_raises_triggered_different_msg\'
890
893
  ---------------
891
894
  EOM
892
895
 
893
896
  actual = e.message.gsub(/^.+:\d+/, "FILE:LINE")
894
897
  actual.gsub!(RE_LEVELS, "") unless jruby?
898
+ actual.gsub!(/[`']block in (?:TestMinitestAssertions#)?/, "'block in ")
895
899
 
896
900
  assert_equal expected.chomp, actual
897
901
  end
@@ -936,6 +940,16 @@ class TestMinitestAssertions < Minitest::Test
936
940
  end
937
941
  end
938
942
 
943
+ def test_assert_respond_to__include_all
944
+ @tc.assert_respond_to @tc, :exit, include_all: true
945
+ end
946
+
947
+ def test_assert_respond_to__include_all_triggered
948
+ assert_triggered(/Expected .+::DummyTest. to respond to #exit\?/) do
949
+ @tc.assert_respond_to @tc, :exit?, include_all: true
950
+ end
951
+ end
952
+
939
953
  def test_assert_same
940
954
  @assertion_count = 3
941
955
 
@@ -1153,18 +1167,14 @@ class TestMinitestAssertions < Minitest::Test
1153
1167
  def test_class_asserts_match_refutes
1154
1168
  @assertion_count = 0
1155
1169
 
1156
- methods = Minitest::Assertions.public_instance_methods
1157
- methods.map!(&:to_s) if Symbol === methods.first
1170
+ methods = Minitest::Assertions.public_instance_methods.map(&:to_s)
1158
1171
 
1159
1172
  # These don't have corresponding refutes _on purpose_. They're
1160
1173
  # useless and will never be added, so don't bother.
1161
1174
  ignores = %w[assert_output assert_raises assert_send
1162
1175
  assert_silent assert_throws assert_mock]
1163
1176
 
1164
- # These are test/unit methods. I'm not actually sure why they're still here
1165
- ignores += %w[assert_no_match assert_not_equal assert_not_nil
1166
- assert_not_same assert_nothing_raised
1167
- assert_nothing_thrown assert_raise]
1177
+ ignores += %w[assert_allocations] # for minitest-gcstats
1168
1178
 
1169
1179
  asserts = methods.grep(/^assert/).sort - ignores
1170
1180
  refutes = methods.grep(/^refute/).sort - ignores
@@ -1444,6 +1454,16 @@ class TestMinitestAssertions < Minitest::Test
1444
1454
  end
1445
1455
  end
1446
1456
 
1457
+ def test_refute_respond_to__include_all
1458
+ @tc.refute_respond_to "blah", :missing, include_all: true
1459
+ end
1460
+
1461
+ def test_refute_respond_to__include_all_triggered
1462
+ assert_triggered(/Expected .*DummyTest.* to not respond to exit./) do
1463
+ @tc.refute_respond_to @tc, :exit, include_all: true
1464
+ end
1465
+ end
1466
+
1447
1467
  def test_refute_same
1448
1468
  @tc.refute_same 1, 2
1449
1469
  end
@@ -728,7 +728,7 @@ class TestMinitestStub < Minitest::Test
728
728
  end
729
729
 
730
730
  exp = jruby? ? /Undefined method nope_nope_nope for '#{self.class}::Time'/ :
731
- /undefined method `nope_nope_nope' for( class)? `#{self.class}::Time'/
731
+ /undefined method [`']nope_nope_nope' for( class)? [`']#{self.class}::Time'/
732
732
  assert_match exp, e.message
733
733
  end
734
734
 
@@ -1083,7 +1083,7 @@ class TestMinitestStub < Minitest::Test
1083
1083
  end
1084
1084
  end
1085
1085
  end
1086
- exp = /undefined method `write' for nil/
1086
+ exp = /undefined method [`']write' for nil/
1087
1087
  assert_match exp, e.message
1088
1088
  end
1089
1089
 
@@ -48,6 +48,25 @@ class TestMinitestReporter < MetaMetaMetaTestCase
48
48
  @et
49
49
  end
50
50
 
51
+ def system_stack_error_test
52
+ unless defined? @sse then
53
+
54
+ ex = SystemStackError.new
55
+
56
+ pre = ("a".."c").to_a
57
+ mid = ("aa".."ad").to_a * 67
58
+ post = ("d".."f").to_a
59
+ ary = pre + mid + post
60
+
61
+ ex.set_backtrace ary
62
+
63
+ @sse = Minitest::Test.new(:woot)
64
+ @sse.failures << Minitest::UnexpectedError.new(ex)
65
+ @sse = Minitest::Result.from @sse
66
+ end
67
+ @sse
68
+ end
69
+
51
70
  def fail_test
52
71
  unless defined? @ft then
53
72
  @ft = Minitest::Test.new(:woot)
@@ -65,6 +84,12 @@ class TestMinitestReporter < MetaMetaMetaTestCase
65
84
  @pt ||= Minitest::Result.from Minitest::Test.new(:woot)
66
85
  end
67
86
 
87
+ def passing_test_with_metadata
88
+ test = Minitest::Test.new(:woot)
89
+ test.metadata[:meta] = :data
90
+ @pt ||= Minitest::Result.from test
91
+ end
92
+
68
93
  def skip_test
69
94
  unless defined? @st then
70
95
  @st = Minitest::Test.new(:woot)
@@ -166,6 +191,29 @@ class TestMinitestReporter < MetaMetaMetaTestCase
166
191
  assert_equal 0, r.assertions
167
192
  end
168
193
 
194
+ def test_record_pass_with_metadata
195
+ reporter = self.r
196
+
197
+ def reporter.metadata
198
+ @metadata
199
+ end
200
+
201
+ def reporter.record result
202
+ super
203
+ @metadata = result.metadata if result.metadata?
204
+ end
205
+
206
+ r.record passing_test_with_metadata
207
+
208
+ exp = { :meta => :data }
209
+ assert_equal exp, reporter.metadata
210
+
211
+ assert_equal ".", io.string
212
+ assert_empty r.results
213
+ assert_equal 1, r.count
214
+ assert_equal 0, r.assertions
215
+ end
216
+
169
217
  def test_record_fail
170
218
  fail_test = self.fail_test
171
219
  r.record fail_test
@@ -276,8 +324,44 @@ class TestMinitestReporter < MetaMetaMetaTestCase
276
324
  1) Error:
277
325
  Minitest::Test#woot:
278
326
  RuntimeError: no
279
- FILE:LINE:in `error_test'
280
- FILE:LINE:in `test_report_error'
327
+ FILE:LINE:in 'error_test'
328
+ FILE:LINE:in 'test_report_error'
329
+
330
+ 1 runs, 0 assertions, 0 failures, 1 errors, 0 skips
331
+ EOM
332
+
333
+ assert_equal exp, normalize_output(io.string)
334
+ end
335
+
336
+ def test_report_error__sse
337
+ r.start
338
+ r.record system_stack_error_test
339
+ r.report
340
+
341
+ exp = clean <<-EOM
342
+ Run options:
343
+
344
+ # Running:
345
+
346
+ E
347
+
348
+ Finished in 0.00
349
+
350
+ 1) Error:
351
+ Minitest::Test#woot:
352
+ SystemStackError: 274 -> 12
353
+ a
354
+ b
355
+ c
356
+ +->> 67 cycles of 4 lines:
357
+ | aa
358
+ | ab
359
+ | ac
360
+ | ad
361
+ +-<<
362
+ d
363
+ e
364
+ f
281
365
 
282
366
  1 runs, 0 assertions, 0 failures, 1 errors, 0 skips
283
367
  EOM
@@ -309,4 +393,48 @@ class TestMinitestReporter < MetaMetaMetaTestCase
309
393
 
310
394
  assert_equal exp, normalize_output(io.string)
311
395
  end
396
+
397
+ def test_report_failure_uses_backtrace_filter
398
+ filter = Minitest::BacktraceFilter.new
399
+ def filter.filter _bt
400
+ ["foo.rb:123:in 'foo'"]
401
+ end
402
+
403
+ with_backtrace_filter filter do
404
+ r.start
405
+ r.record fail_test
406
+ r.report
407
+ end
408
+
409
+ exp = "Minitest::Test#woot [foo.rb:123]"
410
+
411
+ assert_includes io.string, exp
412
+ end
413
+
414
+ def test_report_failure_uses_backtrace_filter_complex_sorbet
415
+ backtrace = <<~EOBT
416
+ /Users/user/.gem/ruby/3.2.2/gems/minitest-5.20.0/lib/minitest/assertions.rb:183:in 'assert'
417
+ example_test.rb:9:in 'assert_false'
418
+ /Users/user/.gem/ruby/3.2.2/gems/sorbet-runtime-0.5.11068/lib/types/private/methods/call_validation.rb:256:in 'bind_call'
419
+ /Users/user/.gem/ruby/3.2.2/gems/sorbet-runtime-0.5.11068/lib/types/private/methods/call_validation.rb:256:in 'validate_call'
420
+ /Users/user/.gem/ruby/3.2.2/gems/sorbet-runtime-0.5.11068/lib/types/private/methods/_methods.rb:275:in 'block in _on_method_added'
421
+ example_test.rb:25:in 'test_something'
422
+ /Users/user/.gem/ruby/3.2.2/gems/minitest-5.20.0/lib/minitest/test.rb:94:in 'block (3 levels) in run'
423
+ /Users/user/.gem/ruby/3.2.2/gems/minitest-5.20.0/lib/minitest/test.rb:191:in 'capture_exceptions'
424
+ /Users/user/.gem/ruby/3.2.2/gems/minitest-5.20.0/lib/minitest/test.rb:89:in 'block (2 levels) in run'
425
+ ... so many lines ...
426
+ EOBT
427
+
428
+ filter = Minitest::BacktraceFilter.new %r%lib/minitest|gems/sorbet%
429
+
430
+ with_backtrace_filter filter do
431
+ begin
432
+ assert_equal 1, 2
433
+ rescue Minitest::Assertion => e
434
+ e.set_backtrace backtrace.lines.map(&:chomp)
435
+
436
+ assert_match "example_test.rb:25", e.location
437
+ end
438
+ end
439
+ end
312
440
  end