minitest 5.17.0 → 5.23.1

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.
@@ -29,6 +29,10 @@ module Minitest # :nodoc:
29
29
  # end
30
30
  #
31
31
  # Customize the name and only run unit tests.
32
+ #
33
+ # NOTE: To hook this task up to the default, make it a dependency:
34
+ #
35
+ # task default: :unit
32
36
 
33
37
  class TestTask < Rake::TaskLib
34
38
  WINDOWS = RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ # :nodoc:
@@ -110,7 +114,7 @@ module Minitest # :nodoc:
110
114
  self.test_globs = ["test/**/test_*.rb",
111
115
  "test/**/*_test.rb"]
112
116
  self.test_prelude = nil
113
- self.verbose = Rake.application.options.trace
117
+ self.verbose = Rake.application.options.trace || Rake.verbose == true
114
118
  self.warning = true
115
119
  end
116
120
 
@@ -157,8 +161,6 @@ module Minitest # :nodoc:
157
161
  end
158
162
 
159
163
  def define # :nodoc:
160
- default_tasks = []
161
-
162
164
  desc "Run the test suite. Use N, X, A, and TESTOPTS to add flags/args."
163
165
  task name do
164
166
  ruby make_test_cmd, verbose:verbose
@@ -243,11 +245,6 @@ module Minitest # :nodoc:
243
245
  "sort -n -k2 -t=",
244
246
  "tail -25"].join " | "
245
247
  end
246
-
247
- default_tasks << name
248
-
249
- desc "Run the default task(s)."
250
- task :default => default_tasks
251
248
  end
252
249
 
253
250
  ##
@@ -291,7 +288,6 @@ end
291
288
 
292
289
  class Integer # :nodoc:
293
290
  def threads_do(jobs) # :nodoc:
294
- require "thread"
295
291
  q = Work.new jobs
296
292
 
