minitest 5.14.4 → 5.25.5

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,58 +1,76 @@
1
1
  require "optparse"
2
- require "thread"
3
- require "mutex_m"
4
- require "minitest/parallel"
5
2
  require "stringio"
3
+ require "etc"
4
+
5
+ require_relative "minitest/parallel"
6
+ require_relative "minitest/compress"
6
7
 
7
8
  ##
8
- # :include: README.rdoc
9
+ # The top-level namespace for Minitest. Also the location of the main
10
+ # runtime. See +Minitest.run+ for more information.
9
11
 
10
12
  module Minitest
11
- VERSION = "5.14.4" # :nodoc:
12
- ENCS = "".respond_to? :encoding # :nodoc:
13
+ VERSION = "5.25.5" # :nodoc:
13
14
 
14
15
  @@installed_at_exit ||= false
15
16
  @@after_run = []
16
17
  @extensions = []
17
18
 
18
- mc = (class << self; self; end)
19
+ def self.cattr_accessor name # :nodoc:
20
+ (class << self; self; end).attr_accessor name
21
+ end
22
+
23
+ ##
24
+ # The random seed used for this run. This is used to srand at the
25
+ # start of the run and between each +Runnable.run+.
26
+ #
27
+ # Set via Minitest.run after processing args.
28
+
29
+ cattr_accessor :seed
19
30
 
20
31
  ##
21
32
  # Parallel test executor
22
33
 
23
- mc.send :attr_accessor, :parallel_executor
34
+ cattr_accessor :parallel_executor
35
+
36
+ warn "DEPRECATED: use MT_CPU instead of N for parallel test runs" if ENV["N"] && ENV["N"].to_i > 0
37
+ n_threads = (ENV["MT_CPU"] || ENV["N"] || Etc.nprocessors).to_i
24
38
 
25
- warn "DEPRECATED: use MT_CPU instead of N for parallel test runs" if ENV["N"]
26
- n_threads = (ENV["MT_CPU"] || ENV["N"] || 2).to_i
27
39
  self.parallel_executor = Parallel::Executor.new n_threads
28
40
 
29
41
  ##
30
42
  # Filter object for backtraces.
31
43
 
32
- mc.send :attr_accessor, :backtrace_filter
44
+ cattr_accessor :backtrace_filter
33
45
 
34
46
  ##
35
47
  # Reporter object to be used for all runs.
36
48
  #
37
49
  # NOTE: This accessor is only available during setup, not during runs.
38
50
 
39
- mc.send :attr_accessor, :reporter
51
+ cattr_accessor :reporter
40
52
 
41
53
  ##
42
54
  # Names of known extension plugins.
43
55
 
44
- mc.send :attr_accessor, :extensions
56
+ cattr_accessor :extensions
45
57
 
46
58
  ##
47
59
  # The signal to use for dumping information to STDERR. Defaults to "INFO".
48
60
 
49
- mc.send :attr_accessor, :info_signal
61
+ cattr_accessor :info_signal
50
62
  self.info_signal = "INFO"
51
63
 
64
+ cattr_accessor :allow_fork
65
+ self.allow_fork = false
66
+
52
67
  ##
53
68
  # Registers Minitest to run at process exit
54
69
 
55
70
  def self.autorun
