minitest 5.11.3 → 5.25.4

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,61 +1,84 @@
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.11.3" # :nodoc:
12
- ENCS = "".respond_to? :encoding # :nodoc:
13
+ VERSION = "5.25.4" # :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
24
- self.parallel_executor = Parallel::Executor.new((ENV["N"] || 2).to_i)
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
38
+
39
+ self.parallel_executor = Parallel::Executor.new n_threads
25
40
 
26
41
  ##
27
42
  # Filter object for backtraces.
28
43
 
29
- mc.send :attr_accessor, :backtrace_filter
44
+ cattr_accessor :backtrace_filter
30
45
 
31
46
  ##
32
47
  # Reporter object to be used for all runs.
33
48
  #
34
49
  # NOTE: This accessor is only available during setup, not during runs.
35
50
 
36
- mc.send :attr_accessor, :reporter
51
+ cattr_accessor :reporter
37
52
 
38
53
  ##
39
54
  # Names of known extension plugins.
40
55
 
41
- mc.send :attr_accessor, :extensions
56
+ cattr_accessor :extensions
42
57
 
43
58
  ##
44
59
  # The signal to use for dumping information to STDERR. Defaults to "INFO".
45
60
 
46
- mc.send :attr_accessor, :info_signal
61
+ cattr_accessor :info_signal
47
62
  self.info_signal = "INFO"
48
63
 
64
+ cattr_accessor :allow_fork
65
+ self.allow_fork = false
66
+
49
67
  ##
50
68
  # Registers Minitest to run at process exit
51
69
 
52
70
  def self.autorun
