minitest 5.11.3 → 5.27.0

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,83 @@
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.26.2" # :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 if n_threads > 1
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
72
+
53
73
  at_exit {
54
74
  next if $! and not ($!.kind_of? SystemExit and $!.success?)
55
75
 
56
76
  exit_code = nil
57
77
 
78
+ pid = Process.pid
58
79
  at_exit {
80
+ next if !Minitest.allow_fork && Process.pid != pid
59
81
  @@after_run.reverse_each(&:call)
60
82
  exit exit_code || false
61
83
  }
@@ -75,20 +97,19 @@ module Minitest
75
97
  @@after_run << block
76
98
  end
77
99
 
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
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
83
106
  end
84
107
 
85
108
  def self.load_plugins # :nodoc:
86
- return unless self.extensions.empty?
109
+ return unless defined? Gem
87
110
 
88
111
  seen = {}
89
112
 
90
- require "rubygems" unless defined? Gem
91
-
92
113
  Gem.find_files("minitest/*_plugin.rb").each do |plugin_path|
93
114
  name = File.basename plugin_path, "_plugin.rb"
94
115
 
@@ -100,93 +121,65 @@ module Minitest
100
121
  end
101
122
  end
102
123
 
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..."
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
139
139
  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
140
  end
162
141
 
163
142
  def self.process_args args = [] # :nodoc:
164
143
  options = {
165
- :io => $stdout,
144
+ :io => $stdout,
166
145
  }
167
146
  orig_args = args.dup
168
147
 
169
148
  OptionParser.new do |opts|
170
- opts.banner = "minitest options:"
149
+ opts.program_name = "minitest"
171
150
  opts.version = Minitest::VERSION
172
151
 
152
+ opts.banner = [
153
+ "Usage: rake test [A=options] (see Minitest::TestTask for more options)",
154
+ "minitest [paths] [options] (with minitest-sprint gem)",
155
+ "ruby path/to/test.rb [options]\n\n",
156
+ ].join "\n or: "
157
+
173
158
  opts.on "-h", "--help", "Display this help." do
174
159
  puts opts
175
160
  exit
176
161
  end
177
162
 
178
- opts.on "--no-plugins", "Bypass minitest plugin auto-loading (or set $MT_NO_PLUGINS)."
163
+ opts.on "--no-plugins", "Bypass minitest plugin auto-loading (or env: MT_NO_PLUGINS=1)."
179
164
 
180
- desc = "Sets random seed. Also via env. Eg: SEED=n rake"
165
+ desc = "Sets random seed. Also via env, eg: SEED=42"
181
166
  opts.on "-s", "--seed SEED", Integer, desc do |m|
182
- options[:seed] = m.to_i
167
+ options[:seed] = m
183
168
  end
184
169
 
185
- opts.on "-v", "--verbose", "Verbose. Show progress processing files." do
170
+ opts.on "-v", "--verbose", "Verbose. Print each name as they run." do
186
171
  options[:verbose] = true
187
172
  end
188
173
 
189
- opts.on "-n", "--name PATTERN", "Filter run on /regexp/ or string." do |a|
174
+ opts.on "-q", "--quiet", "Quiet. Show no dots while processing files." do
175
+ options[:quiet] = true
176
+ end
177
+
178
+ opts.on "--show-skips", "Show skipped at the end of run." do
179
+ options[:show_skips] = true
180
+ end
181
+
182
+ opts.on "-n", "--name PATTERN", "Include /regexp/ or string for run." do |a|
190
183
  options[:filter] = a
191
184
  end
192
185
 
@@ -194,13 +187,48 @@ module Minitest
194
187
  options[:exclude] = a
195
188
  end
196
189
 
190
+ # part of my unofficial embedded gem "makeoptparseworkwell"
191
+ def opts.topdict(name) = (name.length > 1 ? top.long : top.short)
192
+ def opts.alias(from, to) = (dict = topdict(from) ; dict[to] = dict[from])
193
+
194
+ # these will work but won't show up in --help output:
195
+ opts.alias "name", "include"
196
+ opts.alias "n", "i"
197
+ opts.alias "e", "x"
198
+
199
+ opts.on "-S", "--skip CODES", String, "Skip reporting of certain types of results (eg E)." do |s|
200
+ options[:skip] = s.chars.to_a
201
+ end
202
+
203
+ opts.on "-W[error]", String, "Turn Ruby warnings into errors" do |s|
204
+ options[:Werror] = true
205
+ case s
206
+ when "error", "all", nil then
207
+ require_relative "minitest/error_on_warning"
208
+ $VERBOSE = true
209
+ ::Warning[:deprecated] = true
210
+ else
211
+ ::Warning[s.to_sym] = true # check validity of category
212
+ end
213
+ end
214
+
197
215
  unless extensions.empty?
198
216
  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)
