minitest 5.11.3 → 5.22.3

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
9
  # :include: README.rdoc
9
10
 
10
11
  module Minitest
11
- VERSION = "5.11.3" # :nodoc:
12
- ENCS = "".respond_to? :encoding # :nodoc:
12
+ VERSION = "5.22.2" # :nodoc:
13
13
 
14
14
  @@installed_at_exit ||= false
15
15
  @@after_run = []
16
16
  @extensions = []
17
17
 
18
- mc = (class << self; self; end)
18
+ def self.cattr_accessor name # :nodoc:
19
+ (class << self; self; end).attr_accessor name
20
+ end
21
+
22
+ ##
23
+ # The random seed used for this run. This is used to srand at the
24
+ # start of the run and between each +Runnable.run+.
25
+ #
26
+ # Set via Minitest.run after processing args.
27
+
28
+ cattr_accessor :seed
19
29
 
20
30
  ##
21
31
  # Parallel test executor
22
32
 
23
- mc.send :attr_accessor, :parallel_executor
24
- self.parallel_executor = Parallel::Executor.new((ENV["N"] || 2).to_i)
33
+ cattr_accessor :parallel_executor
34
+
35
+ warn "DEPRECATED: use MT_CPU instead of N for parallel test runs" if ENV["N"] && ENV["N"].to_i > 0
36
+ n_threads = (ENV["MT_CPU"] || ENV["N"] || Etc.nprocessors).to_i
37
+
38
+ self.parallel_executor = Parallel::Executor.new n_threads
25
39
 
26
40
  ##
27
41
  # Filter object for backtraces.
28
42
 
29
- mc.send :attr_accessor, :backtrace_filter
43
+ cattr_accessor :backtrace_filter
30
44
 
31
45
  ##
32
46
  # Reporter object to be used for all runs.
33
47
  #
34
48
  # NOTE: This accessor is only available during setup, not during runs.
35
49
 
36
- mc.send :attr_accessor, :reporter
50
+ cattr_accessor :reporter
37
51
 
38
52
  ##
39
53
  # Names of known extension plugins.
40
54
 
41
- mc.send :attr_accessor, :extensions
55
+ cattr_accessor :extensions
42
56
 
43
57
  ##
44
58
  # The signal to use for dumping information to STDERR. Defaults to "INFO".
45
59
 
46
- mc.send :attr_accessor, :info_signal
60
+ cattr_accessor :info_signal
47
61
  self.info_signal = "INFO"
48
62
 
63
+ cattr_accessor :allow_fork
64
+ self.allow_fork = false
65
+
49
66
  ##
50
67
  # Registers Minitest to run at process exit
51
68
 
52
69
  def self.autorun
70
+ if Object.const_defined?(:Warning) && Warning.respond_to?(:[]=)
71
+ Warning[:deprecated] = true
72
+ end
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
  }
@@ -111,7 +134,7 @@ module Minitest
111
134
  # Minitest.run(args)
112
135
  # Minitest.__run(reporter, options)
113
136
  # Runnable.runnables.each
114
- # runnable.run(reporter, options)
137
+ # runnable_klass.run(reporter, options)
115
138
  # self.runnable_methods.each
116
139
  # self.run_one_method(self, runnable_method, reporter)
117
140
  # Minitest.run_one_method(klass, runnable_method)
@@ -122,9 +145,12 @@ module Minitest
122
145
 
123
146
  options = process_args args
124
147
 
148
+ Minitest.seed = options[:seed]
149
+ srand Minitest.seed
150
+
125
151
  reporter = CompositeReporter.new
126
152
  reporter << SummaryReporter.new(options[:io], options)
127
- reporter << ProgressReporter.new(options[:io], options)
153
+ reporter << ProgressReporter.new(options[:io], options) unless options[:quiet]
128
154
 
129
155
  self.reporter = reporter # this makes it available to plugins
130
156
  self.init_plugins options
@@ -138,17 +164,38 @@ module Minitest
138
164
  warn "Interrupted. Exiting..."
139
165
  end
140
166
  self.parallel_executor.shutdown
167
+
168
+ # might have been removed/replaced during init_plugins:
169
+ summary = reporter.reporters.grep(SummaryReporter).first
170
+ return empty_run! options if summary && summary.count == 0
171
+
141
172
  reporter.report
142
173
 
143
174
  reporter.passed?
144
175
  end
145
176
 