71
+ Warning[:deprecated] = true if
72
+ Object.const_defined?(:Warning) && Warning.respond_to?(:[]=)
73
+
53
74
  at_exit {
54
75
  next if $! and not ($!.kind_of? SystemExit and $!.success?)
55
76
 
56
77
  exit_code = nil
57
78
 
79
+ pid = Process.pid
58
80
  at_exit {
81
+ next if !Minitest.allow_fork && Process.pid != pid
59
82
  @@after_run.reverse_each(&:call)
60
83
  exit exit_code || false
61
84
  }
@@ -75,20 +98,19 @@ module Minitest
75
98
  @@after_run << block
76
99
  end
77
100
 
78
- def self.init_plugins options # :nodoc:
79
- self.extensions.each do |name|
80
- msg = "plugin_#{name}_init"
81
- send msg, options if self.respond_to? msg
82
- 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
83
107
  end
84
108
 
85
109
  def self.load_plugins # :nodoc:
86
- return unless self.extensions.empty?
110
+ return unless defined? Gem
87
111
 
88
112
  seen = {}
89
113
 
90
- require "rubygems" unless defined? Gem
91
-
92
114
  Gem.find_files("minitest/*_plugin.rb").each do |plugin_path|
93
115
  name = File.basename plugin_path, "_plugin.rb"
94
116
 
@@ -100,69 +122,27 @@ module Minitest
100
122
  end
101
123
  end
102
124
 
103
- ##
104
- # This is the top-level run method. Everything starts from here. It
105
- # tells each Runnable sub-class to run, and each of those are
106
- # responsible for doing whatever they do.
107
- #
108
- # The overall structure of a run looks like this:
109
- #
110
- # Minitest.autorun
111
- # Minitest.run(args)
112
- # Minitest.__run(reporter, options)
113
- # Runnable.runnables.each
114
- # runnable.run(reporter, options)
115
- # self.runnable_methods.each
116
- # self.run_one_method(self, runnable_method, reporter)
117
- # Minitest.run_one_method(klass, runnable_method)
118
- # klass.new(runnable_method).run
119
-
120
- def self.run args = []
121
- self.load_plugins unless args.delete("--no-plugins") || ENV["MT_NO_PLUGINS"]
122
-
123
- options = process_args args
124
-
125
- reporter = CompositeReporter.new
126
- reporter << SummaryReporter.new(options[:io], options)
127
- reporter << ProgressReporter.new(options[:io], options)
128
-
129
- self.reporter = reporter # this makes it available to plugins
130
- self.init_plugins options
131
- self.reporter = nil # runnables shouldn't depend on the reporter, ever
132
-
133
- self.parallel_executor.start if parallel_executor.respond_to?(:start)
134
- reporter.start
135
- begin
136
- __run reporter, options
137
- rescue Interrupt
138
- 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
139
140
  end
140
- self.parallel_executor.shutdown
141
- reporter.report
142
-
143
- reporter.passed?
144
- end
145
-
146
- ##
147
- # Internal run method. Responsible for telling all Runnable
148
- # sub-classes to run.
149
-
150
- def self.__run reporter, options
151
- suites = Runnable.runnables.reject { |s| s.runnable_methods.empty? }.shuffle
152
- parallel, serial = suites.partition { |s| s.test_order == :parallel }
153
-
154
- # If we run the parallel tests before the serial tests, the parallel tests
155
- # could run in parallel with the serial tests. This would be bad because
156
- # the serial tests won't lock around Reporter#record. Run the serial tests
157
- # first, so that after they complete, the parallel tests will lock when
158
- # recording results.
159
- serial.map { |suite| suite.run reporter, options } +
160
- parallel.map { |suite| suite.run reporter, options }
161
141
  end
162
142
 
163
143
  def self.process_args args = [] # :nodoc:
164
144
  options = {
165
- :io => $stdout,
145
+ :io => $stdout,
166
146
  }
167
147
  orig_args = args.dup
168
148
 
@@ -186,6 +166,14 @@ module Minitest
186
166
  options[:verbose] = true
187
167
  end
188
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
+
189
177
  opts.on "-n", "--name PATTERN", "Filter run on /regexp/ or string." do |a|
190
178
  options[:filter] = a
191
179
  end
@@ -194,13 +182,41 @@ module Minitest
194
182
  options[:exclude] = a
195
183
  end
196
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
+
197
203
  unless extensions.empty?
198
204
  opts.separator ""
199
- opts.separator "Known extensions: #{extensions.join(", ")}"
200
-
201
- extensions.each do |meth|
202
- msg = "plugin_#{meth}_options"
203
- 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
204
220
  end
205
221
  end
206
222
 
@@ -223,17 +239,104 @@ module Minitest
223
239
  orig_args << "--seed" << options[:seed].to_s
224
240
  end
225
241
 
226
- srand options[:seed]
227
-
228
242
  options[:args] = orig_args.map { |s|
229
- s =~ /[\s|&<>$()]/ ? s.inspect : s
243
+ s.match?(/[\s|&<>$()]/) ? s.inspect : s
230
244
  }.join " "
231
245
 
232
246
  options
233
247
  end
234
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
+
235
336
  def self.filter_backtrace bt # :nodoc:
236
- backtrace_filter.filter bt
337
+ result = backtrace_filter.filter bt
338
+ result = bt.dup if result.empty?
339
+ result
237
340
  end
238
341
 
239
342
  ##
@@ -300,24 +403,34 @@ module Minitest
300
403
  # reporter to record.
301
404
 
302
405
  def self.run reporter, options = {}
303
- filter = options[:filter] || "/./"
304
- filter = Regexp.new $1 if filter =~ %r%/(.*)/%
305
-
306
- filtered_methods = self.runnable_methods.find_all { |m|
307
- filter === m || filter === "#{self}##{m}"
308
- }
406
+ pos = options[:filter]
407
+ neg = options[:exclude]
309
408
 
310
- exclude = options[:exclude]
311
- 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%/(.*)/%
312
411
 
313
- filtered_methods.delete_if { |m|
314
- exclude === m || exclude === "#{self}##{m}"
315
- }
412
+ filtered_methods = self.runnable_methods
413
+ .select { |m| !pos || pos === m || pos === "#{self}##{m}" }
414
+ .reject { |m| neg && (neg === m || neg === "#{self}##{m}") }
316
415
 
317
416
  return if filtered_methods.empty?
318
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
+
319
429
  with_info_handler reporter do
320
430
  filtered_methods.each do |method_name|
431
+ name = method_name
432
+ t0 = Minitest.clock_time
433
+
321
434
  run_one_method self, method_name, reporter
322
435
  end
323
436
  end
@@ -334,17 +447,16 @@ module Minitest
334
447
  reporter.record Minitest.run_one_method(klass, method_name)
335
448
  end
336
449
 
337
- def self.with_info_handler reporter, &block # :nodoc:
338
- handler = lambda do
339
- unless reporter.passed? then
340
- warn "Current results:"
341
- warn ""
342
- warn reporter.reporters.first
343
- warn ""
344
- end
345
- 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
346
457
 
347
- 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
348
460
  end
349
461
 
350
462
  SIGNALS = Signal.list # :nodoc:
@@ -382,7 +494,7 @@ module Minitest
382
494
  def marshal_dump # :nodoc:
383
495
  unless @@marshal_dump_warned then
384
496
  warn ["Minitest::Runnable#marshal_dump is deprecated.",
385
- "You might be violating internals. From", caller.first].join " "
497
+ "You might be violating internals. From", caller(1..1).first].join " "
386
498
  @@marshal_dump_warned = true
