minitest 5.10.3 → 5.18.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/hoe/minitest.rb CHANGED
@@ -24,9 +24,5 @@ module Hoe::Minitest
24
24
 
25
25
  def define_minitest_tasks
26
26
  self.testlib = :minitest
27
-
28
- # make sure we use the gemmed minitest on 1.9
29
- self.test_prelude = 'gem "minitest"' unless
30
- minitest? or ENV["MT_NO_ISOLATE"]
31
27
  end
32
28
  end
@@ -27,20 +27,18 @@ module Minitest
27
27
  # figure out what diff to use.
28
28
 
29
29
  def self.diff
30
+ return @diff if defined? @diff
31
+
30
32
  @diff = if (RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ &&
31
33
  system("diff.exe", __FILE__, __FILE__)) then
32
34
  "diff.exe -u"
33
- elsif Minitest::Test.maglev? then
34
- "diff -u"
35
35
  elsif system("gdiff", __FILE__, __FILE__)
36
36
  "gdiff -u" # solaris and kin suck
37
37
  elsif system("diff", __FILE__, __FILE__)
38
38
  "diff -u"
39
39
  else
40
40
  nil
41
- end unless defined? @diff
42
-
43
- @diff
41
+ end
44
42
  end
45
43
 
46
44
  ##
@@ -55,22 +53,16 @@ module Minitest
55
53
  # diff command or if it doesn't make sense to diff the output
56
54
  # (single line, short output), then it simply returns a basic
57
55
  # comparison between the two.
56
+ #
57
+ # See +things_to_diff+ for more info.
58
58
 
59
59
  def diff exp, act
60
- expect = mu_pp_for_diff exp
61
- butwas = mu_pp_for_diff act
62
60
  result = nil
63
61
 
64
- need_to_diff =
65
- (expect.include?("\n") ||
66
- butwas.include?("\n") ||
67
- expect.size > 30 ||
68
- butwas.size > 30 ||
69
- expect == butwas) &&
70
- Minitest::Assertions.diff
62
+ expect, butwas = things_to_diff(exp, act)
71
63
 
72
64
  return "Expected: #{mu_pp exp}\n Actual: #{mu_pp act}" unless
73
- need_to_diff
65
+ expect
74
66
 
75
67
  Tempfile.open("expect") do |a|
76
68
  a.puts expect
@@ -99,10 +91,40 @@ module Minitest
99
91
  result
100
92
  end
101
93
 
94
+ ##
95
+ # Returns things to diff [expect, butwas], or [nil, nil] if nothing to diff.
96
+ #
97
+ # Criterion:
98
+ #
99
+ # 1. Strings include newlines or escaped newlines, but not both.
100
+ # 2. or: String lengths are > 30 characters.
101
+ # 3. or: Strings are equal to each other (but maybe different encodings?).
102
+ # 4. and: we found a diff executable.
103
+
104
+ def things_to_diff exp, act
105
+ expect = mu_pp_for_diff exp
106
+ butwas = mu_pp_for_diff act
107
+
108
+ e1, e2 = expect.include?("\n"), expect.include?("\\n")
109
+ b1, b2 = butwas.include?("\n"), butwas.include?("\\n")
110
+
111
+ need_to_diff =
112
+ (e1 ^ e2 ||
113
+ b1 ^ b2 ||
114
+ expect.size > 30 ||
115
+ butwas.size > 30 ||
116
+ expect == butwas) &&
117
+ Minitest::Assertions.diff
118
+
119
+ need_to_diff && [expect, butwas]
120
+ end
121
+
102
122
  ##
103
123
  # This returns a human-readable version of +obj+. By default
104
- # #inspect is called. You can override this to use #pretty_print
124
+ # #inspect is called. You can override this to use #pretty_inspect
105
125
  # if you want.
126
+ #
127
+ # See Minitest::Test.make_my_diffs_pretty!
106
128
 
107
129
  def mu_pp obj
108
130
  s = obj.inspect
@@ -110,8 +132,11 @@ module Minitest
110
132
  if defined? Encoding then
111
133
  s = s.encode Encoding.default_external
