minitest 5.20.0 → 5.25.1

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.
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.20.0" # :nodoc:
12
+ VERSION = "5.25.1" # :nodoc:
13
13
 
14
14
  @@installed_at_exit ||= false
15
15
  @@after_run = []
@@ -67,9 +67,8 @@ module Minitest
67
67
  # Registers Minitest to run at process exit
68
68
 
69
69
  def self.autorun
70
- if Object.const_defined?(:Warning) && Warning.respond_to?(:[]=)
71
- Warning[:deprecated] = true
72
- end
70
+ Warning[:deprecated] = true if
71
+ Object.const_defined?(:Warning) && Warning.respond_to?(:[]=)
73
72
 
74
73
  at_exit {
75
74
  next if $! and not ($!.kind_of? SystemExit and $!.success?)
@@ -98,20 +97,19 @@ module Minitest
98
97
  @@after_run << block
99
98
  end
100
99
 
101
- def self.init_plugins options # :nodoc:
102
- self.extensions.each do |name|
103
- msg = "plugin_#{name}_init"
104
- send msg, options if self.respond_to? msg
105
- end
100
+ ##
101
+ # Register a plugin to be used. Does NOT require / load it.
102
+
103
+ def self.register_plugin name_or_mod
104
+ self.extensions << name_or_mod
105
+ nil
106
106
  end
107
107
 
108
108
  def self.load_plugins # :nodoc:
109
- return unless self.extensions.empty?
109
+ return unless defined? Gem
110
110
 
111
111
  seen = {}
112
112
 
113
- require "rubygems" unless defined? Gem
114
-
115
113
  Gem.find_files("minitest/*_plugin.rb").each do |plugin_path|
116
114
  name = File.basename plugin_path, "_plugin.rb"
117
115
 
@@ -123,72 +121,27 @@ module Minitest
123
121
  end
124
122
  end
125
123
 
126
- ##
127
- # This is the top-level run method. Everything starts from here. It
128
- # tells each Runnable sub-class to run, and each of those are
129
- # responsible for doing whatever they do.
130
- #
131
- # The overall structure of a run looks like this:
132
- #
133
- # Minitest.autorun
134
- # Minitest.run(args)
135
- # Minitest.__run(reporter, options)
136
- # Runnable.runnables.each
137
- # runnable.run(reporter, options)
138
- # self.runnable_methods.each
139
- # self.run_one_method(self, runnable_method, reporter)
140
- # Minitest.run_one_method(klass, runnable_method)
141
- # klass.new(runnable_method).run
142
-
143
- def self.run args = []
144
- self.load_plugins unless args.delete("--no-plugins") || ENV["MT_NO_PLUGINS"]
145
-
146
- options = process_args args
147
-
148
- Minitest.seed = options[:seed]
149
- srand Minitest.seed
150
-
151
- reporter = CompositeReporter.new
152
- reporter << SummaryReporter.new(options[:io], options)
153
- reporter << ProgressReporter.new(options[:io], options)
154
-
155
- self.reporter = reporter # this makes it available to plugins
156
- self.init_plugins options
157
- self.reporter = nil # runnables shouldn't depend on the reporter, ever
158
-
159
- self.parallel_executor.start if parallel_executor.respond_to?(:start)
160
- reporter.start
161
- begin
162
- __run reporter, options
163
- rescue Interrupt
164
- warn "Interrupted. Exiting..."
124
+ def self.init_plugins options # :nodoc:
125
+ self.extensions.each do |mod_or_meth|
126
+ case mod_or_meth
127
+ when Symbol, String then
128
+ name = mod_or_meth
129
+ msg = "plugin_#{name}_init"
130
+ next unless self.respond_to? msg
131
+ send msg, options
132
+ when Module then
133
+ recv = mod_or_meth
134
+ next unless recv.respond_to? :minitest_plugin_init
135
+ recv.minitest_plugin_init options
136
+ else
137
+ raise ArgumentError, "plugin is %p, but it must be a symbol, string or module" % [mod_or_meth]
138
+ end
165
139
  end
166
- self.parallel_executor.shutdown
167
- reporter.report
168
-
169
- reporter.passed?
170
- end
171
-
172
- ##
173
- # Internal run method. Responsible for telling all Runnable
174
- # sub-classes to run.
175
-
176
- def self.__run reporter, options
177
- suites = Runnable.runnables.shuffle
178
- parallel, serial = suites.partition { |s| s.test_order == :parallel }
179
-
180
- # If we run the parallel tests before the serial tests, the parallel tests
181
- # could run in parallel with the serial tests. This would be bad because
182
- # the serial tests won't lock around Reporter#record. Run the serial tests
183
- # first, so that after they complete, the parallel tests will lock when
184
- # recording results.
185
- serial.map { |suite| suite.run reporter, options } +
186
- parallel.map { |suite| suite.run reporter, options }
187
140
  end
188
141
 
189
142
  def self.process_args args = [] # :nodoc:
190
143
  options = {
191
- :io => $stdout,
144
+ :io => $stdout,
192
145
  }
193
146
  orig_args = args.dup
194
147
 
@@ -212,6 +165,10 @@ module Minitest
212
165
  options[:verbose] = true
213
166
  end
214
167
 
168
+ opts.on "-q", "--quiet", "Quiet. Show no progress processing files." do
169
+ options[:quiet] = true
170
+ end
171
+
215
172
  opts.on "--show-skips", "Show skipped at the end of run." do
216
173
  options[:show_skips] = true
217
174
  end
@@ -228,13 +185,37 @@ module Minitest
228
185
  options[:skip] = s.chars.to_a
229
186
  end
230
187
 
188
+ ruby27plus = ::Warning.respond_to? :[]=
189
+
190
+ opts.on "-W[error]", String, "Turn Ruby warnings into errors" do |s|
191
+ options[:Werror] = true
192
+ case s
193
+ when "error", "all", nil then
194
+ require "minitest/error_on_warning"
195
+ $VERBOSE = true
196
+ ::Warning[:deprecated] = true if ruby27plus
197
+ else
198
+ ::Warning[s.to_sym] = true if ruby27plus # check validity of category
199
+ end
200
+ end
201
+
231
202
  unless extensions.empty?
232
203
  opts.separator ""
233
- opts.separator "Known extensions: #{extensions.join(", ")}"
234
-
235
- extensions.each do |meth|
236
- msg = "plugin_#{meth}_options"
237
- send msg, opts, options if self.respond_to?(msg)
204
+ opts.separator "Known extensions: #{extensions.join ", "}"
205
+
206
+ extensions.each do |mod_or_meth|
207
+ case mod_or_meth
208
+ when Symbol, String then
209
+ meth = mod_or_meth
210
+ msg = "plugin_#{meth}_options"
211
+ send msg, opts, options if respond_to? msg
212
+ when Module
213
+ recv = mod_or_meth
214
+ next unless recv.respond_to? :minitest_plugin_options
215
+ recv.minitest_plugin_options opts, options
216
+ else
217
+ raise ArgumentError, "plugin is %p, but it must be a symbol, string or module" % [mod_or_meth]
218
+ end
238
219
  end
239
220
  end
240
221
 
@@ -258,12 +239,99 @@ module Minitest
258
239
  end
259
240
 
260
241
  options[:args] = orig_args.map { |s|
261
- s =~ /[\s|&<>$()]/ ? s.inspect : s
242
+ s.match?(/[\s|&<>$()]/) ? s.inspect : s
262
243
  }.join " "
263
244
 
264
245
  options
265
246
  end
266
247
 
248
+ ##
249
+ # This is the top-level run method. Everything starts from here. It
250
+ # tells each Runnable sub-class to run, and each of those are
251
+ # responsible for doing whatever they do.
252
+ #
253
+ # The overall structure of a run looks like this:
254
+ #
255
+ # Minitest.autorun
256
+ # Minitest.run(args)
257
+ # Minitest.load_plugins
258
+ # Minitest.process_args
259
+ # Minitest.init_plugins
260
+ # Minitest.__run(reporter, options)
261
+ # Runnable.runnables.each
262
+ # runnable_klass.run(reporter, options)
263
+ # self.runnable_methods.each
264
+ # self.run_one_method(self, runnable_method, reporter)
265
+ # Minitest.run_one_method(klass, runnable_method)
266
+ # klass.new(runnable_method).run
267
+
268
+ def self.run args = []
269
+ self.load_plugins unless args.delete("--no-plugins") || ENV["MT_NO_PLUGINS"]
270
+
271
+ options = process_args args
272
+
273
+ Minitest.seed = options[:seed]
274
+ srand Minitest.seed
275
+
276
+ reporter = CompositeReporter.new
277
+ reporter << SummaryReporter.new(options[:io], options)
278
+ reporter << ProgressReporter.new(options[:io], options) unless options[:quiet]
279
+
280
+ self.reporter = reporter # this makes it available to plugins
281
+ self.init_plugins options
282
+ self.reporter = nil # runnables shouldn't depend on the reporter, ever
283
+
284
+ self.parallel_executor.start if parallel_executor.respond_to? :start
285
+ reporter.start
286
+ begin
287
+ __run reporter, options
288
+ rescue Interrupt
289
+ warn "Interrupted. Exiting..."
290
+ end
291
+ self.parallel_executor.shutdown
292
+
293
+ # might have been removed/replaced during init_plugins:
294
+ summary = reporter.reporters.grep(SummaryReporter).first
295
+
296
+ reporter.report
297
+
298
+ return empty_run! options if summary && summary.count == 0
299
+ reporter.passed?
300
+ end
301
+
302
+ def self.empty_run! options # :nodoc:
303
+ filter = options[:filter]
304
+ return true unless filter # no filter, but nothing ran == success
305
+
306
+ warn "Nothing ran for filter: %s" % [filter]
307
+
308
+ require "did_you_mean" # soft dependency, punt if it doesn't load
309
+
310
+ ms = Runnable.runnables.flat_map(&:runnable_methods)
311
+ cs = DidYouMean::SpellChecker.new(dictionary: ms).correct filter
312
+
313
+ warn DidYouMean::Formatter.message_for cs unless cs.empty?
314
+ rescue LoadError
315
+ # do nothing
316
+ end
317
+
318
+ ##
319
+ # Internal run method. Responsible for telling all Runnable
320
+ # sub-classes to run.
321
+
322
+ def self.__run reporter, options
323
+ suites = Runnable.runnables.shuffle
324
+ parallel, serial = suites.partition { |s| s.test_order == :parallel }
325
+
326
+ # If we run the parallel tests before the serial tests, the parallel tests
327
+ # could run in parallel with the serial tests. This would be bad because
328
+ # the serial tests won't lock around Reporter#record. Run the serial tests
329
+ # first, so that after they complete, the parallel tests will lock when
330
+ # recording results.
331
+ serial.map { |suite| suite.run reporter, options } +
332
+ parallel.map { |suite| suite.run reporter, options }
333
+ end
334
+
267
335
  def self.filter_backtrace bt # :nodoc:
268
336
  result = backtrace_filter.filter bt
269
337
  result = bt.dup if result.empty?
@@ -334,30 +402,34 @@ module Minitest
334
402
  # reporter to record.
335
403
 
336
404
  def self.run reporter, options = {}
337
- filtered_methods = if options[:filter]
338
- filter = options[:filter]
339
- filter = Regexp.new $1 if filter.is_a?(String) && filter =~ %r%/(.*)/%
340
-
341
- self.runnable_methods.find_all { |m|
342
- filter === m || filter === "#{self}##{m}"
343
- }
344
- else
345
- self.runnable_methods
346
- end
405
+ pos = options[:filter]
406
+ neg = options[:exclude]
347
407
 
348
- if options[:exclude]
349
- exclude = options[:exclude]
350
- exclude = Regexp.new $1 if exclude =~ %r%/(.*)/%
408
+ pos = Regexp.new $1 if pos.kind_of?(String) && pos =~ %r%/(.*)/%
409
+ neg = Regexp.new $1 if neg.kind_of?(String) && neg =~ %r%/(.*)/%
351
410
 
352
- filtered_methods.delete_if { |m|
353
- exclude === m || exclude === "#{self}##{m}"
354
- }
355
- end
411
+ filtered_methods = self.runnable_methods
412
+ .select { |m| !pos || pos === m || pos === "#{self}##{m}" }
413
+ .reject { |m| neg && (neg === m || neg === "#{self}##{m}") }
356
414
 
357
415
  return if filtered_methods.empty?
358
416
 
417
+ t0 = name = nil
418
+
419
+ @_info_handler = lambda do
420
+ unless reporter.passed? then
421
+ warn "Current results:"
422
+ warn reporter.reporters.grep(SummaryReporter).first
423
+ end
424
+
425
+ warn "Current: %s#%s %.2fs" % [self, name, Minitest.clock_time - t0]
426
+ end
427
+
359
428
  with_info_handler reporter do
360
429
  filtered_methods.each do |method_name|
430
+ name = method_name
431
+ t0 = Minitest.clock_time
432
+
361
433
  run_one_method self, method_name, reporter
362
434
  end
363
435
  end
@@ -383,16 +455,7 @@ module Minitest
383
455
  end
384
456
 
385
457
  def self.with_info_handler reporter, &block # :nodoc:
386
- handler = lambda do
387
- unless reporter.passed? then
388
- warn "Current results:"
389
- warn ""
390
- warn reporter.reporters.first
391
- warn ""
392
- end
393
- end
394
-
395
- on_signal ::Minitest.info_signal, handler, &block
458
+ on_signal ::Minitest.info_signal, @_info_handler, &block
396
459
  end
397
460
 
398
461
  SIGNALS = Signal.list # :nodoc:
@@ -430,7 +493,7 @@ module Minitest
430
493
  def marshal_dump # :nodoc:
431
494
  unless @@marshal_dump_warned then
432
495
  warn ["Minitest::Runnable#marshal_dump is deprecated.",
433
- "You might be violating internals. From", caller.first].join " "
496
+ "You might be violating internals. From", caller(1..1).first].join " "
434
497
  @@marshal_dump_warned = true