387
499
  end
388
500
 
@@ -401,6 +513,31 @@ module Minitest
401
513
  self.name = name
402
514
  self.failures = []
403
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
404
541
  end
405
542
 
406
543
  ##
@@ -422,7 +559,8 @@ module Minitest
422
559
 
423
560
  ##
424
561
  # Returns a single character string to print based on the result
425
- # of the run. Eg ".", "F", or "E".
562
+ # of the run. One of <tt>"."</tt>, <tt>"F"</tt>,
563
+ # <tt>"E"</tt> or <tt>"S"</tt>.
426
564
 
427
565
  def result_code
428
566
  raise NotImplementedError, "subclass responsibility"
@@ -451,12 +589,14 @@ module Minitest
451
589
  not self.failure
452
590
  end
453
591
 
592
+ BASE_DIR = "#{Dir.pwd}/" # :nodoc:
593
+
454
594
  ##
455
595
  # The location identifier of this test. Depends on a method
456
596
  # existing called class_name.
457
597
 
458
598
  def location
459
- loc = " [#{self.failure.location}]" unless passed? or error?
599
+ loc = " [#{self.failure.location.delete_prefix BASE_DIR}]" unless passed? or error?
460
600
  "#{self.class_name}##{self.name}#{loc}"
461
601
  end
462
602
 
@@ -482,7 +622,7 @@ module Minitest
482
622
  # Did this run error?
483
623
 
484
624
  def error?
485
- self.failures.any? { |f| UnexpectedError === f }
625
+ self.failures.any? UnexpectedError
486
626
  end
487
627
  end
488
628
 
@@ -520,6 +660,7 @@ module Minitest
520
660
  r.assertions = o.assertions
521
661
  r.failures = o.failures.dup
522
662
  r.time = o.time
663
+ r.metadata = o.metadata if o.metadata?
523
664
 
524
665
  r.source_location = o.method(o.name).source_location rescue ["unknown", -1]
525
666
 
@@ -544,7 +685,10 @@ module Minitest
544
685
  # you want. Go nuts.
545
686
 
546
687
  class AbstractReporter
547
- include Mutex_m
688
+
689
+ def initialize # :nodoc:
690
+ @mutex = Mutex.new
691
+ end
548
692
 
549
693
  ##
550
694
  # Starts reporting on the run.
@@ -560,8 +704,10 @@ module Minitest
560
704
  end
561
705
 
562
706
  ##
563
- # Record a result and output the Runnable#result_code. Stores the
564
- # result of the run if the run did not pass.
707
+ # Output and record the result of the test. Call
708
+ # {result#result_code}[rdoc-ref:Runnable#result_code] to get the
709
+ # result character string. Stores the result of the run if the run
710
+ # did not pass.
565
711
 