112
134
 
113
- if String === obj && obj.encoding != Encoding.default_external then
114
- s = "# encoding: #{obj.encoding}\n#{s}"
135
+ if String === obj && (obj.encoding != Encoding.default_external ||
136
+ !obj.valid_encoding?) then
137
+ enc = "# encoding: #{obj.encoding}"
138
+ val = "# valid: #{obj.valid_encoding?}"
139
+ s = "#{enc}\n#{val}\n#{s}"
115
140
  end
116
141
  end
117
142
 
@@ -119,13 +144,32 @@ module Minitest
119
144
  end
120
145
 
121
146
  ##
122
- # This returns a diff-able human-readable version of +obj+. This
123
- # differs from the regular mu_pp because it expands escaped
124
- # newlines and makes hex-values generic (like object_ids). This
147
+ # This returns a diff-able more human-readable version of +obj+.
148
+ # This differs from the regular mu_pp because it expands escaped
149
+ # newlines and makes hex-values (like object_ids) generic. This
125
150
  # uses mu_pp to do the first pass and then cleans it up.
126
151
 
127
152
  def mu_pp_for_diff obj
128
- mu_pp(obj).gsub(/\\n/, "\n").gsub(/:0x[a-fA-F0-9]{4,}/m, ":0xXXXXXX")
153
+ str = mu_pp obj
154
+
155
+ # both '\n' & '\\n' (_after_ mu_pp (aka inspect))
156
+ single = !!str.match(/(?<!\\|^)\\n/)
157
+ double = !!str.match(/(?<=\\|^)\\n/)
158
+
159
+ process =
160
+ if single ^ double then
161
+ if single then
162
+ lambda { |s| s == "\\n" ? "\n" : s } # unescape
163
+ else
164
+ lambda { |s| s == "\\\\n" ? "\\n\n" : s } # unescape a bit, add nls
165
+ end
166
+ else
167
+ :itself # leave it alone
168
+ end
169
+
170
+ str.
171
+ gsub(/\\?\\n/, &process).
172
+ gsub(/:0x[a-fA-F0-9]{4,}/m, ":0xXXXXXX") # anonymize hex values
129
173
  end
130
174
 
131
175
  ##
@@ -173,7 +217,7 @@ module Minitest
173
217
  msg = message(msg, E) { diff exp, act }
174
218
  result = assert exp == act, msg
175
219
 
176
- if exp.nil? then
220
+ if nil == exp then
177
221
  if Minitest::VERSION =~ /^6/ then
178
222
  refute_nil exp, "Use assert_nil if expecting nil."
179
223
  else
@@ -205,8 +249,8 @@ module Minitest
205
249
  # For comparing Floats. Fails unless +exp+ and +act+ have a relative
206
250
  # error less than +epsilon+.
207
251
 
208
- def assert_in_epsilon a, b, epsilon = 0.001, msg = nil
209
- assert_in_delta a, b, [a.abs, b.abs].min * epsilon, msg
252
+ def assert_in_epsilon exp, act, epsilon = 0.001, msg = nil
253
+ assert_in_delta exp, act, [exp.abs, act.abs].min * epsilon, msg
210
254
  end
211
255
 
212
256
  ##
@@ -249,6 +293,8 @@ module Minitest
249
293
  assert_respond_to matcher, :"=~"
250
294
  matcher = Regexp.new Regexp.escape matcher if String === matcher
251
295
  assert matcher =~ obj, msg
296
+
297
+ Regexp.last_match
252
298
  end
253
299
 
254
300
  ##
@@ -283,6 +329,9 @@ module Minitest
283
329
  # See also: #assert_silent
284
330
 
285
331
  def assert_output stdout = nil, stderr = nil
332
+ flunk "assert_output requires a block to capture output." unless
333
+ block_given?
334
+
286
335
  out, err = capture_io do
287
336
  yield
288
337
  end
@@ -294,6 +343,44 @@ module Minitest
294
343
  x = send out_msg, stdout, out, "In stdout" if out_msg
295
344
 
296
345
  (!stdout || x) && (!stderr || y)