177
+ def self.empty_run! options # :nodoc:
178
+ filter = options[:filter]
179
+ return true unless filter # no filter, but nothing ran == success
180
+
181
+ warn "Nothing ran for filter: %s" % [filter]
182
+
183
+ require "did_you_mean" # soft dependency, punt if it doesn't load
184
+
185
+ ms = Runnable.runnables.flat_map(&:runnable_methods)
186
+ cs = DidYouMean::SpellChecker.new(dictionary: ms).correct filter
187
+
188
+ warn DidYouMean::Formatter.message_for cs unless cs.empty?
189
+ rescue LoadError
190
+ # do nothing
191
+ end
192
+
146
193
  ##
147
194
  # Internal run method. Responsible for telling all Runnable
148
195
  # sub-classes to run.
149
196
 
150
197
  def self.__run reporter, options
151
- suites = Runnable.runnables.reject { |s| s.runnable_methods.empty? }.shuffle
198
+ suites = Runnable.runnables.shuffle
152
199
  parallel, serial = suites.partition { |s| s.test_order == :parallel }
153
200
 
154
201
  # If we run the parallel tests before the serial tests, the parallel tests
@@ -186,6 +233,14 @@ module Minitest
186
233
  options[:verbose] = true
187
234
  end
188
235
 
236
+ opts.on "-q", "--quiet", "Quiet. Show no progress processing files." do
237
+ options[:quiet] = true
238
+ end
239
+
240
+ opts.on "--show-skips", "Show skipped at the end of run." do
241
+ options[:show_skips] = true
242
+ end
243
+
189
244
  opts.on "-n", "--name PATTERN", "Filter run on /regexp/ or string." do |a|
190
245
  options[:filter] = a
191
246
  end
@@ -194,6 +249,10 @@ module Minitest
194
249
  options[:exclude] = a
195
250
  end
196
251
 
252
+ opts.on "-S", "--skip CODES", String, "Skip reporting of certain types of results (eg E)." do |s|
253
+ options[:skip] = s.chars.to_a
254
+ end
255
+
197
256
  unless extensions.empty?
198
257
  opts.separator ""
199
258
  opts.separator "Known extensions: #{extensions.join(", ")}"
@@ -223,8 +282,6 @@ module Minitest
223
282
  orig_args << "--seed" << options[:seed].to_s
224
283
  end
225
284
 
226
- srand options[:seed]
227
-
228
285
  options[:args] = orig_args.map { |s|
229
286
  s =~ /[\s|&<>$()]/ ? s.inspect : s
230
287
  }.join " "
@@ -233,7 +290,9 @@ module Minitest
233
290
  end
234
291
 
235
292
  def self.filter_backtrace bt # :nodoc:
236
- backtrace_filter.filter bt
293
+ result = backtrace_filter.filter bt
294
+ result = bt.dup if result.empty?
295
+ result
237
296
  end
238
297
 
239
298
  ##
@@ -300,19 +359,15 @@ module Minitest
300
359
  # reporter to record.
301
360
 
302
361
  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
- }
362
+ pos = options[:filter]
363
+ neg = options[:exclude]
309
364
 
310
- exclude = options[:exclude]
311
- exclude = Regexp.new $1 if exclude =~ %r%/(.*)/%
365
+ pos = Regexp.new $1 if pos.is_a?(String) && pos =~ %r%/(.*)/%
366
+ neg = Regexp.new $1 if neg.is_a?(String) && neg =~ %r%/(.*)/%
312
367
 
313
- filtered_methods.delete_if { |m|
314
- exclude === m || exclude === "#{self}##{m}"
315
- }
368
+ filtered_methods = self.runnable_methods
369
+ .select { |m| !pos || pos === m || pos === "#{self}##{m}" }
370
+ .reject { |m| neg && (neg === m || neg === "#{self}##{m}") }
316
371
 
317
372
  return if filtered_methods.empty?
318
373
 
@@ -334,6 +389,14 @@ module Minitest
334
389
  reporter.record Minitest.run_one_method(klass, method_name)
335
390
  end
336
391
 
392
+ ##
393
+ # Defines the order to run tests (:random by default). Override
394
+ # this or use a convenience method to change it for your tests.
395
+
396
+ def self.test_order
397
+ :random
398
+ end
399
+
337
400
  def self.with_info_handler reporter, &block # :nodoc:
338
401
  handler = lambda do
339
402
  unless reporter.passed? then