71
+ Warning[:deprecated] = true if
72
+ Object.const_defined?(:Warning) && Warning.respond_to?(:[]=)
73
+
56
74
  at_exit {
57
75
  next if $! and not ($!.kind_of? SystemExit and $!.success?)
58
76
 
@@ -60,7 +78,7 @@ module Minitest
60
78
 
61
79
  pid = Process.pid
62
80
  at_exit {
63
- next if Process.pid != pid
81
+ next if !Minitest.allow_fork && Process.pid != pid
64
82
  @@after_run.reverse_each(&:call)
65
83
  exit exit_code || false
66
84
  }
@@ -80,20 +98,19 @@ module Minitest
80
98
  @@after_run << block
81
99
  end
82
100
 
83
- def self.init_plugins options # :nodoc:
84
- self.extensions.each do |name|
85
- msg = "plugin_#{name}_init"
86
- send msg, options if self.respond_to? msg
87
- end
101
+ ##
102
+ # Register a plugin to be used. Does NOT require / load it.
103
+
104
+ def self.register_plugin name_or_mod
105
+ self.extensions << name_or_mod
106
+ nil
88
107
  end
89
108
 
90
109
  def self.load_plugins # :nodoc:
91
- return unless self.extensions.empty?
110
+ return unless defined? Gem
92
111
 
93
112
  seen = {}
94
113
 
95
- require "rubygems" unless defined? Gem
96
-
97
114
  Gem.find_files("minitest/*_plugin.rb").each do |plugin_path|
98
115
  name = File.basename plugin_path, "_plugin.rb"
99
116
 
@@ -105,69 +122,27 @@ module Minitest
105
122
  end
106
123
  end
107
124
 
108
- ##
109
- # This is the top-level run method. Everything starts from here. It
110
- # tells each Runnable sub-class to run, and each of those are
111
- # responsible for doing whatever they do.
112
- #
113
- # The overall structure of a run looks like this:
114
- #
115
- # Minitest.autorun
116
- # Minitest.run(args)
117
- # Minitest.__run(reporter, options)
118
- # Runnable.runnables.each
119
- # runnable.run(reporter, options)
120
- # self.runnable_methods.each
121
- # self.run_one_method(self, runnable_method, reporter)
122
- # Minitest.run_one_method(klass, runnable_method)
123
- # klass.new(runnable_method).run
124
-
125
- def self.run args = []
126
- self.load_plugins unless args.delete("--no-plugins") || ENV["MT_NO_PLUGINS"]
127
-
128
- options = process_args args
129
-
130
- reporter = CompositeReporter.new
131
- reporter << SummaryReporter.new(options[:io], options)
132
- reporter << ProgressReporter.new(options[:io], options)
133
-
134
- self.reporter = reporter # this makes it available to plugins
135
- self.init_plugins options
136
- self.reporter = nil # runnables shouldn't depend on the reporter, ever
137
-
138
- self.parallel_executor.start if parallel_executor.respond_to?(:start)
139
- reporter.start
140
- begin
141
- __run reporter, options
142
- rescue Interrupt
143
- warn "Interrupted. Exiting..."
125
+ def self.init_plugins options # :nodoc:
126
+ self.extensions.each do |mod_or_meth|
127
+ case mod_or_meth
128
+ when Symbol, String then
129
+ name = mod_or_meth
130
+ msg = "plugin_#{name}_init"
131
+ next unless self.respond_to? msg
132
+ send msg, options
133
+ when Module then
134
+ recv = mod_or_meth
135
+ next unless recv.respond_to? :minitest_plugin_init
136
+ recv.minitest_plugin_init options
137
+ else
138
+ raise ArgumentError, "plugin is %p, but it must be a symbol, string or module" % [mod_or_meth]
139
+ end
144
140
  end
145
- self.parallel_executor.shutdown
146
- reporter.report
147
-
148
- reporter.passed?
149
- end
150
-
151
- ##
152
- # Internal run method. Responsible for telling all Runnable
153
- # sub-classes to run.
154
-
155
- def self.__run reporter, options
156
- suites = Runnable.runnables.reject { |s| s.runnable_methods.empty? }.shuffle
157
- parallel, serial = suites.partition { |s| s.test_order == :parallel }
158
-
159
- # If we run the parallel tests before the serial tests, the parallel tests
160
- # could run in parallel with the serial tests. This would be bad because
161
- # the serial tests won't lock around Reporter#record. Run the serial tests
162
- # first, so that after they complete, the parallel tests will lock when
163
- # recording results.
164
- serial.map { |suite| suite.run reporter, options } +
165
- parallel.map { |suite| suite.run reporter, options }
166
141
  end
167
142
 
168
143
  def self.process_args args = [] # :nodoc:
169
144
  options = {
170
- :io => $stdout,
145
+ :io => $stdout,
171
146
  }
172
147
  orig_args = args.dup
173
148
 
@@ -191,6 +166,14 @@ module Minitest
191
166
  options[:verbose] = true
192
167
  end
193
168
 
169
+ opts.on "-q", "--quiet", "Quiet. Show no progress processing files." do
170
+ options[:quiet] = true
171
+ end
172
+
173
+ opts.on "--show-skips", "Show skipped at the end of run." do
174
+ options[:show_skips] = true
175
+ end
176
+
194
177
  opts.on "-n", "--name PATTERN", "Filter run on /regexp/ or string." do |a|
195
178
  options[:filter] = a
196
179
  end
@@ -199,13 +182,41 @@ module Minitest
199
182
  options[:exclude] = a
200
183
  end
201
184
 
185
+ opts.on "-S", "--skip CODES", String, "Skip reporting of certain types of results (eg E)." do |s|
186
+ options[:skip] = s.chars.to_a
187
+ end
188
+
189
+ ruby27plus = ::Warning.respond_to? :[]=
190
+
191
+ opts.on "-W[error]", String, "Turn Ruby warnings into errors" do |s|
192
+ options[:Werror] = true
193
+ case s
194
+ when "error", "all", nil then
195
+ require "minitest/error_on_warning"
196
+ $VERBOSE = true
197
+ ::Warning[:deprecated] = true if ruby27plus
198
+ else
199
+ ::Warning[s.to_sym] = true if ruby27plus # check validity of category
200
+ end
201
+ end
202
+
202
203
  unless extensions.empty?
203
204
  opts.separator ""
204
- opts.separator "Known extensions: #{extensions.join(", ")}"
205
-
206
- extensions.each do |meth|
207
- msg = "plugin_#{meth}_options"
208
- send msg, opts, options if self.respond_to?(msg)
205
+ opts.separator "Known extensions: #{extensions.join ", "}"
206
+
207
+ extensions.each do |mod_or_meth|
208
+ case mod_or_meth
209
+ when Symbol, String then
210
+ meth = mod_or_meth
211
+ msg = "plugin_#{meth}_options"
212
+ send msg, opts, options if respond_to? msg
213
+ when Module
214
+ recv = mod_or_meth
215
+ next unless recv.respond_to? :minitest_plugin_options
216
+ recv.minitest_plugin_options opts, options
217
+ else
218
+ raise ArgumentError, "plugin is %p, but it must be a symbol, string or module" % [mod_or_meth]
219
+ end
209
220
  end
210
221
  end
211
222
 
@@ -228,15 +239,100 @@ module Minitest
228
239
  orig_args << "--seed" << options[:seed].to_s
229
240
  end
230
241
 
231
- srand options[:seed]
232
-
233
242
  options[:args] = orig_args.map { |s|
234
- s =~ /[\s|&<>$()]/ ? s.inspect : s
243
+ s.match?(/[\s|&<>$()]/) ? s.inspect : s
235
244
  }.join " "
236
245
 
237
246
  options
238
247
  end
239
248
 
249
+ ##
250
+ # This is the top-level run method. Everything starts from here. It
251
+ # tells each Runnable sub-class to run, and each of those are
252
+ # responsible for doing whatever they do.
253
+ #
254
+ # The overall structure of a run looks like this:
255
+ #
256
+ # Minitest.autorun
257
+ # Minitest.run(args)
258
+ # Minitest.load_plugins
259
+ # Minitest.process_args
260
+ # Minitest.init_plugins
261
+ # Minitest.__run(reporter, options)
262
+ # Runnable.runnables.each
263
+ # runnable_klass.run(reporter, options)
264
+ # self.runnable_methods.each
265
+ # self.run_one_method(self, runnable_method, reporter)
266
+ # Minitest.run_one_method(klass, runnable_method)
267
+ # klass.new(runnable_method).run
268
+
269
+ def self.run args = []
270
+ self.load_plugins unless args.delete("--no-plugins") || ENV["MT_NO_PLUGINS"]
271
+
272
+ options = process_args args
273
+
274
+ Minitest.seed = options[:seed]
275
+ srand Minitest.seed
276
+
277
+ reporter = CompositeReporter.new
278
+ reporter << SummaryReporter.new(options[:io], options)
279
+ reporter << ProgressReporter.new(options[:io], options) unless options[:quiet]
280
+
281
+ self.reporter = reporter # this makes it available to plugins
282
+ self.init_plugins options
283
+ self.reporter = nil # runnables shouldn't depend on the reporter, ever
284
+
285
+ self.parallel_executor.start if parallel_executor.respond_to? :start
286
+ reporter.start
287
+ begin
288
+ __run reporter, options
289
+ rescue Interrupt
290
+ warn "Interrupted. Exiting..."
291
+ end
292
+ self.parallel_executor.shutdown
293
+
294
+ # might have been removed/replaced during init_plugins:
295
+ summary = reporter.reporters.grep(SummaryReporter).first
296
+
297
+ reporter.report
298
+
299
+ return empty_run! options if summary && summary.count == 0
300
+ reporter.passed?
301
+ end
302
+
303
+ def self.empty_run! options # :nodoc:
304
+ filter = options[:filter]
305
+ return true unless filter # no filter, but nothing ran == success
306
+
307
+ warn "Nothing ran for filter: %s" % [filter]
308
+
309
+ require "did_you_mean" # soft dependency, punt if it doesn't load
310
+
311
+ ms = Runnable.runnables.flat_map(&:runnable_methods)
312
+ cs = DidYouMean::SpellChecker.new(dictionary: ms).correct filter
313
+
314
+ warn DidYouMean::Formatter.message_for cs unless cs.empty?
315
+ rescue LoadError
316
+ # do nothing
317
+ end
318
+
319
+ ##
320
+ # Internal run method. Responsible for telling all Runnable
321
+ # sub-classes to run.
322
+
323
+ def self.__run reporter, options
324
+ suites = Runnable.runnables.shuffle
325
+ parallel, serial = suites.partition { |s| s.test_order == :parallel }
326
+
327
+ # If we run the parallel tests before the serial tests, the parallel tests
328
+ # could run in parallel with the serial tests. This would be bad because
329
+ # the serial tests won't lock around Reporter#record. Run the serial tests
330
+ # first, so that after they complete, the parallel tests will lock when
331
+ # recording results.
332
+ serial.map { |suite| suite.run reporter, options } +
333
+ parallel.map { |suite| suite.run reporter, options }
334
+ end
335
+
240
336
  def self.filter_backtrace bt # :nodoc:
241
337
  result = backtrace_filter.filter bt
242
338
  result = bt.dup if result.empty?
@@ -307,24 +403,34 @@ module Minitest
307
403
  # reporter to record.
308
404
 
309
405
  def self.run reporter, options = {}
310
- filter = options[:filter] || "/./"
311
- filter = Regexp.new $1 if filter.is_a?(String) && filter =~ %r%/(.*)/%
406
+ pos = options[:filter]
407
+ neg = options[:exclude]
312
408
 
313
- filtered_methods = self.runnable_methods.find_all { |m|
314
- filter === m || filter === "#{self}##{m}"
315
- }
316
-
317
- exclude = options[:exclude]
318
- exclude = Regexp.new $1 if exclude =~ %r%/(.*)/%
409
+ pos = Regexp.new $1 if pos.kind_of?(String) && pos =~ %r%/(.*)/%
410
+ neg = Regexp.new $1 if neg.kind_of?(String) && neg =~ %r%/(.*)/%
319
411
 
320
- filtered_methods.delete_if { |m|
321
- exclude === m || exclude === "#{self}##{m}"
322
- }
412
+ filtered_methods = self.runnable_methods
413
+ .select { |m| !pos || pos === m || pos === "#{self}##{m}" }
414
+ .reject { |m| neg && (neg === m || neg === "#{self}##{m}") }
323
415
 
324
416
  return if filtered_methods.empty?
325
417
 
418
+ t0 = name = nil
419
+
420
+ @_info_handler = lambda do
421
+ unless reporter.passed? then
422
+ warn "Current results:"
423
+ warn reporter.reporters.grep(SummaryReporter).first
424
+ end
425
+
426
+ warn "Current: %s#%s %.2fs" % [self, name, Minitest.clock_time - t0]
427
+ end
428
+
326
429
  with_info_handler reporter do
327
430
  filtered_methods.each do |method_name|
431
+ name = method_name
432
+ t0 = Minitest.clock_time
433
+
328
434
  run_one_method self, method_name, reporter
329
435
  end
330
436
  end
@@ -341,17 +447,16 @@ module Minitest
341
447
  reporter.record Minitest.run_one_method(klass, method_name)
342
448
  end
343
449
 
344
- def self.with_info_handler reporter, &block # :nodoc:
345
- handler = lambda do
346
- unless reporter.passed? then
347
- warn "Current results:"
348
- warn ""
349
- warn reporter.reporters.first
350
- warn ""
351
- end
352
- end
450
+ ##
451
+ # Defines the order to run tests (:random by default). Override
452
+ # this or use a convenience method to change it for your tests.
453
+
454
+ def self.test_order
455
+ :random
456
+ end
353
457
 
354
- on_signal ::Minitest.info_signal, handler, &block
458
+ def self.with_info_handler reporter, &block # :nodoc:
459
+ on_signal ::Minitest.info_signal, @_info_handler, &block
355
460
  end
356
461
 
357
462
  SIGNALS = Signal.list # :nodoc:
@@ -389,7 +494,7 @@ module Minitest
389
494
  def marshal_dump # :nodoc:
390
495
  unless @@marshal_dump_warned then
391
496
  warn ["Minitest::Runnable#marshal_dump is deprecated.",
392
- "You might be violating internals. From", caller.first].join " "
497
+ "You might be violating internals. From", caller(1..1).first].join " "
393
498
  @@marshal_dump_warned = true