435
498
  end
436
499
 
@@ -525,12 +588,14 @@ module Minitest
525
588
  not self.failure
526
589
  end
527
590
 
591
+ BASE_DIR = "#{Dir.pwd}/" # :nodoc:
592
+
528
593
  ##
529
594
  # The location identifier of this test. Depends on a method
530
595
  # existing called class_name.
531
596
 
532
597
  def location
533
- loc = " [#{self.failure.location}]" unless passed? or error?
598
+ loc = " [#{self.failure.location.delete_prefix BASE_DIR}]" unless passed? or error?
534
599
  "#{self.class_name}##{self.name}#{loc}"
535
600
  end
536
601
 
@@ -556,7 +621,7 @@ module Minitest
556
621
  # Did this run error?
557
622
 
558
623
  def error?
559
- self.failures.any? { |f| UnexpectedError === f }
624
+ self.failures.any? UnexpectedError
560
625
  end
561
626
  end
562
627
 
@@ -619,7 +684,10 @@ module Minitest
619
684
  # you want. Go nuts.
620
685
 
621
686
  class AbstractReporter
622
- include Mutex_m
687
+
688
+ def initialize # :nodoc:
689
+ @mutex = Mutex.new
690
+ end
623
691
 
624
692
  ##
625
693
  # Starts reporting on the run.
