minitest 5.16.3 → 5.25.1

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.16.3" # :nodoc:
12
+ VERSION = "5.25.1" # :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,13 +60,15 @@ 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
 
66
69
  def self.autorun
67
- if Object.const_defined?(:Warning) && Warning.respond_to?(:[]=)
68
- Warning[:deprecated] = true
69
- end
70
+ Warning[:deprecated] = true if
71
+ Object.const_defined?(:Warning) && Warning.respond_to?(:[]=)
70
72
 
71
73
  at_exit {
72
74
  next if $! and not ($!.kind_of? SystemExit and $!.success?)
@@ -75,7 +77,7 @@ module Minitest
75
77
 
76
78
  pid = Process.pid
77
79
  at_exit {
78
- next if Process.pid != pid
80
+ next if !Minitest.allow_fork && Process.pid != pid
79
81
  @@after_run.reverse_each(&:call)
80
82
  exit exit_code || false
81
83
  }
@@ -95,20 +97,19 @@ module Minitest
95
97
  @@after_run << block
96
98
  end
97
99
 
98
- def self.init_plugins options # :nodoc:
99
- self.extensions.each do |name|
100
- msg = "plugin_#{name}_init"
101
- send msg, options if self.respond_to? msg
102
- 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
103
106
  end
104
107
 
105
108
  def self.load_plugins # :nodoc:
106
- return unless self.extensions.empty?
109
+ return unless defined? Gem
107
110
 
108
111
  seen = {}
109
112
 
110
- require "rubygems" unless defined? Gem
111
-
112
113
  Gem.find_files("minitest/*_plugin.rb").each do |plugin_path|
113
114
  name = File.basename plugin_path, "_plugin.rb"
114
115
 
@@ -120,72 +121,27 @@ module Minitest
120
121
  end
121
122
  end
122
123
 
123
- ##
124
- # This is the top-level run method. Everything starts from here. It
125
- # tells each Runnable sub-class to run, and each of those are
126
- # responsible for doing whatever they do.
127
- #
128
- # The overall structure of a run looks like this:
129
- #
130
- # Minitest.autorun
131
- # Minitest.run(args)
132
- # Minitest.__run(reporter, options)
133
- # Runnable.runnables.each
134
- # runnable.run(reporter, options)
135
- # self.runnable_methods.each
136
- # self.run_one_method(self, runnable_method, reporter)
137
- # Minitest.run_one_method(klass, runnable_method)
138
- # klass.new(runnable_method).run
139
-
140
- def self.run args = []
141
- self.load_plugins unless args.delete("--no-plugins") || ENV["MT_NO_PLUGINS"]
142
-
143
- options = process_args args
144
-
145
- Minitest.seed = options[:seed]
146
- srand Minitest.seed
147
-
148
- reporter = CompositeReporter.new
149
- reporter << SummaryReporter.new(options[:io], options)
150
- reporter << ProgressReporter.new(options[:io], options)
151
-
152
- self.reporter = reporter # this makes it available to plugins
153
- self.init_plugins options
154
- self.reporter = nil # runnables shouldn't depend on the reporter, ever
155
-
156
- self.parallel_executor.start if parallel_executor.respond_to?(:start)
157
- reporter.start
158
- begin
159
- __run reporter, options
160
- rescue Interrupt
161
- 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
162
139
  end
163
- self.parallel_executor.shutdown
164
- reporter.report
165
-
166
- reporter.passed?
167
- end
168
-
169
- ##
170
- # Internal run method. Responsible for telling all Runnable
171
- # sub-classes to run.
172
-
173
- def self.__run reporter, options
174
- suites = Runnable.runnables.shuffle
175
- parallel, serial = suites.partition { |s| s.test_order == :parallel }
176
-
177
- # If we run the parallel tests before the serial tests, the parallel tests
178
- # could run in parallel with the serial tests. This would be bad because
179
- # the serial tests won't lock around Reporter#record. Run the serial tests
180
- # first, so that after they complete, the parallel tests will lock when
181
- # recording results.
182
- serial.map { |suite| suite.run reporter, options } +
183
- parallel.map { |suite| suite.run reporter, options }
184
140
  end
185
141
 
186
142
  def self.process_args args = [] # :nodoc:
187
143
  options = {
188
- :io => $stdout,
144
+ :io => $stdout,
189
145
  }
190
146
  orig_args = args.dup
191
147
 
@@ -209,6 +165,10 @@ module Minitest
209
165
  options[:verbose] = true
210
166
  end
211
167
 
168
+ opts.on "-q", "--quiet", "Quiet. Show no progress processing files." do
169
+ options[:quiet] = true
170
+ end
171
+
212
172
  opts.on "--show-skips", "Show skipped at the end of run." do
213
173
  options[:show_skips] = true
214
174
  end
@@ -225,13 +185,37 @@ module Minitest
225
185
  options[:skip] = s.chars.to_a
226
186
  end
227
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
+
228
202
  unless extensions.empty?
229
203
  opts.separator ""
230
- opts.separator "Known extensions: #{extensions.join(", ")}"
231
-
232
- extensions.each do |meth|
233
- msg = "plugin_#{meth}_options"
234
- 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
235
219
  end
236
220
  end
237
221
 
@@ -255,12 +239,99 @@ module Minitest
255
239
  end
256
240
 
257
241
  options[:args] = orig_args.map { |s|
258
- s =~ /[\s|&<>$()]/ ? s.inspect : s
242
+ s.match?(/[\s|&<>$()]/) ? s.inspect : s
259
243
  }.join " "
260
244
 
261
245
  options
262
246
  end
263
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
+
264
335
  def self.filter_backtrace bt # :nodoc:
265
336
  result = backtrace_filter.filter bt
266
337
  result = bt.dup if result.empty?
@@ -331,24 +402,34 @@ module Minitest
331
402
  # reporter to record.
332
403
 
333
404
  def self.run reporter, options = {}
334
- filter = options[:filter] || "/./"
335
- filter = Regexp.new $1 if filter.is_a?(String) && filter =~ %r%/(.*)/%
336
-
337
- filtered_methods = self.runnable_methods.find_all { |m|
338
- filter === m || filter === "#{self}##{m}"
339
- }
405
+ pos = options[:filter]
406
+ neg = options[:exclude]
340
407
 
341
- exclude = options[:exclude]
342
- 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%/(.*)/%
343
410
 
344
- filtered_methods.delete_if { |m|
345
- exclude === m || exclude === "#{self}##{m}"
346
- }
411
+ filtered_methods = self.runnable_methods
412
+ .select { |m| !pos || pos === m || pos === "#{self}##{m}" }
413
+ .reject { |m| neg && (neg === m || neg === "#{self}##{m}") }
347
414
 
348
415
  return if filtered_methods.empty?
349
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
+
350
428
  with_info_handler reporter do
351
429
  filtered_methods.each do |method_name|
430
+ name = method_name
431
+ t0 = Minitest.clock_time
432
+
352
433
  run_one_method self, method_name, reporter
353
434
  end
354
435
  end
@@ -365,17 +446,16 @@ module Minitest
365
446
  reporter.record Minitest.run_one_method(klass, method_name)
366
447
  end
367
448
 
368
- def self.with_info_handler reporter, &block # :nodoc:
369
- handler = lambda do
370
- unless reporter.passed? then
371
- warn "Current results:"
372
- warn ""
373
- warn reporter.reporters.first
374
- warn ""
375
- end
376
- end
449
+ ##
450
+ # Defines the order to run tests (:random by default). Override
451
+ # this or use a convenience method to change it for your tests.
377
452
 
378
- on_signal ::Minitest.info_signal, handler, &block
453
+ def self.test_order
454
+ :random
455
+ end
456
+
457
+ def self.with_info_handler reporter, &block # :nodoc:
458
+ on_signal ::Minitest.info_signal, @_info_handler, &block
379
459
  end
380
460
 
381
461
  SIGNALS = Signal.list # :nodoc:
@@ -413,7 +493,7 @@ module Minitest
413
493
  def marshal_dump # :nodoc:
414
494
  unless @@marshal_dump_warned then
415
495
  warn ["Minitest::Runnable#marshal_dump is deprecated.",
416
- "You might be violating internals. From", caller.first].join " "
496
+ "You might be violating internals. From", caller(1..1).first].join " "
417
497
  @@marshal_dump_warned = true