394
499
  end
395
500
 
@@ -408,6 +513,31 @@ module Minitest
408
513
  self.name = name
409
514
  self.failures = []
410
515
  self.assertions = 0
516
+ # lazy initializer for metadata
517
+ end
518
+
519
+ ##
520
+ # Metadata you attach to the test results that get sent to the reporter.
521
+ #
522
+ # Lazily initializes to a hash, to keep memory down.
523
+ #
524
+ # NOTE: this data *must* be plain (read: marshal-able) data!
525
+ # Hashes! Arrays! Strings!
526
+
527
+ def metadata
528
+ @metadata ||= {}
529
+ end
530
+
531
+ ##
532
+ # Sets metadata, mainly used for +Result.from+.
533
+
534
+ attr_writer :metadata
535
+
536
+ ##
537
+ # Returns true if metadata exists.
538
+
539
+ def metadata?
540
+ defined? @metadata
411
541
  end
412
542
 
413
543
  ##
@@ -459,12 +589,14 @@ module Minitest
459
589
  not self.failure
460
590
  end
461
591
 
592
+ BASE_DIR = "#{Dir.pwd}/" # :nodoc:
593
+
462
594
  ##
463
595
  # The location identifier of this test. Depends on a method
464
596
  # existing called class_name.
465
597
 