346
+ rescue Assertion
347
+ raise
348
+ rescue => e
349
+ raise UnexpectedError, e
350
+ end
351
+
352
+ ##
353
+ # Fails unless +path+ exists.
354
+
355
+ def assert_path_exists path, msg = nil
356
+ msg = message(msg) { "Expected path '#{path}' to exist" }
357
+ assert File.exist?(path), msg
358
+ end
359
+
360
+ ##
361
+ # For testing with pattern matching (only supported with Ruby 3.0 and later)
362
+ #
363
+ # # pass
364
+ # assert_pattern { [1,2,3] => [Integer, Integer, Integer] }
365
+ #
366
+ # # fail "length mismatch (given 3, expected 1)"
367
+ # assert_pattern { [1,2,3] => [Integer] }
368
+ #
369
+ # The bare <tt>=></tt> pattern will raise a NoMatchingPatternError on failure, which would
370
+ # normally be counted as a test error. This assertion rescues NoMatchingPatternError and
371
+ # generates a test failure. Any other exception will be raised as normal and generate a test
372
+ # error.
373
+
374
+ def assert_pattern
375
+ raise NotImplementedError, "only available in Ruby 3.0+" unless RUBY_VERSION >= "3.0"
376
+ flunk "assert_pattern requires a block to capture errors." unless block_given?
377
+
378
+ begin # TODO: remove after ruby 2.6 dropped
379
+ yield
380
+ pass
381
+ rescue NoMatchingPatternError => e
382
+ flunk e.message
383
+ end
297
384
  end
298
385
 
299
386
  ##
@@ -316,9 +403,26 @@ module Minitest
316
403
  #
317
404
  # +exp+ takes an optional message on the end to help explain
318
405
  # failures and defaults to StandardError if no exception class is
319
- # passed.
406
+ # passed. Eg:
407
+ #
408
+ # assert_raises(CustomError) { method_with_custom_error }
409
+ #
410
+ # With custom error message:
411
+ #
412
+ # assert_raises(CustomError, 'This should have raised CustomError') { method_with_custom_error }
413
+ #
414
+ # Using the returned object:
415
+ #
416
+ # error = assert_raises(CustomError) do
417
+ # raise CustomError, 'This is really bad'
418
+ # end
419
+ #
420
+ # assert_equal 'This is really bad', error.message
320
421
 
321
422
  def assert_raises *exp
423
+ flunk "assert_raises requires a block to capture errors." unless
424
+ block_given?
425
+
322
426
  msg = "#{exp.pop}.\n" if String === exp.last
323
427
  exp << StandardError if exp.empty?
324
428
 
@@ -327,7 +431,7 @@ module Minitest
327
431
  rescue *exp => e
328
432
  pass # count assertion
329
433
  return e
330
- rescue Minitest::Skip, Minitest::Assertion
434
+ rescue Minitest::Assertion # incl Skip & UnexpectedError
331
435
  # don't count assertion
332
436
  raise
333
437
  rescue SignalException, SystemExit
@@ -397,7 +501,7 @@ module Minitest
397
501
  def assert_throws sym, msg = nil
398
502
  default = "Expected #{mu_pp(sym)} to have been thrown"
399
503
  caught = true
400
- catch(sym) do
504
+ value = catch(sym) do
401
505
  begin
402
506
  yield
403
507
  rescue ThreadError => e # wtf?!? 1.8 + threads == suck
@@ -413,6 +517,11 @@ module Minitest
413
517
  end
414
518
 
415
519
  assert caught, message(msg) { default }
520
+ value
521
+ rescue Assertion
522
+ raise
523
+ rescue => e
524
+ raise UnexpectedError, e
416
525
  end
417
526
 
418
527
  ##
@@ -481,10 +590,13 @@ module Minitest
481
590
 
482
591
  return captured_stdout.read, captured_stderr.read
483
592
  ensure
484
- captured_stdout.unlink
485
- captured_stderr.unlink
486
593
  $stdout.reopen orig_stdout
487
594
  $stderr.reopen orig_stderr