@@ -655,6 +723,10 @@ module Minitest
655
723
  def passed?
656
724
  true
657
725
  end
726
+
727
+ def synchronize &block # :nodoc:
728
+ @mutex.synchronize(&block)
729
+ end
658
730
  end
659
731
 
660
732
  class Reporter < AbstractReporter # :nodoc:
@@ -684,11 +756,11 @@ module Minitest
684
756
  # own.
685
757
 
686
758
  class ProgressReporter < Reporter
687
- def prerecord klass, name #:nodoc:
688
- if options[:verbose] then
689
- io.print "%s#%s = " % [klass.name, name]
690
- io.flush
691
- end
759
+ def prerecord klass, name # :nodoc:
760
+ return unless options[:verbose]
761
+
762
+ io.print "%s#%s = " % [klass.name, name]
763
+ io.flush
692
764
  end
693
765
 
694
766
  def record result # :nodoc:
@@ -758,6 +830,11 @@ module Minitest
758
830
 
759
831
  attr_accessor :errors
760
832
 
833
+ ##
834
+ # Total number of tests that warned.
835
+
836
+ attr_accessor :warnings
837
+
761
838
  ##
762
839
  # Total number of tests that where skipped.
763
840
 
@@ -773,6 +850,7 @@ module Minitest
773
850
  self.total_time = nil