217
+ opts.separator "Known extensions: #{extensions.join ", "}"
218
+
219
+ extensions.each do |mod_or_meth|
220
+ case mod_or_meth
221
+ when Symbol, String then
222
+ meth = mod_or_meth
223
+ msg = "plugin_#{meth}_options"
224
+ send msg, opts, options if respond_to? msg
225
+ when Module
226
+ recv = mod_or_meth
227
+ next unless recv.respond_to? :minitest_plugin_options
228
+ recv.minitest_plugin_options opts, options
229
+ else
230
+ raise ArgumentError, "plugin is %p, but it must be a symbol, string or module" % [mod_or_meth]
231
+ end
204
232
  end
205
233
  end
206
234
 
@@ -223,17 +251,106 @@ module Minitest
223
251
  orig_args << "--seed" << options[:seed].to_s
224
252
  end
225
253
 
226
- srand options[:seed]
227
-
228
254
  options[:args] = orig_args.map { |s|
229
- s =~ /[\s|&<>$()]/ ? s.inspect : s
255
+ s.match?(/[\s|&<>$()]/) ? s.inspect : s
230
256
  }.join " "
231
257
 
232
258
  options
233
259
  end
234
260
 
261
+ ##
262
+ # This is the top-level run method. Everything starts from here. It
263
+ # tells each Runnable sub-class to run, and each of those are
264
+ # responsible for doing whatever they do.
265
+ #
266
+ # The overall structure of a run looks like this:
267
+ #
268
+ # Minitest.autorun
269
+ # Minitest.run(args)
270
+ # Minitest.load_plugins
271
+ # Minitest.process_args
272
+ # Minitest.init_plugins
273
+ # Minitest.__run(reporter, options)
274
+ # Runnable.runnables.each |runnable_klass|
275
+ # runnable_klass.run(reporter, options)
276
+ # filtered_methods = runnable_methods.select {...}.reject {...}
277
+ # filtered_methods.each |runnable_method|
278
+ # runnable_klass.run_one_method(self, runnable_method, reporter)
279
+ # Minitest.run_one_method(runnable_klass, runnable_method)
280
+ # runnable_klass.new(runnable_method).run
281
+
282
+ def self.run args = []
283
+ self.load_plugins unless args.delete("--no-plugins") || ENV["MT_NO_PLUGINS"]
284
+
285
+ options = process_args args
286
+
287
+ Minitest.seed = options[:seed]
288
+ srand Minitest.seed
289
+
290
+ reporter = CompositeReporter.new
291
+ reporter << SummaryReporter.new(options[:io], options)
292
+ reporter << ProgressReporter.new(options[:io], options) unless options[:quiet]
293
+
294
+ self.reporter = reporter # this makes it available to plugins
295
+ self.init_plugins options
296
+ self.reporter = nil # runnables shouldn't depend on the reporter, ever
297
+
298
+ self.parallel_executor.start if parallel_executor.respond_to? :start
299
+ reporter.start
300
+ begin
301
+ __run reporter, options
302
+ finished = true
303
+ rescue Interrupt
304
+ warn "Interrupted. Exiting..."
305
+ end
306
+ self.parallel_executor.shutdown if parallel_executor.respond_to? :shutdown
307
+
308
+ # might have been removed/replaced during init_plugins:
309
+ summary = reporter.reporters.grep(SummaryReporter).first
310
+
311
+ reporter.report
312
+
313
+ return empty_run! options if finished && summary && summary.count == 0
314
+ finished and reporter.passed?
315
+ end
316
+
317
+ def self.empty_run! options # :nodoc:
318
+ filter = options[:filter]
319
+ return true unless filter # no filter, but nothing ran == success
320
+
321
+ warn "Nothing ran for filter: %s" % [filter]
322
+
323
+ require "did_you_mean" # soft dependency, punt if it doesn't load
324
+
325
+ ms = Runnable.runnables.flat_map(&:runnable_methods)
326
+ cs = DidYouMean::SpellChecker.new(dictionary: ms).correct filter
327
+
328
+ warn DidYouMean::Formatter.message_for cs unless cs.empty?
329
+ rescue LoadError
330
+ # do nothing
331
+ end
332
+
333
+ ##
334
+ # Internal run method. Responsible for telling all Runnable
335
+ # sub-classes to run.
336
+
337
+ def self.__run reporter, options
338
+ suites = Runnable.runnables.shuffle
339
+ parallel, serial = suites.partition { |s| s.test_order == :parallel }
340
+
341
+ # If we run the parallel tests before the serial tests, the parallel tests
342
+ # could run in parallel with the serial tests. This would be bad because
343
+ # the serial tests won't lock around Reporter#record. Run the serial tests
344
+ # first, so that after they complete, the parallel tests will lock when
345
+ # recording results.
346
+ serial.map { |suite| suite.run reporter, options } +
347
+ parallel.map { |suite| suite.run reporter, options }
348
+ end
349
+
235
350
  def self.filter_backtrace bt # :nodoc:
236
- backtrace_filter.filter bt
351
+ result = backtrace_filter.filter bt
352
+ result = bt.dup if result.empty?
353
+ result
237
354
  end
238
355
 
239
356
  ##
@@ -300,24 +417,35 @@ module Minitest
300
417
  # reporter to record.
301
418
 
302
419
  def self.run reporter, options = {}
303
- filter = options[:filter] || "/./"
304
- filter = Regexp.new $1 if filter =~ %r%/(.*)/%
420
+ pos = options[:filter]
421
+ neg = options[:exclude]
305
422
 
306
- filtered_methods = self.runnable_methods.find_all { |m|
307
- filter === m || filter === "#{self}##{m}"
308
- }
309
-
310
- exclude = options[:exclude]
311
- exclude = Regexp.new $1 if exclude =~ %r%/(.*)/%
423
+ pos = Regexp.new $1 if pos.kind_of?(String) && pos =~ %r%/(.*)/%
424
+ neg = Regexp.new $1 if neg.kind_of?(String) && neg =~ %r%/(.*)/%
312
425
 
313
- filtered_methods.delete_if { |m|
314
- exclude === m || exclude === "#{self}##{m}"
315
- }
426
+ # at most 1-2% slower than a 1-pass version, stop optimizing this
427
+ filtered_methods = self.runnable_methods
428
+ .select { |m| !pos || pos === m || pos === "#{self}##{m}" }
429
+ .reject { |m| neg && (neg === m || neg === "#{self}##{m}") }
316
430
 
317
431
  return if filtered_methods.empty?
318
432
 
433
+ t0 = name = nil
434
+
435
+ @_info_handler = lambda do
436
+ unless reporter.passed? then
437
+ warn "Current results:"
438
+ warn reporter.reporters.grep(SummaryReporter).first
439
+ end
440
+
441
+ warn "Current: %s#%s %.2fs" % [self, name, Minitest.clock_time - t0]
442
+ end
443
+
319
444
  with_info_handler reporter do