595
+
596
+ orig_stdout.close
597
+ orig_stderr.close
598
+ captured_stdout.close!
599
+ captured_stderr.close!
488
600
  end
489
601
  end
490
602
  end
@@ -504,7 +616,16 @@ module Minitest
504
616
  end
505
617
 
506
618
  ##
507
- # Fails with +msg+
619
+ # Fails after a given date (in the local time zone). This allows
620
+ # you to put time-bombs in your tests if you need to keep
621
+ # something around until a later date lest you forget about it.
622
+
623
+ def fail_after y,m,d,msg
624
+ flunk msg if Time.now > Time.local(y, m, d)
625
+ end
626
+
627
+ ##
628
+ # Fails with +msg+.
508
629
 
509
630
  def flunk msg = nil
510
631
  msg ||= "Epic Fail!"
@@ -534,7 +655,7 @@ module Minitest
534
655
 
535
656
  def refute test, msg = nil
536
657
  msg ||= message { "Expected #{mu_pp(test)} to not be truthy" }
537
- not assert !test, msg
658
+ assert !test, msg
538
659
  end
539
660
 
540
661
  ##
@@ -626,6 +747,30 @@ module Minitest
626
747
  refute obj.nil?, msg
627
748
  end
628
749
 
750
+ ##
751
+ # For testing with pattern matching (only supported with Ruby 3.0 and later)
752
+ #
753
+ # # pass
754
+ # refute_pattern { [1,2,3] => [String] }
755
+ #
756
+ # # fail "NoMatchingPatternError expected, but nothing was raised."
757
+ # refute_pattern { [1,2,3] => [Integer, Integer, Integer] }
758
+ #
759
+ # This assertion expects a NoMatchingPatternError exception, and will fail if none is raised. Any
760
+ # other exceptions will be raised as normal and generate a test error.
761
+
762
+ def refute_pattern
763
+ raise NotImplementedError, "only available in Ruby 3.0+" unless RUBY_VERSION >= "3.0"
764
+ flunk "refute_pattern requires a block to capture errors." unless block_given?
765
+
766
+ begin
767
+ yield
768
+ flunk("NoMatchingPatternError expected, but nothing was raised.")
769
+ rescue NoMatchingPatternError
770
+ pass
771
+ end
772
+ end
773
+
629
774
  ##
630
775
  # Fails if +o1+ is not +op+ +o2+. Eg:
631
776
  #
@@ -638,6 +783,14 @@ module Minitest
638
783
  refute o1.__send__(op, o2), msg
639
784
  end
640
785
 
786
+ ##
787
+ # Fails if +path+ exists.
788
+
789
+ def refute_path_exists path, msg = nil
790
+ msg = message(msg) { "Expected path '#{path}' to not exist" }
791
+ refute File.exist?(path), msg
792
+ end
793
+
641
794
  ##
642
795
  # For testing with predicates.
643
796
  #
@@ -683,6 +836,18 @@ module Minitest
683
836
  raise Minitest::Skip, msg, bt
684
837
  end
685
838
 
839
+ ##
840
+ # Skips the current run until a given date (in the local time
841
+ # zone). This allows you to put some fixes on hold until a later
842
+ # date, but still holds you accountable and prevents you from
843
+ # forgetting it.
844
+
845
+ def skip_until y,m,d,msg
846
+ skip msg if Time.now < Time.local(y, m, d)
847
+ where = caller.first.rpartition(':in').reject(&:empty?).first
848
+ warn "Stale skip_until %p at %s" % [msg, where]
849
+ end
850
+
686
851
  ##
687
852
  # Was this testcase skipped? Meant for #teardown.
688
853
 
@@ -109,8 +109,8 @@ module Minitest
109
109
  # is applied against the slope itself. As such, you probably want
110
110
  # to tighten it from the default.
111
111
  #
112
- # See http://www.graphpad.com/curvefit/goodness_of_fit.htm for
113
- # more details.
112
+ # See https://www.graphpad.com/guides/prism/8/curve-fitting/reg_intepretingnonlinr2.htm
113
+ # for more details.
114
114
  #