@@ -401,6 +464,31 @@ module Minitest
401
464
  self.name = name
402
465
  self.failures = []
403
466
  self.assertions = 0
467
+ # lazy initializer for metadata
468
+ end
469
+
470
+ ##
471
+ # Metadata you attach to the test results that get sent to the reporter.
472
+ #
473
+ # Lazily initializes to a hash, to keep memory down.
474
+ #
475
+ # NOTE: this data *must* be plain (read: marshal-able) data!
476
+ # Hashes! Arrays! Strings!
477
+
478
+ def metadata
479
+ @metadata ||= {}
480
+ end
481
+
482
+ ##
483
+ # Sets metadata, mainly used for +Result.from+.
484
+
485
+ attr_writer :metadata
486
+
487
+ ##
488
+ # Returns true if metadata exists.
489
+
490
+ def metadata?
491
+ defined? @metadata
404
492
  end
405
493
 
406
494
  ##
@@ -422,7 +510,8 @@ module Minitest
422
510
 
423
511
  ##
424
512
  # Returns a single character string to print based on the result
425
- # of the run. Eg ".", "F", or "E".
513
+ # of the run. One of <tt>"."</tt>, <tt>"F"</tt>,
514
+ # <tt>"E"</tt> or <tt>"S"</tt>.
426
515
 
427
516
  def result_code
428
517
  raise NotImplementedError, "subclass responsibility"
@@ -451,12 +540,14 @@ module Minitest
451
540
  not self.failure
452
541
  end
453
542
 
543
+ BASE_DIR = "#{Dir.pwd}/" # :nodoc:
544
+
454
545
  ##
455
546
  # The location identifier of this test. Depends on a method
456
547
  # existing called class_name.
457
548
 
458
549
  def location
459
- loc = " [#{self.failure.location}]" unless passed? or error?
550
+ loc = " [#{self.failure.location.delete_prefix BASE_DIR}]" unless passed? or error?
460
551
  "#{self.class_name}##{self.name}#{loc}"
461
552
  end
462
553
 
@@ -520,6 +611,7 @@ module Minitest
520
611
  r.assertions = o.assertions
521
612
  r.failures = o.failures.dup
522
613
  r.time = o.time
614
+ r.metadata = o.metadata if o.metadata?
523
615
 
524
616
  r.source_location = o.method(o.name).source_location rescue ["unknown", -1]
525
617
 
@@ -544,7 +636,10 @@ module Minitest
544
636
  # you want. Go nuts.
545
637
 
546
638
  class AbstractReporter
547
- include Mutex_m
639
+
640
+ def initialize # :nodoc:
641
+ @mutex = Mutex.new
642
+ end
548
643
 
549
644
  ##
550
645
  # Starts reporting on the run.
@@ -560,8 +655,10 @@ module Minitest
560
655
  end
561
656
 
562
657
  ##
563
- # Record a result and output the Runnable#result_code. Stores the
564
- # result of the run if the run did not pass.
658
+ # Output and record the result of the test. Call
659
+ # {result#result_code}[rdoc-ref:Runnable#result_code] to get the
660
+ # result character string. Stores the result of the run if the run
661
+ # did not pass.
565
662
 
566
663
  def record result
567
664
  end
@@ -578,6 +675,10 @@ module Minitest
578
675
  def passed?
579
676
  true
580
677
  end
678
+
679
+ def synchronize(&block) # :nodoc:
680
+ @mutex.synchronize(&block)
681
+ end
581
682
  end
582
683
 
583
684
  class Reporter < AbstractReporter # :nodoc:
@@ -628,18 +729,63 @@ module Minitest
628
729
  #
629
730
  # If you want to create an entirely different type of output (eg,
630
731
  # CI, HTML, etc), this is the place to start.
732
+ #
733
+ # Example:
734
+ #
735
+ # class JenkinsCIReporter < StatisticsReporter
736
+ # def report
737
+ # super # Needed to calculate some statistics
738
+ #
739
+ # print "<testsuite "
740
+ # print "tests='#{count}' "
741
+ # print "failures='#{failures}' "
742
+ # # Remaining XML...
743
+ # end
744
+ # end
631
745
 
632
746
  class StatisticsReporter < Reporter
633
- # :stopdoc:
747
+ ##
748
+ # Total number of assertions.
749
+
634
750
  attr_accessor :assertions
751
+
752
+ ##
753
+ # Total number of test cases.
754
+
635
755
  attr_accessor :count