297
293
  self.times.map {
data/lib/minitest.rb CHANGED
@@ -1,15 +1,15 @@
1
1
  require "optparse"
2
- require "thread"
3
- require "mutex_m"
4
- require "minitest/parallel"
5
2
  require "stringio"
6
3
  require "etc"
7
4
 
5
+ require_relative "minitest/parallel"
6
+ require_relative "minitest/compress"
7
+
8
8
  ##
9
9
  # :include: README.rdoc
10
10
 
11
11
  module Minitest
12
- VERSION = "5.17.0" # :nodoc:
12
+ VERSION = "5.23.1" # :nodoc:
13
13
 
14
14
  @@installed_at_exit ||= false
15
15
  @@after_run = []
@@ -32,7 +32,7 @@ module Minitest
32
32
 
33
33
  cattr_accessor :parallel_executor
34
34
 
35
- warn "DEPRECATED: use MT_CPU instead of N for parallel test runs" if ENV["N"]
35
+ warn "DEPRECATED: use MT_CPU instead of N for parallel test runs" if ENV["N"] && ENV["N"].to_i > 0
36
36
  n_threads = (ENV["MT_CPU"] || ENV["N"] || Etc.nprocessors).to_i
37
37
 
38
38
  self.parallel_executor = Parallel::Executor.new n_threads
@@ -60,6 +60,9 @@ module Minitest
60
60
  cattr_accessor :info_signal
61
61
  self.info_signal = "INFO"
62
62
 
63
+ cattr_accessor :allow_fork
64
+ self.allow_fork = false
65
+
63
66
  ##
64
67
  # Registers Minitest to run at process exit
65
68
 
@@ -75,7 +78,7 @@ module Minitest
75
78
 
76
79
  pid = Process.pid
77
80
  at_exit {
78
- next if Process.pid != pid
81
+ next if !Minitest.allow_fork && Process.pid != pid
79
82
  @@after_run.reverse_each(&:call)
80
83
  exit exit_code || false
81
84
  }
@@ -131,7 +134,7 @@ module Minitest
131
134
  # Minitest.run(args)
132
135
  # Minitest.__run(reporter, options)
133
136
  # Runnable.runnables.each
134
- # runnable.run(reporter, options)
137
+ # runnable_klass.run(reporter, options)
135
138
  # self.runnable_methods.each
136
139
  # self.run_one_method(self, runnable_method, reporter)
137
140
  # Minitest.run_one_method(klass, runnable_method)
@@ -147,7 +150,7 @@ module Minitest
147
150
 
148
151
  reporter = CompositeReporter.new
149
152
  reporter << SummaryReporter.new(options[:io], options)
150
- reporter << ProgressReporter.new(options[:io], options)
153
+ reporter << ProgressReporter.new(options[:io], options) unless options[:quiet]
151
154
 
152
155
  self.reporter = reporter # this makes it available to plugins
153
156
  self.init_plugins options
@@ -161,11 +164,32 @@ module Minitest
161
164
  warn "Interrupted. Exiting..."
162
165
  end
163
166
  self.parallel_executor.shutdown
167
+
168
+ # might have been removed/replaced during init_plugins:
169
+ summary = reporter.reporters.grep(SummaryReporter).first
170
+
164
171
  reporter.report
165
172
 
173
+ return empty_run! options if summary && summary.count == 0
166
174
  reporter.passed?
167
175
  end
168
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
+
169
193
  ##
170
194
  # Internal run method. Responsible for telling all Runnable
171
195
  # sub-classes to run.
@@ -209,6 +233,10 @@ module Minitest
209
233
  options[:verbose] = true
210
234
  end
211
235
 
236
+ opts.on "-q", "--quiet", "Quiet. Show no progress processing files." do
237
+ options[:quiet] = true
238
+ end
239
+
212
240
  opts.on "--show-skips", "Show skipped at the end of run." do
213
241
  options[:show_skips] = true
214
242
  end
@@ -225,6 +253,20 @@ module Minitest
225
253
  options[:skip] = s.chars.to_a
226
254
  end
227
255
 
256
+ ruby27plus = ::Warning.respond_to?(:[]=)
257
+
258
+ opts.on "-W[error]", String, "Turn Ruby warnings into errors" do |s|
259
+ options[:Werror] = true
260
+ case s
261
+ when "error", "all", nil then
262
+ require "minitest/error_on_warning"
263
+ $VERBOSE = true
264
+ ::Warning[:deprecated] = true if ruby27plus
265
+ else
266
+ ::Warning[s.to_sym] = true if ruby27plus # check validity of category
267
+ end
268
+ end
269
+
228
270
  unless extensions.empty?
229
271
  opts.separator ""
230
272
  opts.separator "Known extensions: #{extensions.join(", ")}"
@@ -331,19 +373,15 @@ module Minitest
331
373
  # reporter to record.
332
374
 
333
375
  def self.run reporter, options = {}
334
- filter = options[:filter] || "/./"
335
- filter = Regexp.new $1 if filter.is_a?(String) && filter =~ %r%/(.*)/%
376
+ pos = options[:filter]
377
+ neg = options[:exclude]
336
378
 
337
- filtered_methods = self.runnable_methods.find_all { |m|
338
- filter === m || filter === "#{self}##{m}"
339
- }
379
+ pos = Regexp.new $1 if pos.is_a?(String) && pos =~ %r%/(.*)/%
380
+ neg = Regexp.new $1 if neg.is_a?(String) && neg =~ %r%/(.*)/%
340
381
 
341
- exclude = options[:exclude]
342
- exclude = Regexp.new $1 if exclude =~ %r%/(.*)/%
343
-
344
- filtered_methods.delete_if { |m|
345
- exclude === m || exclude === "#{self}##{m}"
346
- }
382
+ filtered_methods = self.runnable_methods
383
+ .select { |m| !pos || pos === m || pos === "#{self}##{m}" }
384
+ .reject { |m| neg && (neg === m || neg === "#{self}##{m}") }
347
385
 
348
386
  return if filtered_methods.empty?
349
387
 
@@ -365,6 +403,14 @@ module Minitest
365
403
  reporter.record Minitest.run_one_method(klass, method_name)
366
404
  end
367
405
 
406
+ ##
407
+ # Defines the order to run tests (:random by default). Override
408
+ # this or use a convenience method to change it for your tests.
409
+
410
+ def self.test_order
411
+ :random
412
+ end
413
+
368
414
  def self.with_info_handler reporter, &block # :nodoc:
369
415
  handler = lambda do
370
416
  unless reporter.passed? then
@@ -432,6 +478,31 @@ module Minitest
432
478
  self.name = name
433
479
  self.failures = []
434
480
  self.assertions = 0
481
+ # lazy initializer for metadata
482
+ end
483
+
484
+ ##
485
+ # Metadata you attach to the test results that get sent to the reporter.
486
+ #
487
+ # Lazily initializes to a hash, to keep memory down.
488
+ #
489
+ # NOTE: this data *must* be plain (read: marshal-able) data!
490
+ # Hashes! Arrays! Strings!
491
+
492
+ def metadata
493
+ @metadata ||= {}
494
+ end
495
+
496
+ ##
497
+ # Sets metadata, mainly used for +Result.from+.
498
+
499
+ attr_writer :metadata
500
+
501
+ ##
502
+ # Returns true if metadata exists.
503
+
504
+ def metadata?
505
+ defined? @metadata
435
506
  end
436
507
 
437
508
  ##
@@ -483,12 +554,14 @@ module Minitest
483
554
  not self.failure
484
555
  end
485
556
 
557
+ BASE_DIR = "#{Dir.pwd}/" # :nodoc:
558
+
486
559
  ##
487
560
  # The location identifier of this test. Depends on a method
488
561
  # existing called class_name.
489
562
 
490
563
  def location
491
- loc = " [#{self.failure.location}]" unless passed? or error?
564
+ loc = " [#{self.failure.location.delete_prefix BASE_DIR}]" unless passed? or error?
492
565
  "#{self.class_name}##{self.name}#{loc}"
493
566
  end
494
567
 
@@ -552,6 +625,7 @@ module Minitest
552
625
  r.assertions = o.assertions
553
626
  r.failures = o.failures.dup
554
627
  r.time = o.time
628
+ r.metadata = o.metadata if o.metadata?
555
629
 
556
630
  r.source_location = o.method(o.name).source_location rescue ["unknown", -1]
557
631
 
@@ -576,7 +650,10 @@ module Minitest
576
650
  # you want. Go nuts.
577
651
 
578
652
  class AbstractReporter
579
- include Mutex_m
653
+
654
+ def initialize # :nodoc:
655
+ @mutex = Mutex.new
656
+ end
580
657
 
581
658
  ##
582
659
  # Starts reporting on the run.
@@ -612,6 +689,10 @@ module Minitest
612
689
  def passed?
613
690
  true
614
691
  end
692
+
693
+ def synchronize(&block) # :nodoc:
694
+ @mutex.synchronize(&block)
695
+ end
615
696
  end
616
697
 
617
698
  class Reporter < AbstractReporter # :nodoc:
@@ -715,6 +796,11 @@ module Minitest
715
796
 
716
797
  attr_accessor :errors
717
798
 
799
+ ##
800
+ # Total number of tests that warned.
801
+
802
+ attr_accessor :warnings
803
+
718
804
  ##
719
805
  # Total number of tests that where skipped.
720
806
 
@@ -730,6 +816,7 @@ module Minitest
730
816
  self.total_time = nil
731
817
  self.failures = nil
732
818
  self.errors = nil
819
+ self.warnings = nil
733
820
  self.skips = nil
734
821
  end
735
822
 
@@ -758,6 +845,7 @@ module Minitest
758
845
  self.total_time = Minitest.clock_time - start_time
759
846
  self.failures = aggregate[Assertion].size
760
847
  self.errors = aggregate[UnexpectedError].size
848
+ self.warnings = aggregate[UnexpectedWarning].size
761
849
  self.skips = aggregate[Skip].size
762
850
  end
763
851
  end
@@ -785,7 +873,7 @@ module Minitest
785
873
  io.puts "# Running:"
786
874
  io.puts
787
875
 
788
- self.sync = io.respond_to? :"sync=" # stupid emacs
876
+ self.sync = io.respond_to? :"sync="
789
877
  self.old_sync, io.sync = io.sync, true if self.sync
790
878
  end
791
879
 
@@ -833,6 +921,8 @@ module Minitest
833
921
  results.any?(&:skipped?) unless
834
922
  options[:verbose] or options[:show_skips] or ENV["MT_NO_SKIP_MSG"]
835
923
 
924
+ extra.prepend ", %d warnings" % [warnings] if options[:Werror]
925
+
836
926
  "%d runs, %d assertions, %d failures, %d errors, %d skips%s" %
837
927
  [count, assertions, failures, errors, skips, extra]
838
928
  end
@@ -893,6 +983,8 @@ module Minitest
893
983
  # Represents run failures.
894
984
 
895
985
  class Assertion < Exception
986
+ RE = /in [`'](?:[^']+[#.])?(?:assert|refute|flunk|pass|fail|raise|must|wont)/ # :nodoc:
987
+
896
988
  def error # :nodoc:
897
989
  self
898
990
  end
@@ -901,12 +993,11 @@ module Minitest
901
993
  # Where was this run before an assertion was raised?
902
994
 
903
995
  def location
904
- last_before_assertion = ""
905
- self.backtrace.reverse_each do |s|
906
- break if s =~ /in .(assert|refute|flunk|pass|fail|raise|must|wont)/
907
- last_before_assertion = s
908
- end
909
- last_before_assertion.sub(/:in .*$/, "")
996
+ bt = Minitest.filter_backtrace self.backtrace
997
+ idx = bt.rindex { |s| s.match? RE } || -1 # fall back to first item
998
+ loc = bt[idx+1] || bt.last || "unknown:-1"
999
+
1000
+ loc.sub(/:in .*$/, "")
910
1001
  end
911
1002
 
912
1003
  def result_code # :nodoc:
@@ -931,11 +1022,21 @@ module Minitest
931
1022
  # Assertion wrapping an unexpected error that was raised during a run.
932
1023
 
933
1024
  class UnexpectedError < Assertion
1025
+ include Minitest::Compress
1026
+
934
1027
  # TODO: figure out how to use `cause` instead
935
1028
  attr_accessor :error # :nodoc:
936
1029
 
937
1030
  def initialize error # :nodoc:
938
1031
  super "Unexpected exception"
1032
+
1033
+ if SystemStackError === error then
1034
+ bt = error.backtrace
1035
+ new_bt = compress bt
1036
+ error = error.exception "#{bt.size} -> #{new_bt.size}"
1037
+ error.set_backtrace new_bt
1038
+ end
1039
+
939
1040
  self.error = error
940
1041
  end
941
1042
 
@@ -943,8 +1044,11 @@ module Minitest
943
1044
  self.error.backtrace
944
1045
  end
945
1046
 
1047
+ BASE_RE = %r%#{Dir.pwd}/% # :nodoc:
1048
+
946
1049
  def message # :nodoc:
947
- bt = Minitest.filter_backtrace(self.backtrace).join "\n "
1050
+ bt = Minitest.filter_backtrace(self.backtrace).join("\n ")
1051
+ .gsub(BASE_RE, "")
948
1052
  "#{self.error.class}: #{self.error.message}\n #{bt}"
949
1053
  end
950
1054
 
@@ -953,6 +1057,15 @@ module Minitest
953
1057
  end
954
1058
  end
955
1059
 
1060
+ ##
1061
+ # Assertion raised on warning when running in -Werror mode.
1062
+
1063
+ class UnexpectedWarning < Assertion
1064
+ def result_label # :nodoc:
1065
+ "Warning"
1066
+ end
1067
+ end
1068
+
956
1069
  ##
957
1070
  # Provides a simple set of guards that you can use in your tests
958
1071
  # to skip execution if it is not applicable. These methods are
@@ -1026,7 +1139,13 @@ module Minitest
1026
1139
 
1027
1140
  class BacktraceFilter
1028
1141
 
1029
- MT_RE = %r%lib/minitest% #:nodoc:
1142
+ MT_RE = %r%lib/minitest|internal:warning% #:nodoc:
1143
+
1144
+ attr_accessor :regexp
1145
+
1146
+ def initialize regexp = MT_RE
1147
+ self.regexp = regexp
1148
+ end
1030
1149
 
1031
1150
  ##
1032
1151
  # Filter +bt+ to something useful. Returns the whole thing if
@@ -1037,9 +1156,9 @@ module Minitest
1037
1156
 
1038
1157
  return bt.dup if $DEBUG || ENV["MT_DEBUG"]
1039
1158
 
1040
- new_bt = bt.take_while { |line| line !~ MT_RE }
1041
- new_bt = bt.select { |line| line !~ MT_RE } if new_bt.empty?
1042
- new_bt = bt.dup if new_bt.empty?
1159
+ new_bt = bt.take_while { |line| line.to_s !~ regexp }
1160
+ new_bt = bt.select { |line| line.to_s !~ regexp } if new_bt.empty?
1161
+ new_bt = bt.dup if new_bt.empty?
1043
1162
 
1044
1163
  new_bt
1045
1164
  end
@@ -8,24 +8,36 @@ class Minitest::Test
8
8
  end
9
9
 
10
10
  def with_empty_backtrace_filter
11
- original = Minitest.backtrace_filter
12
-
13
- obj = Minitest::BacktraceFilter.new
14
- def obj.filter _bt
15
- []
11
+ with_backtrace_filter Minitest::BacktraceFilter.new %r%.% do
12
+ yield
16
13
  end
14
+ end
15
+
16
+ def with_backtrace_filter filter
17
+ original = Minitest.backtrace_filter
17
18
 
18
19
  Minitest::Test.io_lock.synchronize do # try not to trounce in parallel
19
20
  begin
20
- Minitest.backtrace_filter = obj
21
+ Minitest.backtrace_filter = filter
21
22
  yield
22
23
  ensure
23
24
  Minitest.backtrace_filter = original
24
25
  end
25
26
  end
26
27
  end
27
- end
28
28
 
29
+ def error_on_warn?
30
+ defined?(Minitest::ErrorOnWarning)
31
+ end
32
+
33
+ def assert_deprecation re = /DEPRECATED/
34
+ assert_output "", re do
35
+ yield
36
+ end
37
+ rescue Minitest::UnexpectedWarning => e # raised if -Werror was used
38
+ assert_match re, e.message
39
+ end
40
+ end
29
41
 
30
42
  class FakeNamedTest < Minitest::Test
31
43
  @@count = 0
@@ -55,7 +67,7 @@ class MetaMetaMetaTestCase < Minitest::Test
55
67
  def run_tu_with_fresh_reporter flags = %w[--seed 42]
56
68
  options = Minitest.process_args flags
57
69
 
58
- @output = StringIO.new("".encode('UTF-8'))
70
+ @output = StringIO.new("".encode(Encoding::UTF_8))
59
71
 
60
72
  self.reporter = Minitest::CompositeReporter.new
61
73
  reporter << Minitest::SummaryReporter.new(@output, options)
@@ -105,15 +117,20 @@ class MetaMetaMetaTestCase < Minitest::Test
105
117
  output.gsub!(/0x[A-Fa-f0-9]+/, "0xXXX")
106
118
  output.gsub!(/ +$/, "")
107
119
 
120
+ file = ->(s) { s.start_with?("/") ? "FULLFILE" : "FILE" }
121
+
108
122
  if windows? then
109
123
  output.gsub!(/\[(?:[A-Za-z]:)?[^\]:]+:\d+\]/, "[FILE:LINE]")
110
- output.gsub!(/^(\s+)(?:[A-Za-z]:)?[^:]+:\d+:in/, '\1FILE:LINE:in')
124
+ output.gsub!(/^(\s+)(?:[A-Za-z]:)?[^:]+:\d+:in [`']/, '\1FILE:LINE:in \'')
111
125
  else
112
- output.gsub!(/\[[^\]:]+:\d+\]/, "[FILE:LINE]")
113
- output.gsub!(/^(\s+)[^:]+:\d+:in/, '\1FILE:LINE:in')
126
+ output.gsub!(/\[([^\]:]+):\d+\]/) { "[#{file[$1]}:LINE]" }
127
+ output.gsub!(/^(\s+)([^:]+):\d+:in [`']/) { "#{$1}#{file[$2]}:LINE:in '" }
114
128
  end
115
129
 
116
- output.gsub!(/( at )[^:]+:\d+/, '\1[FILE:LINE]')
130
+ output.gsub!(/in [`']block in (?:([^']+)[#.])?/, "in 'block in")
131
+ output.gsub!(/in [`'](?:([^']+)[#.])?/, "in '")
132
+
133
+ output.gsub!(/( at )[^:]+:\d+/) { "#{$1}[#{file[$2]}:LINE]" } # eval?
117
134
 
118
135
  output
119
136
  end