418
498
  end
419
499
 
@@ -432,6 +512,31 @@ module Minitest
432
512
  self.name = name
433
513
  self.failures = []
434
514
  self.assertions = 0
515
+ # lazy initializer for metadata
516
+ end
517
+
518
+ ##
519
+ # Metadata you attach to the test results that get sent to the reporter.
520
+ #
521
+ # Lazily initializes to a hash, to keep memory down.
522
+ #
523
+ # NOTE: this data *must* be plain (read: marshal-able) data!
524
+ # Hashes! Arrays! Strings!
525
+
526
+ def metadata
527
+ @metadata ||= {}
528
+ end
529
+
530
+ ##
531
+ # Sets metadata, mainly used for +Result.from+.
532
+
533
+ attr_writer :metadata
534
+
535
+ ##
536
+ # Returns true if metadata exists.
537
+
538
+ def metadata?
539
+ defined? @metadata
435
540
  end
436
541
 
437
542
  ##
@@ -483,12 +588,14 @@ module Minitest
483
588
  not self.failure
484
589
  end
485
590
 
591
+ BASE_DIR = "#{Dir.pwd}/" # :nodoc:
592
+
486
593
  ##
487
594
  # The location identifier of this test. Depends on a method
488
595
  # existing called class_name.
489
596
 