115
115
  # Fit is calculated by #fit_linear.
116
116
  #
@@ -217,7 +217,7 @@ module Minitest
217
217
  ##
218
218
  # Takes an array of x/y pairs and calculates the general R^2 value.
219
219
  #
220
- # See: http://en.wikipedia.org/wiki/Coefficient_of_determination
220
+ # See: https://en.wikipedia.org/wiki/Coefficient_of_determination
221
221
 
222
222
  def fit_error xys
223
223
  y_bar = sigma(xys) { |_, y| y } / xys.size.to_f
@@ -232,7 +232,7 @@ module Minitest
232
232
  #
233
233
  # Takes x and y values and returns [a, b, r^2].
234
234
  #
235
- # See: http://mathworld.wolfram.com/LeastSquaresFittingExponential.html
235
+ # See: https://mathworld.wolfram.com/LeastSquaresFittingExponential.html
236
236
 
237
237
  def fit_exponential xs, ys
238
238
  n = xs.size
@@ -254,7 +254,7 @@ module Minitest
254
254
  #
255
255
  # Takes x and y values and returns [a, b, r^2].
256
256
  #
257
- # See: http://mathworld.wolfram.com/LeastSquaresFittingLogarithmic.html
257
+ # See: https://mathworld.wolfram.com/LeastSquaresFittingLogarithmic.html
258
258
 
259
259
  def fit_logarithmic xs, ys
260
260
  n = xs.size
@@ -276,7 +276,7 @@ module Minitest
276
276
  #
277
277
  # Takes x and y values and returns [a, b, r^2].
278
278
  #
279
- # See: http://mathworld.wolfram.com/LeastSquaresFitting.html
279
+ # See: https://mathworld.wolfram.com/LeastSquaresFitting.html
280
280
 
281
281
  def fit_linear xs, ys
282
282
  n = xs.size
@@ -298,7 +298,7 @@ module Minitest
298
298
  #
299
299
  # Takes x and y values and returns [a, b, r^2].
300
300
  #
301
- # See: http://mathworld.wolfram.com/LeastSquaresFittingPowerLaw.html
301
+ # See: https://mathworld.wolfram.com/LeastSquaresFittingPowerLaw.html
302
302
 
303
303
  def fit_power xs, ys
304
304
  n = xs.size
@@ -318,7 +318,7 @@ module Minitest
318
318
  # Enumerates over +enum+ mapping +block+ if given, returning the
319
319
  # sum of the result. Eg:
320
320
  #
321
- # sigma([1, 2, 3]) # => 1 + 2 + 3 => 7
321
+ # sigma([1, 2, 3]) # => 1 + 2 + 3 => 6
322
322
  # sigma([1, 2, 3]) { |n| n ** 2 } # => 1 + 4 + 9 => 14
323
323
 
324
324
  def sigma enum, &block
@@ -418,6 +418,37 @@ module Minitest
418
418
  assert_performance_exponential threshold, &work
419
419
  end
420
420
  end
421
+
422
+
423
+ ##
424
+ # Create a benchmark that verifies that the performance is logarithmic.
425
+ #
426
+ # describe "my class Bench" do
427
+ # bench_performance_logarithmic "algorithm" do |n|
428
+ # @obj.algorithm(n)
429
+ # end
430
+ # end
431
+
432
+ def self.bench_performance_logarithmic name, threshold = 0.99, &work
433
+ bench name do
434
+ assert_performance_logarithmic threshold, &work
435
+ end
436
+ end
437
+
438
+ ##
439
+ # Create a benchmark that verifies that the performance is power.
440
+ #
441
+ # describe "my class Bench" do
442
+ # bench_performance_power "algorithm" do |n|
443
+ # @obj.algorithm(n)
444
+ # end
445
+ # end
446
+
447
+ def self.bench_performance_power name, threshold = 0.99, &work
448
+ bench name do
449
+ assert_performance_power threshold, &work
450
+ end
451
+ end
421
452
  end
422
453
 
423
454
  Minitest::Spec.register_spec_type(/Bench(mark)?$/, Minitest::BenchSpec)