320
445
  filtered_methods.each do |method_name|
446
+ name = method_name
447
+ t0 = Minitest.clock_time
448
+
321
449
  run_one_method self, method_name, reporter
322
450
  end
323
451
  end
@@ -334,17 +462,16 @@ module Minitest
334
462
  reporter.record Minitest.run_one_method(klass, method_name)
335
463
  end
336
464
 
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
465
+ ##
466
+ # Defines the order to run tests (:random by default). Override
467
+ # this or use a convenience method to change it for your tests.
468
+
469
+ def self.test_order
470
+ :random
471
+ end
346
472
 
347
- on_signal ::Minitest.info_signal, handler, &block
473
+ def self.with_info_handler reporter, &block # :nodoc:
474
+ on_signal ::Minitest.info_signal, @_info_handler, &block
348
475
  end
349
476
 
350
477
  SIGNALS = Signal.list # :nodoc:
@@ -382,7 +509,7 @@ module Minitest
382
509
  def marshal_dump # :nodoc:
383
510
  unless @@marshal_dump_warned then
384
511
  warn ["Minitest::Runnable#marshal_dump is deprecated.",
385
- "You might be violating internals. From", caller.first].join " "
512
+ "You might be violating internals. From", caller(1..1).first].join " "
386
513
  @@marshal_dump_warned = true
387
514
  end
388
515
 
@@ -401,6 +528,31 @@ module Minitest
401
528
  self.name = name
402
529
  self.failures = []
403
530
  self.assertions = 0
531
+ # lazy initializer for metadata
532
+ end
533
+
534
+ ##
535
+ # Metadata you attach to the test results that get sent to the reporter.
536
+ #
537
+ # Lazily initializes to a hash, to keep memory down.
538
+ #
539
+ # NOTE: this data *must* be plain (read: marshal-able) data!
540
+ # Hashes! Arrays! Strings!
541
+
542
+ def metadata
543
+ @metadata ||= {}
544
+ end
545
+
546
+ ##
547
+ # Sets metadata, mainly used for +Result.from+.
548
+
549
+ attr_writer :metadata
550
+
551
+ ##
552
+ # Returns true if metadata exists.
553
+
554
+ def metadata?
555
+ defined? @metadata
404
556
  end
405
557
 
406
558
  ##
@@ -422,7 +574,8 @@ module Minitest
422
574
 
423
575
  ##
424
576
  # Returns a single character string to print based on the result
425
- # of the run. Eg ".", "F", or "E".
577
+ # of the run. One of <tt>"."</tt>, <tt>"F"</tt>,
578
+ # <tt>"E"</tt> or <tt>"S"</tt>.
426
579
 
427
580
  def result_code
428
581
  raise NotImplementedError, "subclass responsibility"
@@ -434,7 +587,7 @@ module Minitest
434
587
  def skipped?
435
588
  raise NotImplementedError, "subclass responsibility"
436
589
  end
437
- end
590
+ end # Runnable
438
591
 
439
592
  ##
440
593
  # Shared code for anything that can get passed to a Reporter. See
@@ -451,12 +604,14 @@ module Minitest
451
604
  not self.failure
452
605
  end
453
606
 
607
+ BASE_DIR = "#{Dir.pwd}/" # :nodoc:
608
+
454
609
  ##
455
610
  # The location identifier of this test. Depends on a method
456
611
  # existing called class_name.
457
612
 
458
613
  def location
459
- loc = " [#{self.failure.location}]" unless passed? or error?
614
+ loc = " [#{self.failure.location.delete_prefix BASE_DIR}]" unless passed? or error?
460
615
  "#{self.class_name}##{self.name}#{loc}"
461
616
  end
462
617
 
@@ -482,9 +637,9 @@ module Minitest
482
637
  # Did this run error?
483
638
 