466
598
  def location
467
- loc = " [#{self.failure.location}]" unless passed? or error?
599
+ loc = " [#{self.failure.location.delete_prefix BASE_DIR}]" unless passed? or error?
468
600
  "#{self.class_name}##{self.name}#{loc}"
469
601
  end
470
602
 
@@ -490,7 +622,7 @@ module Minitest
490
622
  # Did this run error?
491
623
 
492
624
  def error?
493
- self.failures.any? { |f| UnexpectedError === f }
625
+ self.failures.any? UnexpectedError
494
626
  end
495
627
  end
496
628
 
@@ -528,6 +660,7 @@ module Minitest
528
660
  r.assertions = o.assertions
529
661
  r.failures = o.failures.dup
530
662
  r.time = o.time
663
+ r.metadata = o.metadata if o.metadata?
531
664
 
532
665
  r.source_location = o.method(o.name).source_location rescue ["unknown", -1]
533
666
 
@@ -552,7 +685,10 @@ module Minitest
552
685
  # you want. Go nuts.
553
686
 
554
687
  class AbstractReporter
555
- include Mutex_m
688
+
689
+ def initialize # :nodoc:
690
+ @mutex = Mutex.new
691
+ end
556
692
 
557
693
  ##
558
694
  # Starts reporting on the run.