566
712
  def record result
567
713
  end
@@ -578,6 +724,10 @@ module Minitest
578
724
  def passed?
579
725
  true
580
726
  end
727
+
728
+ def synchronize &block # :nodoc:
729
+ @mutex.synchronize(&block)
730
+ end
581
731
  end
582
732
 
583
733
  class Reporter < AbstractReporter # :nodoc:
@@ -607,11 +757,11 @@ module Minitest
607
757
  # own.
608
758
 
609
759
  class ProgressReporter < Reporter
610
- def prerecord klass, name #:nodoc:
611
- if options[:verbose] then
612
- io.print "%s#%s = " % [klass.name, name]
613
- io.flush
614
- end
760
+ def prerecord klass, name # :nodoc:
761
+ return unless options[:verbose]
762
+
763
+ io.print "%s#%s = " % [klass.name, name]
764
+ io.flush
615
765
  end
616
766
 
617
767
  def record result # :nodoc:
@@ -628,18 +778,68 @@ module Minitest
628
778
  #
629
779
  # If you want to create an entirely different type of output (eg,
630
780
  # CI, HTML, etc), this is the place to start.
781
+ #
782
+ # Example:
783
+ #
784
+ # class JenkinsCIReporter < StatisticsReporter
785
+ # def report
786
+ # super # Needed to calculate some statistics
787
+ #
788
+ # print "<testsuite "
789
+ # print "tests='#{count}' "
790
+ # print "failures='#{failures}' "
791
+ # # Remaining XML...
792
+ # end
793
+ # end
631
794
 
632
795
  class StatisticsReporter < Reporter
633
- # :stopdoc:
796
+ ##
797
+ # Total number of assertions.
798
+
634
799
  attr_accessor :assertions
800
+
801
+ ##
802
+ # Total number of test cases.
803
+
635
804
  attr_accessor :count
805
+
806
+ ##
807
+ # An +Array+ of test cases that failed or were skipped.
808
+
636
809
  attr_accessor :results
810
+
811
+ ##
812
+ # Time the test run started. If available, the monotonic clock is
813
+ # used and this is a +Float+, otherwise it's an instance of
814
+ # +Time+.
815
+
637
816
  attr_accessor :start_time
817
+
818
+ ##
819
+ # Test run time. If available, the monotonic clock is used and
820
+ # this is a +Float+, otherwise it's an instance of +Time+.
821
+
638
822
  attr_accessor :total_time
823
+
824
+ ##
825
+ # Total number of tests that failed.
826
+
639
827
  attr_accessor :failures
828
+
829
+ ##
830
+ # Total number of tests that erred.
831
+
640
832
  attr_accessor :errors
833
+
834
+ ##
835
+ # Total number of tests that warned.
836
+
837
+ attr_accessor :warnings
838
+
839
+ ##
840
+ # Total number of tests that where skipped.
841
+
641
842
  attr_accessor :skips
642
- # :startdoc:
643
843
 
644
844
  def initialize io = $stdout, options = {} # :nodoc:
645
845
  super
@@ -651,6 +851,7 @@ module Minitest
651
851
  self.total_time = nil
652
852
  self.failures = nil
653
853
  self.errors = nil
854
+ self.warnings = nil
654
855
  self.skips = nil
655
856
  end
656
857
 
@@ -669,13 +870,17 @@ module Minitest
669
870
  results << result if not result.passed? or result.skipped?
670
871
  end
671
872
 
672
- def report # :nodoc:
873
+ ##
874
+ # Report on the tracked statistics.
875
+
876
+ def report
673
877
  aggregate = results.group_by { |r| r.failure.class }
674
878
  aggregate.default = [] # dumb. group_by should provide this
675
879
 
676
880
  self.total_time = Minitest.clock_time - start_time
677
881
  self.failures = aggregate[Assertion].size
678
882
  self.errors = aggregate[UnexpectedError].size