484
639
  def error?
485
- self.failures.any? { |f| UnexpectedError === f }
640
+ self.failures.any? UnexpectedError
486
641
  end
487
- end
642
+ end # Reportable
488
643
 
489
644
  ##
490
645
  # This represents a test result in a clean way that can be
@@ -520,6 +675,7 @@ module Minitest
520
675
  r.assertions = o.assertions
521
676
  r.failures = o.failures.dup
522
677
  r.time = o.time
678
+ r.metadata = o.metadata if o.metadata?
523
679
 
524
680
  r.source_location = o.method(o.name).source_location rescue ["unknown", -1]
525
681
 
@@ -537,14 +693,17 @@ module Minitest
537
693
  "#{failure.result_label}:\n#{self.location}:\n#{failure.message}\n"
538
694
  }.join "\n"
539
695
  end
540
- end
696
+ end # Result
541
697
 
542
698
  ##
543
699
  # Defines the API for Reporters. Subclass this and override whatever
544
700
  # you want. Go nuts.
545
701
 
546
702
  class AbstractReporter
547
- include Mutex_m
703
+
704
+ def initialize # :nodoc:
705
+ @mutex = Mutex.new
706
+ end
548
707
 
549
708
  ##
550
709
  # Starts reporting on the run.
@@ -560,8 +719,10 @@ module Minitest
560
719
  end
561
720
 
562
721
  ##
563
- # Record a result and output the Runnable#result_code. Stores the
564
- # result of the run if the run did not pass.
722
+ # Output and record the result of the test. Call
723
+ # {result#result_code}[rdoc-ref:Runnable#result_code] to get the
724
+ # result character string. Stores the result of the run if the run
725
+ # did not pass.
565
726
 
566
727
  def record result
567
728
  end
@@ -578,7 +739,11 @@ module Minitest
578
739
  def passed?
579
740
  true
580
741
  end
581
- end
742
+
743
+ def synchronize &block # :nodoc:
744
+ @mutex.synchronize(&block)
745
+ end
746
+ end # AbstractReportera
582
747
 
583
748
  class Reporter < AbstractReporter # :nodoc:
584
749
  ##
@@ -607,11 +772,11 @@ module Minitest
607
772
  # own.
608
773
 
609
774
  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
775
+ def prerecord klass, name # :nodoc:
776
+ return unless options[:verbose]
777
+
778
+ io.print "%s#%s = " % [klass.name, name]
779
+ io.flush
615
780
  end
616
781
 
617
782
  def record result # :nodoc:
@@ -628,18 +793,68 @@ module Minitest
628
793
  #
629
794
  # If you want to create an entirely different type of output (eg,
630
795
  # CI, HTML, etc), this is the place to start.
796
+ #
797
+ # Example:
798
+ #
799
+ # class JenkinsCIReporter < StatisticsReporter
800
+ # def report
801
+ # super # Needed to calculate some statistics
802
+ #
803
+ # print "<testsuite "
804
+ # print "tests='#{count}' "
805
+ # print "failures='#{failures}' "
806
+ # # Remaining XML...
807
+ # end
808
+ # end
631
809
 
632
810
  class StatisticsReporter < Reporter
633
- # :stopdoc:
811
+ ##
812
+ # Total number of assertions.
813
+
634
814
  attr_accessor :assertions
815
+
816
+ ##
817
+ # Total number of test cases.
818
+
635
819
  attr_accessor :count
820
+
821
+ ##
822
+ # An +Array+ of test cases that failed or were skipped.
823
+
636
824
  attr_accessor :results
825
+
826
+ ##
827
+ # Time the test run started. If available, the monotonic clock is
828
+ # used and this is a +Float+, otherwise it's an instance of
829
+ # +Time+.
830
+
637
831
  attr_accessor :start_time
832
+
833
+ ##
834
+ # Test run time. If available, the monotonic clock is used and
835
+ # this is a +Float+, otherwise it's an instance of +Time+.
836
+
638
837
  attr_accessor :total_time