490
597
  def location
491
- loc = " [#{self.failure.location}]" unless passed? or error?
598
+ loc = " [#{self.failure.location.delete_prefix BASE_DIR}]" unless passed? or error?
492
599
  "#{self.class_name}##{self.name}#{loc}"
493
600
  end
494
601
 
@@ -514,7 +621,7 @@ module Minitest
514
621
  # Did this run error?
515
622
 
516
623
  def error?
517
- self.failures.any? { |f| UnexpectedError === f }
624
+ self.failures.any? UnexpectedError
518
625
  end
519
626
  end
520
627
 
@@ -552,6 +659,7 @@ module Minitest
552
659
  r.assertions = o.assertions
553
660
  r.failures = o.failures.dup
554
661
  r.time = o.time
662
+ r.metadata = o.metadata if o.metadata?
555
663
 
556
664
  r.source_location = o.method(o.name).source_location rescue ["unknown", -1]
557
665
 
@@ -576,7 +684,10 @@ module Minitest
576
684
  # you want. Go nuts.
577
685
 
578
686
  class AbstractReporter
579
- include Mutex_m
687
+
688
+ def initialize # :nodoc:
689
+ @mutex = Mutex.new
690
+ end
580
691
 
581
692
  ##
582
693
  # Starts reporting on the run.
@@ -612,6 +723,10 @@ module Minitest
612
723
  def passed?
613
724
  true
614
725
  end
726
+
727
+ def synchronize &block # :nodoc:
728
+ @mutex.synchronize(&block)
729
+ end
615
730
  end
616
731
 
617
732
  class Reporter < AbstractReporter # :nodoc:
@@ -641,11 +756,11 @@ module Minitest
641
756
  # own.
642
757
 
643
758
  class ProgressReporter < Reporter
644
- def prerecord klass, name #:nodoc:
645
- if options[:verbose] then
646
- io.print "%s#%s = " % [klass.name, name]
647
- io.flush
648
- end
759
+ def prerecord klass, name # :nodoc:
760
+ return unless options[:verbose]
761
+
762
+ io.print "%s#%s = " % [klass.name, name]
763
+ io.flush
649
764
  end
650
765
 
651
766
  def record result # :nodoc:
@@ -715,6 +830,11 @@ module Minitest
715
830
 
716
831
  attr_accessor :errors