@@ -588,6 +724,10 @@ module Minitest
588
724
  def passed?
589
725
  true
590
726
  end
727
+
728
+ def synchronize &block # :nodoc:
729
+ @mutex.synchronize(&block)
730
+ end
591
731
  end
592
732
 
593
733
  class Reporter < AbstractReporter # :nodoc:
@@ -617,11 +757,11 @@ module Minitest
617
757
  # own.
618
758
 
619
759
  class ProgressReporter < Reporter
620
- def prerecord klass, name #:nodoc:
621
- if options[:verbose] then
622
- io.print "%s#%s = " % [klass.name, name]
623
- io.flush
624
- end
760
+ def prerecord klass, name # :nodoc:
761
+ return unless options[:verbose]
762
+
763
+ io.print "%s#%s = " % [klass.name, name]
764
+ io.flush
625
765
  end
626
766
 
627
767
  def record result # :nodoc:
@@ -691,6 +831,11 @@ module Minitest
691
831
 
692
832
  attr_accessor :errors
693
833
 
834
+ ##
835
+ # Total number of tests that warned.
836
+
837
+ attr_accessor :warnings
838
+
694
839
  ##
695
840
  # Total number of tests that where skipped.
696
841
 
@@ -706,6 +851,7 @@ module Minitest
706
851
  self.total_time = nil