838
+
839
+ ##
840
+ # Total number of tests that failed.
841
+
639
842
  attr_accessor :failures
843
+
844
+ ##
845
+ # Total number of tests that erred.
846
+
640
847
  attr_accessor :errors
848
+
849
+ ##
850
+ # Total number of tests that warned.
851
+
852
+ attr_accessor :warnings
853
+
854
+ ##
855
+ # Total number of tests that where skipped.
856
+
641
857
  attr_accessor :skips
642
- # :startdoc:
643
858
 
644
859
  def initialize io = $stdout, options = {} # :nodoc:
645
860
  super
@@ -651,6 +866,7 @@ module Minitest
651
866
  self.total_time = nil
652
867
  self.failures = nil
653
868
  self.errors = nil
869
+ self.warnings = nil
654
870
  self.skips = nil
655
871
  end
656
872
 
@@ -669,16 +885,20 @@ module Minitest
669
885
  results << result if not result.passed? or result.skipped?
670
886
  end
671
887
 
672
- def report # :nodoc:
888
+ ##
889
+ # Report on the tracked statistics.
890
+
891
+ def report
673
892
  aggregate = results.group_by { |r| r.failure.class }
674
893
  aggregate.default = [] # dumb. group_by should provide this
675
894
 
676
895
  self.total_time = Minitest.clock_time - start_time
677
896
  self.failures = aggregate[Assertion].size
678
897
  self.errors = aggregate[UnexpectedError].size
898
+ self.warnings = aggregate[UnexpectedWarning].size
679
899
  self.skips = aggregate[Skip].size
680
900
  end
681
- end
901
+ end # StatisticsReporter
682
902
 
683
903
  ##
684
904
  # A reporter that prints the header, summary, and failure details at
@@ -690,10 +910,8 @@ module Minitest
690
910
  # own.
691
911
 
692
912
  class SummaryReporter < StatisticsReporter
693
- # :stopdoc:
694
- attr_accessor :sync
695
- attr_accessor :old_sync
696
- # :startdoc:
913
+ attr_accessor :sync # :nodoc:
914
+ attr_accessor :old_sync # :nodoc:
697
915
 
698
916
  def start # :nodoc:
699
917
  super
@@ -703,7 +921,7 @@ module Minitest
703
921
  io.puts "# Running:"
704
922
  io.puts
705
923
 
706
- self.sync = io.respond_to? :"sync=" # stupid emacs
924
+ self.sync = io.respond_to? :"sync="
707
925
  self.old_sync, io.sync = io.sync, true if self.sync
708
926
  end
709
927
 
@@ -726,9 +944,14 @@ module Minitest
726
944
 
727
945
  def aggregated_results io # :nodoc:
728
946
  filtered_results = results.dup
729
- filtered_results.reject!(&:skipped?) unless options[:verbose]
947
+ filtered_results.reject!(&:skipped?) unless
948
+ options[:verbose] or options[:show_skips]
949
+
950
+ skip = options[:skip] || []
730
951
 
731
952
  filtered_results.each_with_index { |result, i|
953
+ next if skip.include? result.result_code
954
+
732
955
  io.puts "\n%3d) %s" % [i+1, result]
733
956
  }
734
957
  io.puts
@@ -736,27 +959,24 @@ module Minitest
736
959
  end
737
960
 
738
961
  def to_s # :nodoc:
739
- aggregated_results(StringIO.new(binary_string)).string
962
+ aggregated_results(StringIO.new("".b)).string
740
963
  end
741
964
 
742
965
  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"]
966
+ extra = []
747
967
 
748
- "%d runs, %d assertions, %d failures, %d errors, %d skips%s" %
749
- [count, assertions, failures, errors, skips, extra]
750
- end
968
+ extra << ", %d warnings" % [warnings] if options[:Werror]
751
969
 