774
851
  self.failures = nil
775
852
  self.errors = nil
853
+ self.warnings = nil
776
854
  self.skips = nil
777
855
  end
778
856
 
@@ -801,6 +879,7 @@ module Minitest
801
879
  self.total_time = Minitest.clock_time - start_time
802
880
  self.failures = aggregate[Assertion].size
803
881
  self.errors = aggregate[UnexpectedError].size
882
+ self.warnings = aggregate[UnexpectedWarning].size
804
883
  self.skips = aggregate[Skip].size
805
884
  end
806
885
  end
@@ -815,10 +894,8 @@ module Minitest
815
894
  # own.
816
895
 
817
896
  class SummaryReporter < StatisticsReporter
818
- # :stopdoc:
819
- attr_accessor :sync
820
- attr_accessor :old_sync
821
- # :startdoc:
897
+ attr_accessor :sync # :nodoc:
898
+ attr_accessor :old_sync # :nodoc:
822
899
 
823
900
  def start # :nodoc:
824
901
  super
@@ -828,7 +905,7 @@ module Minitest
828
905
  io.puts "# Running:"
829
906
  io.puts
830
907
 
831
- self.sync = io.respond_to? :"sync=" # stupid emacs
908
+ self.sync = io.respond_to? :"sync="
832
909
  self.old_sync, io.sync = io.sync, true if self.sync