756
+
757
+ ##
758
+ # An +Array+ of test cases that failed or were skipped.
759
+
636
760
  attr_accessor :results
761
+
762
+ ##
763
+ # Time the test run started. If available, the monotonic clock is
764
+ # used and this is a +Float+, otherwise it's an instance of
765
+ # +Time+.
766
+
637
767
  attr_accessor :start_time
768
+
769
+ ##
770
+ # Test run time. If available, the monotonic clock is used and
771
+ # this is a +Float+, otherwise it's an instance of +Time+.
772
+
638
773
  attr_accessor :total_time
774
+
775
+ ##
776
+ # Total number of tests that failed.
777
+
639
778
  attr_accessor :failures
779
+
780
+ ##
781
+ # Total number of tests that erred.
782
+
640
783
  attr_accessor :errors
784
+
785
+ ##
786
+ # Total number of tests that where skipped.
787
+
641
788
  attr_accessor :skips
642
- # :startdoc:
643
789
 
644
790
  def initialize io = $stdout, options = {} # :nodoc:
645
791
  super
@@ -669,7 +815,10 @@ module Minitest
669
815
  results << result if not result.passed? or result.skipped?
670
816
  end
671
817
 
672
- def report # :nodoc:
818
+ ##
819
+ # Report on the tracked statistics.
820
+
821
+ def report
673
822
  aggregate = results.group_by { |r| r.failure.class }
674
823
  aggregate.default = [] # dumb. group_by should provide this
675
824
 
@@ -703,7 +852,7 @@ module Minitest
703
852
  io.puts "# Running:"
704
853
  io.puts
705
854
 
706
- self.sync = io.respond_to? :"sync=" # stupid emacs
855
+ self.sync = io.respond_to? :"sync="
707
856
  self.old_sync, io.sync = io.sync, true if self.sync
708
857
  end
709
858
 
@@ -726,9 +875,14 @@ module Minitest
726
875
 
727
876
  def aggregated_results io # :nodoc:
728
877
  filtered_results = results.dup
729
- filtered_results.reject!(&:skipped?) unless options[:verbose]
878
+ filtered_results.reject!(&:skipped?) unless
879
+ options[:verbose] or options[:show_skips]
880
+
881
+ skip = options[:skip] || []
730
882
 
731
883
  filtered_results.each_with_index { |result, i|
884
+ next if skip.include? result.result_code
885
+
732
886
  io.puts "\n%3d) %s" % [i+1, result]
733
887
  }
734
888
  io.puts
@@ -736,26 +890,19 @@ module Minitest
736
890
  end
737
891
 
738
892
  def to_s # :nodoc:
739
- aggregated_results(StringIO.new(binary_string)).string
893
+ aggregated_results(StringIO.new(''.b)).string
740
894
  end
741
895
 
742
896
  def summary # :nodoc:
743
897
  extra = ""
744
898
 
745
899
  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"]
900
+ results.any?(&:skipped?) unless
901
+ options[:verbose] or options[:show_skips] or ENV["MT_NO_SKIP_MSG"]
747
902
 
748
903
  "%d runs, %d assertions, %d failures, %d errors, %d skips%s" %
749
904
  [count, assertions, failures, errors, skips, extra]
750
905
  end
751
-
752
- private
753
-
754
- if '<3'.respond_to? :b
755
- def binary_string; ''.b; end
756
- else
757
- def binary_string; ''.force_encoding(Encoding::ASCII_8BIT); end
758
- end
759
906
  end
760
907
 
761
908
  ##
@@ -813,6 +960,8 @@ module Minitest
813
960
  # Represents run failures.
814
961
 
815
962
  class Assertion < Exception