883
+ self.warnings = aggregate[UnexpectedWarning].size
679
884
  self.skips = aggregate[Skip].size
680
885
  end
681
886
  end
@@ -690,10 +895,8 @@ module Minitest
690
895
  # own.
691
896
 
692
897
  class SummaryReporter < StatisticsReporter
693
- # :stopdoc:
694
- attr_accessor :sync
695
- attr_accessor :old_sync
696
- # :startdoc:
898
+ attr_accessor :sync # :nodoc:
899
+ attr_accessor :old_sync # :nodoc:
697
900
 
698
901
  def start # :nodoc:
699
902
  super
@@ -703,7 +906,7 @@ module Minitest
703
906
  io.puts "# Running:"
704
907
  io.puts
705
908
 
706
- self.sync = io.respond_to? :"sync=" # stupid emacs
909
+ self.sync = io.respond_to? :"sync="
707
910
  self.old_sync, io.sync = io.sync, true if self.sync
708
911
  end
709
912
 
@@ -726,9 +929,14 @@ module Minitest
726
929
 
727
930
  def aggregated_results io # :nodoc:
728
931
  filtered_results = results.dup
729
- 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] || []
730
936
 
731
937
  filtered_results.each_with_index { |result, i|
938
+ next if skip.include? result.result_code
939
+
732
940
  io.puts "\n%3d) %s" % [i+1, result]
733
941
  }
734
942
  io.puts
@@ -736,25 +944,22 @@ module Minitest
736
944
  end
737
945
 
738
946
  def to_s # :nodoc:
739
- aggregated_results(StringIO.new(binary_string)).string
947
+ aggregated_results(StringIO.new("".b)).string
740
948
  end
741
949
 
742
950
  def summary # :nodoc:
743
- extra = ""
744
-
745
- extra = "\n\nYou have skipped tests. Run with --verbose for details." if
746
- results.any?(&:skipped?) unless options[:verbose] or ENV["MT_NO_SKIP_MSG"]
951
+ extra = []
747
952
 
748
- "%d runs, %d assertions, %d failures, %d errors, %d skips%s" %
749
- [count, assertions, failures, errors, skips, extra]
750
- end
953
+ extra << ", %d warnings" % [warnings] if options[:Werror]
751
954
 
752
- 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"]
753
960
 
754
- if '<3'.respond_to? :b
755
- def binary_string; ''.b; end
756
- else
757
- 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]
758
963
  end
759
964
  end
760
965
 
@@ -813,6 +1018,8 @@ module Minitest
813
1018
  # Represents run failures.
814
1019
 
815
1020
  class Assertion < Exception