707
852
  self.failures = nil
708
853
  self.errors = nil
854
+ self.warnings = nil
709
855
  self.skips = nil
710
856
  end
711
857
 
@@ -734,6 +880,7 @@ module Minitest
734
880
  self.total_time = Minitest.clock_time - start_time
735
881
  self.failures = aggregate[Assertion].size
736
882
  self.errors = aggregate[UnexpectedError].size
883
+ self.warnings = aggregate[UnexpectedWarning].size
737
884
  self.skips = aggregate[Skip].size
738
885
  end
739
886
  end
@@ -748,10 +895,8 @@ module Minitest
748
895
  # own.
749
896
 
750
897
  class SummaryReporter < StatisticsReporter
751
- # :stopdoc:
752
- attr_accessor :sync
753
- attr_accessor :old_sync
754
- # :startdoc:
898
+ attr_accessor :sync # :nodoc:
899
+ attr_accessor :old_sync # :nodoc:
755
900
 
756
901
  def start # :nodoc:
757
902
  super
@@ -761,7 +906,7 @@ module Minitest
761
906
  io.puts "# Running:"
762
907
  io.puts
763
908
 
764
- self.sync = io.respond_to? :"sync=" # stupid emacs
909
+ self.sync = io.respond_to? :"sync="
765
910
  self.old_sync, io.sync = io.sync, true if self.sync
766
911
  end
767
912
 
@@ -784,9 +929,14 @@ module Minitest
784
929
 
785
930
  def aggregated_results io # :nodoc:
786
931
  filtered_results = results.dup
787
- filtered_results.reject!(&:skipped?) unless options[:verbose]
932
+ filtered_results.reject!(&:skipped?) unless
933
+ options[:verbose] or options[:show_skips]
934
+
935
+ skip = options[:skip] || []
788
936
 
789
937
  filtered_results.each_with_index { |result, i|
938
+ next if skip.include? result.result_code
939
+
790
940
  io.puts "\n%3d) %s" % [i+1, result]
791
941
  }
792
942
  io.puts
@@ -794,25 +944,22 @@ module Minitest
794
944
  end
795
945
 
796
946
  def to_s # :nodoc:
797
- aggregated_results(StringIO.new(binary_string)).string
947
+ aggregated_results(StringIO.new("".b)).string
798
948
  end
799
949
 
800
950
  def summary # :nodoc:
801
- extra = ""
951
+ extra = []
802
952
 
803
- extra = "\n\nYou have skipped tests. Run with --verbose for details." if
804
- results.any?(&:skipped?) unless options[:verbose] or ENV["MT_NO_SKIP_MSG"]
953
+ extra << ", %d warnings" % [warnings] if options[:Werror]
805
954
 
806
- "%d runs, %d assertions, %d failures, %d errors, %d skips%s" %
807
- [count, assertions, failures, errors, skips, extra]
808
- end
809
-
810
- private
955
+ extra << "\n\nYou have skipped tests. Run with --verbose for details." if
956
+ results.any?(&:skipped?) unless
957
+ options[:verbose] or
958
+ options[:show_skips] or
959
+ ENV["MT_NO_SKIP_MSG"]
811
960
 
812
- if '<3'.respond_to? :b
813
- def binary_string; ''.b; end
814
- else
815
- def binary_string; ''.force_encoding(Encoding::ASCII_8BIT); end
961
+ "%d runs, %d assertions, %d failures, %d errors, %d skips%s" %
962
+ [count, assertions, failures, errors, skips, extra.join]
816
963
  end
817
964
  end
818
965
 
@@ -871,6 +1018,8 @@ module Minitest
871
1018
  # Represents run failures.
872
1019
 
873
1020
  class Assertion < Exception