833
910
  end
834
911
 
@@ -866,18 +943,22 @@ module Minitest
866
943
  end
867
944
 
868
945
  def to_s # :nodoc:
869
- aggregated_results(StringIO.new(''.b)).string
946
+ aggregated_results(StringIO.new("".b)).string
870
947
  end
871
948
 
872
949
  def summary # :nodoc:
873
- extra = ""
950
+ extra = []
874
951
 
875
- extra = "\n\nYou have skipped tests. Run with --verbose for details." if
952
+ extra << ", %d warnings" % [warnings] if options[:Werror]
953
+
954
+ extra << "\n\nYou have skipped tests. Run with --verbose for details." if
876
955
  results.any?(&:skipped?) unless
877
- options[:verbose] or options[:show_skips] or ENV["MT_NO_SKIP_MSG"]
956
+ options[:verbose] or
957
+ options[:show_skips] or
958
+ ENV["MT_NO_SKIP_MSG"]
878
959
 
879
960
  "%d runs, %d assertions, %d failures, %d errors, %d skips%s" %
880
- [count, assertions, failures, errors, skips, extra]
961
+ [count, assertions, failures, errors, skips, extra.join]
881
962
  end
882
963
  end
883
964
 
@@ -936,6 +1017,8 @@ module Minitest
936
1017
  # Represents run failures.
937
1018
 
938
1019
  class Assertion < Exception