1021
+ RE = /in [`'](?:[^']+[#.])?(?:assert|refute|flunk|pass|fail|raise|must|wont)/ # :nodoc:
1022
+
816
1023
  def error # :nodoc:
817
1024
  self
818
1025
  end
@@ -821,12 +1028,11 @@ module Minitest
821
1028
  # Where was this run before an assertion was raised?
822
1029
 
823
1030
  def location
824
- last_before_assertion = ""
825
- self.backtrace.reverse_each do |s|
826
- break if s =~ /in .(assert|refute|flunk|pass|fail|raise|must|wont)/
827
- last_before_assertion = s
828
- end
829
- 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 .*$/, "")
830
1036
  end
831
1037
 
832
1038
  def result_code # :nodoc:
@@ -851,24 +1057,34 @@ module Minitest
851
1057
  # Assertion wrapping an unexpected error that was raised during a run.
852
1058
 
853
1059
  class UnexpectedError < Assertion
854
- attr_accessor :exception # :nodoc:
1060
+ include Minitest::Compress
855
1061
 
856
- def initialize exception # :nodoc:
1062
+ # TODO: figure out how to use `cause` instead
1063
+ attr_accessor :error # :nodoc:
1064
+
1065
+ def initialize error # :nodoc:
857
1066
  super "Unexpected exception"
858
- self.exception = 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
+
1075
+ self.error = error
859
1076
  end
860
1077
 
861
1078
  def backtrace # :nodoc:
862
- self.exception.backtrace
1079
+ self.error.backtrace
863
1080
  end
864
1081
 
865
- def error # :nodoc:
866
- self.exception
867
- end
1082
+ BASE_RE = %r%#{Dir.pwd}/% # :nodoc:
868
1083
 
869
1084
  def message # :nodoc:
870
- bt = Minitest.filter_backtrace(self.backtrace).join "\n "
871
- "#{self.exception.class}: #{self.exception.message}\n #{bt}"
1085
+ bt = Minitest.filter_backtrace(self.backtrace).join("\n ")
1086
+ .gsub(BASE_RE, "")
1087
+ "#{self.error.class}: #{self.error.message}\n #{bt}"
872
1088
  end
873
1089
 
874
1090
  def result_label # :nodoc:
@@ -876,6 +1092,15 @@ module Minitest
876
1092
  end
877
1093
  end
878
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
+
879
1104
  ##
880
1105
  # Provides a simple set of guards that you can use in your tests
881
1106
  # to skip execution if it is not applicable. These methods are
@@ -904,6 +1129,9 @@ module Minitest
904
1129
  # Is this running on maglev?
905
1130
 
906
1131
  def maglev? platform = defined?(RUBY_ENGINE) && RUBY_ENGINE
1132
+ where = Minitest.filter_backtrace(caller).first
1133
+ where = where.split(":in ", 2).first # clean up noise
1134
+ warn "DEPRECATED: `maglev?` called from #{where}. This will fail in Minitest 6."
907
1135
  "maglev" == platform
908
1136
  end
909
1137
 
@@ -911,13 +1139,23 @@ module Minitest
911
1139
  # Is this running on mri?
912
1140
 
913
1141
  def mri? platform = RUBY_DESCRIPTION
914
- /^ruby/ =~ platform
1142
+ platform.start_with? "ruby"
1143
+ end
1144
+
1145
+ ##
1146
+ # Is this running on macOS?
1147
+
1148
+ def osx? platform = RUBY_PLATFORM
1149
+ platform.include? "darwin"
915
1150
  end
916
1151
 
917
1152
  ##
918
1153
  # Is this running on rubinius?
919
1154
 
920
1155
  def rubinius? platform = defined?(RUBY_ENGINE) && RUBY_ENGINE
1156
+ where = Minitest.filter_backtrace(caller).first
1157
+ where = where.split(":in ", 2).first # clean up noise
1158
+ warn "DEPRECATED: `rubinius?` called from #{where}. This will fail in Minitest 6."
921
1159
  "rbx" == platform
922
1160
  end
923
1161
 
@@ -925,7 +1163,7 @@ module Minitest
925
1163
  # Is this running on windows?
926
1164
 
927
1165
  def windows? platform = RUBY_PLATFORM
928
- /mswin|mingw/ =~ platform
1166
+ /mswin|mingw/.match? platform
929
1167
  end
930
1168
  end
931
1169
 
@@ -936,19 +1174,29 @@ module Minitest
936
1174
 
937
1175
  class BacktraceFilter
938
1176
 
939
- 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
940
1187
 
941
1188
  ##
942
- # Filter +bt+ to something useful. Returns the whole thing if $DEBUG.
1189
+ # Filter +bt+ to something useful. Returns the whole thing if
1190
+ # $DEBUG (ruby) or $MT_DEBUG (env).
943
1191
 
944
1192
  def filter bt
945
1193
  return ["No backtrace"] unless bt
946
1194
 
947
- return bt.dup if $DEBUG
1195
+ return bt.dup if $DEBUG || ENV["MT_DEBUG"]
948
1196
 
949
- new_bt = bt.take_while { |line| line !~ MT_RE }
950
- new_bt = bt.select { |line| line !~ MT_RE } if new_bt.empty?
951
- 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?
952
1200
 
953
1201
  new_bt
954
1202
  end