717
832
 
833
+ ##
834
+ # Total number of tests that warned.
835
+
836
+ attr_accessor :warnings
837
+
718
838
  ##
719
839
  # Total number of tests that where skipped.
720
840
 
@@ -730,6 +850,7 @@ module Minitest
730
850
  self.total_time = nil
731
851
  self.failures = nil
732
852
  self.errors = nil
853
+ self.warnings = nil
733
854
  self.skips = nil
734
855
  end
735
856
 
@@ -758,6 +879,7 @@ module Minitest
758
879
  self.total_time = Minitest.clock_time - start_time
759
880
  self.failures = aggregate[Assertion].size
760
881
  self.errors = aggregate[UnexpectedError].size
882
+ self.warnings = aggregate[UnexpectedWarning].size
761
883
  self.skips = aggregate[Skip].size
762
884
  end
763
885
  end
@@ -772,10 +894,8 @@ module Minitest
772
894
  # own.
773
895
 
774
896
  class SummaryReporter < StatisticsReporter
775
- # :stopdoc:
776
- attr_accessor :sync
777
- attr_accessor :old_sync
778
- # :startdoc:
897
+ attr_accessor :sync # :nodoc:
898
+ attr_accessor :old_sync # :nodoc:
779
899
 
780
900
  def start # :nodoc:
781
901
  super
@@ -785,7 +905,7 @@ module Minitest
785
905
  io.puts "# Running:"
786
906
  io.puts
787
907
 
788
- self.sync = io.respond_to? :"sync=" # stupid emacs
908
+ self.sync = io.respond_to? :"sync="
789
909
  self.old_sync, io.sync = io.sync, true if self.sync
790
910
  end
791
911
 
@@ -823,26 +943,22 @@ module Minitest
823
943
  end
824
944
 
825
945
  def to_s # :nodoc:
826
- aggregated_results(StringIO.new(binary_string)).string
946
+ aggregated_results(StringIO.new("".b)).string
827
947
  end
828
948
 
829
949
  def summary # :nodoc:
830
- extra = ""
950
+ extra = []
951
+
952
+ extra << ", %d warnings" % [warnings] if options[:Werror]
831
953
 
832
- extra = "\n\nYou have skipped tests. Run with --verbose for details." if
954
+ extra << "\n\nYou have skipped tests. Run with --verbose for details." if
833
955
  results.any?(&:skipped?) unless
834
- 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"]
835
959
 
836
960
  "%d runs, %d assertions, %d failures, %d errors, %d skips%s" %
837
- [count, assertions, failures, errors, skips, extra]
838
- end
839
-
840
- private
841
-
842
- if '<3'.respond_to? :b
843
- def binary_string; ''.b; end
844
- else
845
- def binary_string; ''.force_encoding(Encoding::ASCII_8BIT); end
961
+ [count, assertions, failures, errors, skips, extra.join]
846
962
  end
847
963
  end
848
964
 
@@ -901,6 +1017,8 @@ module Minitest
901
1017
  # Represents run failures.
902
1018
 
903
1019
  class Assertion < Exception