1020
+ RE = /in [`'](?:[^']+[#.])?(?:assert|refute|flunk|pass|fail|raise|must|wont)/ # :nodoc:
1021
+
939
1022
  def error # :nodoc:
940
1023
  self
941
1024
  end
@@ -944,12 +1027,11 @@ module Minitest
944
1027
  # Where was this run before an assertion was raised?
945
1028
 
946
1029
  def location
947
- last_before_assertion = ""
948
- self.backtrace.reverse_each do |s|
949
- break if s =~ /in .(assert|refute|flunk|pass|fail|raise|must|wont)/
950
- last_before_assertion = s
951
- end
952
- last_before_assertion.sub(/:in .*$/, "")
1030
+ bt = Minitest.filter_backtrace self.backtrace
1031
+ idx = bt.rindex { |s| s.match? RE } || -1 # fall back to first item
1032
+ loc = bt[idx+1] || bt.last || "unknown:-1"
1033
+
1034
+ loc.sub(/:in .*$/, "")
953
1035
  end
954
1036
 
955
1037
  def result_code # :nodoc:
@@ -974,11 +1056,21 @@ module Minitest
974
1056
  # Assertion wrapping an unexpected error that was raised during a run.
975
1057
 
976
1058
  class UnexpectedError < Assertion
1059
+ include Minitest::Compress
1060
+
977
1061
  # TODO: figure out how to use `cause` instead
978
1062
  attr_accessor :error # :nodoc:
979
1063
 
980
1064
  def initialize error # :nodoc:
981
1065
  super "Unexpected exception"
1066
+
1067
+ if SystemStackError === error then
1068
+ bt = error.backtrace
1069
+ new_bt = compress bt
1070
+ error = error.exception "#{bt.size} -> #{new_bt.size}"
1071
+ error.set_backtrace new_bt
1072
+ end
1073
+
982
1074
  self.error = error
983
1075
  end
984
1076
 
@@ -986,8 +1078,11 @@ module Minitest
986
1078
  self.error.backtrace
987
1079
  end
988
1080
 
1081
+ BASE_RE = %r%#{Dir.pwd}/% # :nodoc:
1082
+
989
1083
  def message # :nodoc:
990
- bt = Minitest.filter_backtrace(self.backtrace).join "\n "
1084
+ bt = Minitest.filter_backtrace(self.backtrace).join("\n ")
1085
+ .gsub(BASE_RE, "")
991
1086
  "#{self.error.class}: #{self.error.message}\n #{bt}"
992
1087
  end
993
1088
 
@@ -996,6 +1091,15 @@ module Minitest
996
1091
  end
997
1092
  end
998
1093
 
1094
+ ##
1095
+ # Assertion raised on warning when running in -Werror mode.
1096
+
1097
+ class UnexpectedWarning < Assertion
1098
+ def result_label # :nodoc:
1099
+ "Warning"
1100
+ end
1101
+ end
1102
+
999
1103
  ##
1000
1104
  # Provides a simple set of guards that you can use in your tests
1001
1105
  # to skip execution if it is not applicable. These methods are
@@ -1025,7 +1129,7 @@ module Minitest
1025
1129
 
1026
1130
  def maglev? platform = defined?(RUBY_ENGINE) && RUBY_ENGINE
1027
1131
  where = Minitest.filter_backtrace(caller).first
1028
- where = where.split(/:in /, 2).first # clean up noise
1132
+ where = where.split(":in ", 2).first # clean up noise
1029
1133
  warn "DEPRECATED: `maglev?` called from #{where}. This will fail in Minitest 6."
1030
1134
  "maglev" == platform
1031
1135
  end
@@ -1034,14 +1138,14 @@ module Minitest
1034
1138
  # Is this running on mri?
1035
1139
 
1036
1140
  def mri? platform = RUBY_DESCRIPTION
1037
- /^ruby/ =~ platform
1141
+ platform.start_with? "ruby"
1038
1142
  end
1039
1143
 
1040
1144
  ##
1041
1145
  # Is this running on macOS?
1042
1146
 
1043
1147
  def osx? platform = RUBY_PLATFORM
1044
- /darwin/ =~ platform
1148
+ platform.include? "darwin"
1045
1149
  end
1046
1150
 
1047
1151
  ##
@@ -1049,7 +1153,7 @@ module Minitest
1049
1153
 
1050
1154
  def rubinius? platform = defined?(RUBY_ENGINE) && RUBY_ENGINE
1051
1155
  where = Minitest.filter_backtrace(caller).first
1052
- where = where.split(/:in /, 2).first # clean up noise
1156
+ where = where.split(":in ", 2).first # clean up noise
1053
1157
  warn "DEPRECATED: `rubinius?` called from #{where}. This will fail in Minitest 6."
1054
1158
  "rbx" == platform
1055
1159
  end
@@ -1058,7 +1162,7 @@ module Minitest
1058
1162
  # Is this running on windows?
1059
1163
 
1060
1164
  def windows? platform = RUBY_PLATFORM
1061
- /mswin|mingw/ =~ platform
1165
+ /mswin|mingw/.match? platform
1062
1166
  end
1063
1167
  end
1064
1168
 
@@ -1069,7 +1173,16 @@ module Minitest
1069
1173
 
1070
1174
  class BacktraceFilter
1071
1175
 
1072
- MT_RE = %r%lib/minitest% #:nodoc:
1176
+ MT_RE = %r%lib/minitest|internal:warning% # :nodoc:
1177
+
1178
+ ##
1179
+ # The regular expression to use to filter backtraces. Defaults to +MT_RE+.
1180
+
1181
+ attr_accessor :regexp
1182
+
1183
+ def initialize regexp = MT_RE # :nodoc:
1184
+ self.regexp = regexp
1185
+ end
1073
1186
 
1074
1187
  ##
1075
1188
  # Filter +bt+ to something useful. Returns the whole thing if
@@ -1080,9 +1193,9 @@ module Minitest
1080
1193
 
1081
1194
  return bt.dup if $DEBUG || ENV["MT_DEBUG"]
1082
1195
 
1083
- new_bt = bt.take_while { |line| line !~ MT_RE }
1084
- new_bt = bt.select { |line| line !~ MT_RE } if new_bt.empty?
1085
- new_bt = bt.dup if new_bt.empty?
1196
+ new_bt = bt.take_while { |line| !regexp.match? line.to_s }
1197
+ new_bt = bt.select { |line| !regexp.match? line.to_s } if new_bt.empty?
1198
+ new_bt = bt.dup if new_bt.empty?
1086
1199
 
1087
1200
  new_bt
1088
1201
  end