963
+ RE = /in [`'](?:[^']+[#.])?(?:assert|refute|flunk|pass|fail|raise|must|wont)/ # :nodoc:
964
+
816
965
  def error # :nodoc:
817
966
  self
818
967
  end
@@ -821,12 +970,11 @@ module Minitest
821
970
  # Where was this run before an assertion was raised?
822
971
 
823
972
  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 .*$/, "")
973
+ bt = Minitest.filter_backtrace self.backtrace
974
+ idx = bt.rindex { |s| s.match? RE } || -1 # fall back to first item
975
+ loc = bt[idx+1] || bt.last || "unknown:-1"
976
+
977
+ loc.sub(/:in .*$/, "")
830
978
  end
831
979
 
832
980
  def result_code # :nodoc:
@@ -851,24 +999,34 @@ module Minitest
851
999
  # Assertion wrapping an unexpected error that was raised during a run.
852
1000
 
853
1001
  class UnexpectedError < Assertion
854
- attr_accessor :exception # :nodoc:
1002
+ include Minitest::Compress
855
1003
 
856
- def initialize exception # :nodoc:
1004
+ # TODO: figure out how to use `cause` instead
1005
+ attr_accessor :error # :nodoc:
1006
+
1007
+ def initialize error # :nodoc:
857
1008
  super "Unexpected exception"
858
- self.exception = exception
1009
+
1010
+ if SystemStackError === error then
1011
+ bt = error.backtrace
1012
+ new_bt = compress bt
1013
+ error = error.exception "#{bt.size} -> #{new_bt.size}"
1014
+ error.set_backtrace new_bt
1015
+ end
1016
+
1017
+ self.error = error
859
1018
  end
860
1019
 
861
1020
  def backtrace # :nodoc:
862
- self.exception.backtrace
1021
+ self.error.backtrace
863
1022
  end
864
1023
 
865
- def error # :nodoc:
866
- self.exception
867
- end
1024
+ BASE_RE = %r%#{Dir.pwd}/% # :nodoc:
868
1025
 
869
1026
  def message # :nodoc:
870
- bt = Minitest.filter_backtrace(self.backtrace).join "\n "
871
- "#{self.exception.class}: #{self.exception.message}\n #{bt}"
1027
+ bt = Minitest.filter_backtrace(self.backtrace).join("\n ")
1028
+ .gsub(BASE_RE, "")
1029
+ "#{self.error.class}: #{self.error.message}\n #{bt}"
872
1030
  end
873
1031
 
874
1032
  def result_label # :nodoc:
@@ -904,6 +1062,9 @@ module Minitest
904
1062
  # Is this running on maglev?
905
1063
 
906
1064
  def maglev? platform = defined?(RUBY_ENGINE) && RUBY_ENGINE
1065
+ where = Minitest.filter_backtrace(caller).first
1066
+ where = where.split(/:in /, 2).first # clean up noise
1067
+ warn "DEPRECATED: `maglev?` called from #{where}. This will fail in Minitest 6."
907
1068
  "maglev" == platform
908
1069
  end
909
1070
 
@@ -914,10 +1075,20 @@ module Minitest
914
1075
  /^ruby/ =~ platform
915
1076
  end
916
1077
 
1078
+ ##
1079
+ # Is this running on macOS?
1080
+
1081
+ def osx? platform = RUBY_PLATFORM
1082
+ /darwin/ =~ platform
1083
+ end
1084
+
917
1085
  ##
918
1086
  # Is this running on rubinius?
919
1087
 
920
1088
  def rubinius? platform = defined?(RUBY_ENGINE) && RUBY_ENGINE
1089
+ where = Minitest.filter_backtrace(caller).first
1090
+ where = where.split(/:in /, 2).first # clean up noise
1091
+ warn "DEPRECATED: `rubinius?` called from #{where}. This will fail in Minitest 6."
921
1092
  "rbx" == platform
922
1093
  end
923
1094
 
@@ -938,17 +1109,24 @@ module Minitest
938
1109
 
939
1110
  MT_RE = %r%lib/minitest% #:nodoc:
940
1111
 
1112
+ attr_accessor :regexp
1113
+
1114
+ def initialize regexp = MT_RE
1115
+ self.regexp = regexp
1116
+ end
1117
+
941
1118
  ##
942
- # Filter +bt+ to something useful. Returns the whole thing if $DEBUG.
1119
+ # Filter +bt+ to something useful. Returns the whole thing if
1120
+ # $DEBUG (ruby) or $MT_DEBUG (env).
943
1121
 
944
1122
  def filter bt
945
1123
  return ["No backtrace"] unless bt
946
1124
 
947
- return bt.dup if $DEBUG
1125
+ return bt.dup if $DEBUG || ENV["MT_DEBUG"]
948
1126
 
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?
1127
+ new_bt = bt.take_while { |line| line.to_s !~ regexp }
1128
+ new_bt = bt.select { |line| line.to_s !~ regexp } if new_bt.empty?
1129
+ new_bt = bt.dup if new_bt.empty?
952
1130
 
953
1131
  new_bt
954
1132
  end