752
- private
970
+ extra << "\n\nYou have skipped tests. Run with --verbose for details." if
971
+ results.any?(&:skipped?) unless
972
+ options[:verbose] or
973
+ options[:show_skips] or
974
+ ENV["MT_NO_SKIP_MSG"]
753
975
 
754
- if '<3'.respond_to? :b
755
- def binary_string; ''.b; end
756
- else
757
- def binary_string; ''.force_encoding(Encoding::ASCII_8BIT); end
976
+ "%d runs, %d assertions, %d failures, %d errors, %d skips%s" %
977
+ [count, assertions, failures, errors, skips, extra.join]
758
978
  end
759
- end
979
+ end # SummaryReporter
760
980
 
761
981
  ##
762
982
  # Dispatch to multiple reporters as one.
@@ -807,12 +1027,14 @@ module Minitest
807
1027
  def report # :nodoc:
808
1028
  self.reporters.each(&:report)
809
1029
  end
810
- end
1030
+ end # CompositeReporter
811
1031
 
812
1032
  ##
813
1033
  # Represents run failures.
814
1034
 
815
1035
  class Assertion < Exception
1036
+ RE = /in [`'](?:[^']+[#.])?(?:assert|refute|flunk|pass|fail|raise|must|wont)/ # :nodoc:
1037
+
816
1038
  def error # :nodoc:
817
1039
  self
818
1040
  end
@@ -821,12 +1043,11 @@ module Minitest
821
1043
  # Where was this run before an assertion was raised?
822
1044
 
823
1045
  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 .*$/, "")
1046
+ bt = Minitest.filter_backtrace self.backtrace
1047
+ idx = bt.rindex { |s| s.match? RE } || -1 # fall back to first item
1048
+ loc = bt[idx+1] || bt.last || "unknown:-1"
1049
+
1050
+ loc.sub(/:in .*$/, "")
830
1051
  end
831
1052
 
832
1053
  def result_code # :nodoc:
@@ -851,24 +1072,34 @@ module Minitest
851
1072
  # Assertion wrapping an unexpected error that was raised during a run.
852
1073
 
853
1074
  class UnexpectedError < Assertion
854
- attr_accessor :exception # :nodoc:
1075
+ include Minitest::Compress
855
1076
 
856
- def initialize exception # :nodoc:
1077
+ # TODO: figure out how to use `cause` instead
1078
+ attr_accessor :error # :nodoc:
1079
+
1080
+ def initialize error # :nodoc:
857
1081
  super "Unexpected exception"
858
- self.exception = exception
1082
+
1083
+ if SystemStackError === error then
1084
+ bt = error.backtrace
1085
+ new_bt = compress bt
1086
+ error = error.exception "#{bt.size} -> #{new_bt.size}"
1087
+ error.set_backtrace new_bt
1088
+ end
1089
+
1090
+ self.error = error
859
1091
  end
860
1092
 
861
1093
  def backtrace # :nodoc:
862
- self.exception.backtrace
1094
+ self.error.backtrace
863
1095
  end
864
1096
 
865
- def error # :nodoc:
866
- self.exception
867
- end
1097
+ BASE_RE = %r%#{Regexp.escape Dir.pwd}/% # :nodoc:
868
1098
 
869
1099
  def message # :nodoc:
870
- bt = Minitest.filter_backtrace(self.backtrace).join "\n "
871
- "#{self.exception.class}: #{self.exception.message}\n #{bt}"
1100
+ bt = Minitest.filter_backtrace(self.backtrace).join("\n ")
1101
+ .gsub(BASE_RE, "")
1102
+ "#{self.error.class}: #{self.error.message}\n #{bt}"
872
1103
  end
873
1104
 
874
1105
  def result_label # :nodoc:
@@ -876,6 +1107,15 @@ module Minitest
876
1107
  end
877
1108
  end
878
1109
 
1110
+ ##
1111
+ # Assertion raised on warning when running in -Werror mode.
1112
+
1113
+ class UnexpectedWarning < Assertion
1114
+ def result_label # :nodoc:
1115
+ "Warning"
1116
+ end
1117
+ end
1118
+
879
1119
  ##
880
1120
  # Provides a simple set of guards that you can use in your tests
881
1121
  # to skip execution if it is not applicable. These methods are
@@ -904,6 +1144,9 @@ module Minitest
904
1144
  # Is this running on maglev?
905
1145
 
906
1146
  def maglev? platform = defined?(RUBY_ENGINE) && RUBY_ENGINE
1147
+ where = Minitest.filter_backtrace(caller).first
1148
+ where = where.split(":in ", 2).first # clean up noise
1149
+ warn "DEPRECATED: `maglev?` called from #{where}. This will fail in Minitest 6."
907
1150
  "maglev" == platform
908
1151
  end
909
1152
 
@@ -911,13 +1154,23 @@ module Minitest
911
1154
  # Is this running on mri?
912
1155
 
913
1156
  def mri? platform = RUBY_DESCRIPTION
914
- /^ruby/ =~ platform
1157
+ platform.start_with? "ruby"
1158
+ end
1159
+
1160
+ ##
1161
+ # Is this running on macOS?
1162
+
1163
+ def osx? platform = RUBY_PLATFORM
1164
+ platform.include? "darwin"
915
1165
  end
916
1166
 
917
1167
  ##
918
1168
  # Is this running on rubinius?
919
1169
 
920
1170
  def rubinius? platform = defined?(RUBY_ENGINE) && RUBY_ENGINE
1171
+ where = Minitest.filter_backtrace(caller).first
1172
+ where = where.split(":in ", 2).first # clean up noise
1173
+ warn "DEPRECATED: `rubinius?` called from #{where}. This will fail in Minitest 6."
921
1174
  "rbx" == platform
922
1175
  end
923
1176
 
@@ -925,7 +1178,7 @@ module Minitest
925
1178
  # Is this running on windows?
926
1179
 
927
1180
  def windows? platform = RUBY_PLATFORM
928
- /mswin|mingw/ =~ platform
1181
+ /mswin|mingw/.match? platform
929
1182
  end
930
1183
  end
931
1184
 
@@ -936,19 +1189,29 @@ module Minitest
936
1189
 
937
1190
  class BacktraceFilter
938
1191
 
939
- MT_RE = %r%lib/minitest% #:nodoc:
1192
+ MT_RE = %r%lib/minitest|internal:warning% # :nodoc:
1193
+
1194
+ ##
1195
+ # The regular expression to use to filter backtraces. Defaults to +MT_RE+.
1196
+
1197
+ attr_accessor :regexp
1198
+
1199
+ def initialize regexp = MT_RE # :nodoc:
1200
+ self.regexp = regexp
1201
+ end
940
1202
 
941
1203
  ##
942
- # Filter +bt+ to something useful. Returns the whole thing if $DEBUG.
1204
+ # Filter +bt+ to something useful. Returns the whole thing if
1205
+ # $DEBUG (ruby) or $MT_DEBUG (env).
943
1206
 
944
1207
  def filter bt
945
1208
  return ["No backtrace"] unless bt
946
1209
 
947
- return bt.dup if $DEBUG
1210
+ return bt.dup if $DEBUG || ENV["MT_DEBUG"]
948
1211
 
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?
1212
+ new_bt = bt.take_while { |line| !regexp.match? line.to_s }
1213
+ new_bt = bt.select { |line| !regexp.match? line.to_s } if new_bt.empty?
1214
+ new_bt = bt.dup if new_bt.empty?
952
1215
 
953
1216
  new_bt
954
1217
  end
@@ -984,4 +1247,4 @@ module Minitest
984
1247
  # :startdoc:
985
1248
  end
986
1249
 
987
- require "minitest/test"
1250
+ require_relative "minitest/test"