1021
+ RE = /in [`'](?:[^']+[#.])?(?:assert|refute|flunk|pass|fail|raise|must|wont)/ # :nodoc:
1022
+
874
1023
  def error # :nodoc:
875
1024
  self
876
1025
  end
@@ -879,12 +1028,11 @@ module Minitest
879
1028
  # Where was this run before an assertion was raised?
880
1029
 
881
1030
  def location
882
- last_before_assertion = ""
883
- self.backtrace.reverse_each do |s|
884
- break if s =~ /in .(assert|refute|flunk|pass|fail|raise|must|wont)/
885
- last_before_assertion = s
886
- end
887
- last_before_assertion.sub(/:in .*$/, "")
1031
+ bt = Minitest.filter_backtrace self.backtrace
1032
+ idx = bt.rindex { |s| s.match? RE } || -1 # fall back to first item
1033
+ loc = bt[idx+1] || bt.last || "unknown:-1"
1034
+
1035
+ loc.sub(/:in .*$/, "")
888
1036
  end
889
1037
 
890
1038
  def result_code # :nodoc:
@@ -909,11 +1057,21 @@ module Minitest
909
1057
  # Assertion wrapping an unexpected error that was raised during a run.
910
1058
 
911
1059
  class UnexpectedError < Assertion
1060
+ include Minitest::Compress
1061
+
912
1062
  # TODO: figure out how to use `cause` instead
913
1063
  attr_accessor :error # :nodoc:
914
1064
 
915
1065
  def initialize error # :nodoc:
916
1066
  super "Unexpected exception"
1067
+
1068
+ if SystemStackError === error then
1069
+ bt = error.backtrace
1070
+ new_bt = compress bt
1071
+ error = error.exception "#{bt.size} -> #{new_bt.size}"
1072
+ error.set_backtrace new_bt
1073
+ end
1074
+
917
1075
  self.error = error
918
1076
  end
919
1077
 
@@ -921,8 +1079,11 @@ module Minitest
921
1079
  self.error.backtrace
922
1080
  end
923
1081
 
1082
+ BASE_RE = %r%#{Dir.pwd}/% # :nodoc:
1083
+
924
1084
  def message # :nodoc:
925
- bt = Minitest.filter_backtrace(self.backtrace).join "\n "
1085
+ bt = Minitest.filter_backtrace(self.backtrace).join("\n ")
1086
+ .gsub(BASE_RE, "")
926
1087
  "#{self.error.class}: #{self.error.message}\n #{bt}"
927
1088
  end
928
1089
 
@@ -931,6 +1092,15 @@ module Minitest
931
1092
  end
932
1093
  end
933
1094
 
1095
+ ##
1096
+ # Assertion raised on warning when running in -Werror mode.
1097
+
1098
+ class UnexpectedWarning < Assertion
1099
+ def result_label # :nodoc:
1100
+ "Warning"
1101
+ end
1102
+ end
1103
+
934
1104
  ##
935
1105
  # Provides a simple set of guards that you can use in your tests
936
1106
  # to skip execution if it is not applicable. These methods are
@@ -960,7 +1130,7 @@ module Minitest
960
1130
 
961
1131
  def maglev? platform = defined?(RUBY_ENGINE) && RUBY_ENGINE
962
1132
  where = Minitest.filter_backtrace(caller).first
963
- where = where.split(/:in /, 2).first # clean up noise
1133
+ where = where.split(":in ", 2).first # clean up noise
964
1134
  warn "DEPRECATED: `maglev?` called from #{where}. This will fail in Minitest 6."
965
1135
  "maglev" == platform
966
1136
  end
@@ -969,14 +1139,14 @@ module Minitest
969
1139
  # Is this running on mri?
970
1140
 
971
1141
  def mri? platform = RUBY_DESCRIPTION
972
- /^ruby/ =~ platform
1142
+ platform.start_with? "ruby"
973
1143
  end
974
1144
 
975
1145
  ##
976
1146
  # Is this running on macOS?
977
1147
 
978
1148
  def osx? platform = RUBY_PLATFORM
979
- /darwin/ =~ platform
1149
+ platform.include? "darwin"
980
1150
  end
981
1151
 
982
1152
  ##
@@ -984,7 +1154,7 @@ module Minitest
984
1154
 
985
1155
  def rubinius? platform = defined?(RUBY_ENGINE) && RUBY_ENGINE
986
1156
  where = Minitest.filter_backtrace(caller).first
987
- where = where.split(/:in /, 2).first # clean up noise
1157
+ where = where.split(":in ", 2).first # clean up noise
988
1158
  warn "DEPRECATED: `rubinius?` called from #{where}. This will fail in Minitest 6."
989
1159
  "rbx" == platform
990
1160
  end
@@ -993,7 +1163,7 @@ module Minitest
993
1163
  # Is this running on windows?
994
1164
 
995
1165
  def windows? platform = RUBY_PLATFORM
996
- /mswin|mingw/ =~ platform
1166
+ /mswin|mingw/.match? platform
997
1167
  end
998
1168
  end
999
1169
 
@@ -1004,7 +1174,16 @@ module Minitest
1004
1174
 
1005
1175
  class BacktraceFilter
1006
1176
 
1007
- MT_RE = %r%lib/minitest% #:nodoc:
1177
+ MT_RE = %r%lib/minitest|internal:warning% # :nodoc:
1178
+
1179
+ ##
1180
+ # The regular expression to use to filter backtraces. Defaults to +MT_RE+.
1181
+
1182
+ attr_accessor :regexp
1183
+
1184
+ def initialize regexp = MT_RE # :nodoc:
1185
+ self.regexp = regexp
1186
+ end
1008
1187
 
1009
1188
  ##
1010
1189
  # Filter +bt+ to something useful. Returns the whole thing if
@@ -1015,9 +1194,9 @@ module Minitest
1015
1194
 
1016
1195
  return bt.dup if $DEBUG || ENV["MT_DEBUG"]
1017
1196
 
1018
- new_bt = bt.take_while { |line| line !~ MT_RE }
1019
- new_bt = bt.select { |line| line !~ MT_RE } if new_bt.empty?
1020
- new_bt = bt.dup if new_bt.empty?
1197
+ new_bt = bt.take_while { |line| !regexp.match? line.to_s }
1198
+ new_bt = bt.select { |line| !regexp.match? line.to_s } if new_bt.empty?
1199
+ new_bt = bt.dup if new_bt.empty?
1021
1200
 
1022
1201
  new_bt
1023
1202
  end