1020
+ RE = /in [`'](?:[^']+[#.])?(?:assert|refute|flunk|pass|fail|raise|must|wont)/ # :nodoc:
1021
+
904
1022
  def error # :nodoc:
905
1023
  self
906
1024
  end
@@ -909,12 +1027,11 @@ module Minitest
909
1027
  # Where was this run before an assertion was raised?
910
1028
 
911
1029
  def location
912
- last_before_assertion = ""
913
- self.backtrace.reverse_each do |s|
914
- break if s =~ /in .(assert|refute|flunk|pass|fail|raise|must|wont)/
915
- last_before_assertion = s
916
- end
917
- 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 .*$/, "")
918
1035
  end
919
1036
 
920
1037
  def result_code # :nodoc:
@@ -939,11 +1056,21 @@ module Minitest
939
1056
  # Assertion wrapping an unexpected error that was raised during a run.
940
1057
 
941
1058
  class UnexpectedError < Assertion
1059
+ include Minitest::Compress
1060
+
942
1061
  # TODO: figure out how to use `cause` instead
943
1062
  attr_accessor :error # :nodoc:
944
1063
 
945
1064
  def initialize error # :nodoc:
946
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
+
947
1074
  self.error = error
948
1075
  end
949
1076
 
@@ -951,8 +1078,11 @@ module Minitest
951
1078
  self.error.backtrace
952
1079
  end
953
1080
 
1081
+ BASE_RE = %r%#{Dir.pwd}/% # :nodoc:
1082
+
954
1083
  def message # :nodoc:
955
- bt = Minitest.filter_backtrace(self.backtrace).join "\n "
1084
+ bt = Minitest.filter_backtrace(self.backtrace).join("\n ")
1085
+ .gsub(BASE_RE, "")
956
1086
  "#{self.error.class}: #{self.error.message}\n #{bt}"
957
1087
  end
958
1088
 
@@ -961,6 +1091,15 @@ module Minitest
961
1091
  end
962
1092
  end
963
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
+
964
1103
  ##
965
1104
  # Provides a simple set of guards that you can use in your tests
966
1105
  # to skip execution if it is not applicable. These methods are
@@ -990,7 +1129,7 @@ module Minitest
990
1129
 
991
1130
  def maglev? platform = defined?(RUBY_ENGINE) && RUBY_ENGINE
992
1131
  where = Minitest.filter_backtrace(caller).first
993
- where = where.split(/:in /, 2).first # clean up noise
1132
+ where = where.split(":in ", 2).first # clean up noise
994
1133
  warn "DEPRECATED: `maglev?` called from #{where}. This will fail in Minitest 6."
995
1134
  "maglev" == platform
996
1135
  end
@@ -999,14 +1138,14 @@ module Minitest
999
1138
  # Is this running on mri?
1000
1139
 
1001
1140
  def mri? platform = RUBY_DESCRIPTION
1002
- /^ruby/ =~ platform
1141
+ platform.start_with? "ruby"
1003
1142
  end
1004
1143
 
1005
1144
  ##
1006
1145
  # Is this running on macOS?
1007
1146
 
1008
1147
  def osx? platform = RUBY_PLATFORM
1009
- /darwin/ =~ platform
1148
+ platform.include? "darwin"
1010
1149
  end
1011
1150
 
1012
1151
  ##
@@ -1014,7 +1153,7 @@ module Minitest
1014
1153
 
1015
1154
  def rubinius? platform = defined?(RUBY_ENGINE) && RUBY_ENGINE
1016
1155
  where = Minitest.filter_backtrace(caller).first
1017
- where = where.split(/:in /, 2).first # clean up noise
1156
+ where = where.split(":in ", 2).first # clean up noise
1018
1157
  warn "DEPRECATED: `rubinius?` called from #{where}. This will fail in Minitest 6."
1019
1158
  "rbx" == platform
1020
1159
  end
@@ -1023,7 +1162,7 @@ module Minitest
1023
1162
  # Is this running on windows?
1024
1163
 
1025
1164
  def windows? platform = RUBY_PLATFORM
1026
- /mswin|mingw/ =~ platform
1165
+ /mswin|mingw/.match? platform
1027
1166
  end
1028
1167
  end
1029
1168
 
@@ -1034,7 +1173,16 @@ module Minitest
1034
1173
 
1035
1174
  class BacktraceFilter
1036
1175
 
1037
- 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
1038
1186
 
1039
1187
  ##
1040
1188
  # Filter +bt+ to something useful. Returns the whole thing if
@@ -1045,9 +1193,9 @@ module Minitest
1045
1193
 
1046
1194
  return bt.dup if $DEBUG || ENV["MT_DEBUG"]
1047
1195
 
1048
- new_bt = bt.take_while { |line| line !~ MT_RE }
1049
- new_bt = bt.select { |line| line !~ MT_RE } if new_bt.empty?
1050
- 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?
1051
1199
 
1052
1200
  new_bt
1053
1201
  end