33module Test
44 module Unit
55 module Assertions
6+ def assert_raises ( *exp , &b )
7+ raise NoMethodError , "use assert_raise" , caller
8+ end
9+
610 def _assertions = n # :nodoc:
711 @_assertions = n
812 end
@@ -16,16 +20,24 @@ def _assertions # :nodoc:
1620
1721 def message msg = nil , ending = nil , &default
1822 proc {
19- msg = msg . call . chomp ( "." ) if Proc === msg
20- custom_message = "#{ msg } .\n " unless msg . nil? or msg . to_s . empty?
21- "#{ custom_message } #{ default . call } #{ ending || "." } "
23+ ending ||= ( ending_pattern = /(?<!\. )\z / ; "." )
24+ ending_pattern ||= /(?<!#{ Regexp . quote ( ending ) } )\z /
25+ msg = msg . call if Proc === msg
26+ ary = [ msg , ( default . call if default ) ] . compact . reject ( &:empty? )
27+ ary . map! { |str | str . to_s . sub ( ending_pattern , ending ) }
28+ begin
29+ ary . join ( "\n " )
30+ rescue Encoding ::CompatibilityError
31+ ary . map ( &:b ) . join ( "\n " )
32+ end
2233 }
2334 end
2435 end
2536
2637 module CoreAssertions
2738 require_relative 'envutil'
2839 require 'pp'
40+ nil . pretty_inspect
2941
3042 def mu_pp ( obj ) #:nodoc:
3143 obj . pretty_inspect . chomp
@@ -99,15 +111,15 @@ def syntax_check(code, fname, line)
99111 end
100112
101113 def assert_no_memory_leak ( args , prepare , code , message = nil , limit : 2.0 , rss : false , **opt )
102- # TODO: consider choosing some appropriate limit for MJIT and stop skipping this once it does not randomly fail
114+ # TODO: consider choosing some appropriate limit for RJIT and stop skipping this once it does not randomly fail
115+ pend 'assert_no_memory_leak may consider RJIT memory usage as leak' if defined? ( RubyVM ::RJIT ) && RubyVM ::RJIT . enabled?
116+ # For previous versions which implemented MJIT
103117 pend 'assert_no_memory_leak may consider MJIT memory usage as leak' if defined? ( RubyVM ::MJIT ) && RubyVM ::MJIT . enabled?
104118
105119 require_relative 'memory_status'
106120 raise Test ::Unit ::PendedError , "unsupported platform" unless defined? ( Memory ::Status )
107121
108- token = "\e [7;1m#{ $$. to_s } :#{ Time . now . strftime ( '%s.%L' ) } :#{ rand ( 0x10000 ) . to_s ( 16 ) } :\e [m"
109- token_dump = token . dump
110- token_re = Regexp . quote ( token )
122+ token_dump , token_re = new_test_token
111123 envs = args . shift if Array === args and Hash === args . first
112124 args = [
113125 "--disable=gems" ,
@@ -167,27 +179,15 @@ def assert_nothing_raised(*args)
167179 msg = args . pop
168180 end
169181 begin
170- line = __LINE__ ; yield
171- rescue Test ::Unit ::PendedError
182+ yield
183+ rescue Test ::Unit ::PendedError , * ( Test :: Unit :: AssertionFailedError if args . empty? )
172184 raise
173- rescue Exception => e
174- bt = e . backtrace
175- as = e . instance_of? ( Test ::Unit ::AssertionFailedError )
176- if as
177- ans = /\A #{ Regexp . quote ( __FILE__ ) } :#{ line } :in /o
178- bt . reject! { |ln | ans =~ ln }
179- end
180- if ( ( args . empty? && !as ) ||
181- args . any? { |a | a . instance_of? ( Module ) ? e . is_a? ( a ) : e . class == a } )
182- msg = message ( msg ) {
183- "Exception raised:\n <#{ mu_pp ( e ) } >\n " +
184- "Backtrace:\n " +
185- e . backtrace . map { |frame | " #{ frame } " } . join ( "\n " )
186- }
187- raise Test ::Unit ::AssertionFailedError , msg . call , bt
188- else
189- raise
190- end
185+ rescue *( args . empty? ? Exception : args ) => e
186+ msg = message ( msg ) {
187+ "Exception raised:\n <#{ mu_pp ( e ) } >\n " "Backtrace:\n " <<
188+ Test . filter_backtrace ( e . backtrace ) . map { |frame | " #{ frame } " } . join ( "\n " )
189+ }
190+ raise Test ::Unit ::AssertionFailedError , msg . call , e . backtrace
191191 end
192192 end
193193
@@ -244,13 +244,17 @@ def assert_ruby_status(args, test_stdin="", message=nil, **opt)
244244
245245 ABORT_SIGNALS = Signal . list . values_at ( *%w" ILL ABRT BUS SEGV TERM " )
246246
247- def separated_runner ( out = nil )
247+ def separated_runner ( token , out = nil )
248248 include ( *Test ::Unit ::TestCase . ancestors . select { |c | !c . is_a? ( Class ) } )
249249 out = out ? IO . new ( out , 'w' ) : STDOUT
250250 at_exit {
251- out . puts [ Marshal . dump ( $!) ] . pack ( 'm' ) , "assertions=#{ self . _assertions } "
251+ out . puts " #{ token } <error>" , [ Marshal . dump ( $!) ] . pack ( 'm' ) , "#{ token } </error>" , " #{ token } assertions=#{ self . _assertions } "
252252 }
253- Test ::Unit ::Runner . class_variable_set ( :@@stop_auto_run , true ) if defined? ( Test ::Unit ::Runner )
253+ if defined? ( Test ::Unit ::Runner )
254+ Test ::Unit ::Runner . class_variable_set ( :@@stop_auto_run , true )
255+ elsif defined? ( Test ::Unit ::AutoRunner )
256+ Test ::Unit ::AutoRunner . need_auto_run = false
257+ end
254258 end
255259
256260 def assert_separately ( args , file = nil , line = nil , src , ignore_stderr : nil , **opt )
@@ -260,22 +264,24 @@ def assert_separately(args, file = nil, line = nil, src, ignore_stderr: nil, **o
260264 line ||= loc . lineno
261265 end
262266 capture_stdout = true
263- unless /mswin|mingw/ =~ RUBY_PLATFORM
267+ unless /mswin|mingw/ =~ RbConfig :: CONFIG [ 'host_os' ]
264268 capture_stdout = false
265269 opt [ :out ] = Test ::Unit ::Runner . output if defined? ( Test ::Unit ::Runner )
266270 res_p , res_c = IO . pipe
267271 opt [ :ios ] = [ res_c ]
268272 end
273+ token_dump , token_re = new_test_token
269274 src = <<eom
270275# -*- coding: #{ line += __LINE__ ; src . encoding } ; -*-
271276BEGIN {
272277 require "test/unit";include Test::Unit::Assertions;require #{ __FILE__ . dump } ;include Test::Unit::CoreAssertions
273- separated_runner #{ res_c &.fileno }
278+ separated_runner #{ token_dump } , #{ res_c &.fileno || 'nil' }
274279}
275280#{ line -= __LINE__ ; src }
276281eom
277282 args = args . dup
278283 args . insert ( ( Hash === args . first ? 1 : 0 ) , "-w" , "--disable=gems" , *$:. map { |l | "-I#{ l } " } )
284+ args << "--debug" if RUBY_ENGINE == 'jruby' # warning: tracing (e.g. set_trace_func) will not capture all events without --debug flag
279285 stdout , stderr , status = EnvUtil . invoke_ruby ( args , src , capture_stdout , true , **opt )
280286 ensure
281287 if res_c
@@ -288,9 +294,9 @@ def assert_separately(args, file = nil, line = nil, src, ignore_stderr: nil, **o
288294 raise if $!
289295 abort = status . coredump? || ( status . signaled? && ABORT_SIGNALS . include? ( status . termsig ) )
290296 assert ( !abort , FailDesc [ status , nil , stderr ] )
291- self . _assertions += res [ /^assertions=(\d +)/ , 1 ] . to_i
297+ self . _assertions += res [ /^#{ token_re } assertions=(\d +)/ , 1 ] . to_i
292298 begin
293- res = Marshal . load ( res . unpack1 ( "m" ) )
299+ res = Marshal . load ( res [ /^ #{ token_re } <error> \n \K .* \n (?= #{ token_re } < \/ error>$)/m ] . unpack1 ( "m" ) )
294300 rescue => marshal_error
295301 ignore_stderr = nil
296302 res = nil
@@ -463,7 +469,7 @@ def assert_raise_with_message(exception, expected, msg = nil, &block)
463469 ex
464470 end
465471
466- MINI_DIR = File . join ( File . dirname ( File . expand_path ( __FILE__ ) ) , "minitest " ) #:nodoc:
472+ TEST_DIR = File . join ( __dir__ , "test/unit " ) #:nodoc:
467473
468474 # :call-seq:
469475 # assert(test, [failure_message])
@@ -483,7 +489,7 @@ def assert(test, *msgs)
483489 when nil
484490 msgs . shift
485491 else
486- bt = caller . reject { |s | s . start_with? ( MINI_DIR ) }
492+ bt = caller . reject { |s | s . start_with? ( TEST_DIR ) }
487493 raise ArgumentError , "assertion message must be String or Proc, but #{ msg . class } was given." , bt
488494 end unless msgs . empty?
489495 super
@@ -506,7 +512,7 @@ def assert_respond_to(obj, (meth, *priv), msg = nil)
506512 return assert obj . respond_to? ( meth , *priv ) , msg
507513 end
508514 #get rid of overcounting
509- if caller_locations ( 1 , 1 ) [ 0 ] . path . start_with? ( MINI_DIR )
515+ if caller_locations ( 1 , 1 ) [ 0 ] . path . start_with? ( TEST_DIR )
510516 return if obj . respond_to? ( meth )
511517 end
512518 super ( obj , meth , msg )
@@ -529,17 +535,17 @@ def assert_not_respond_to(obj, (meth, *priv), msg = nil)
529535 return assert !obj . respond_to? ( meth , *priv ) , msg
530536 end
531537 #get rid of overcounting
532- if caller_locations ( 1 , 1 ) [ 0 ] . path . start_with? ( MINI_DIR )
538+ if caller_locations ( 1 , 1 ) [ 0 ] . path . start_with? ( TEST_DIR )
533539 return unless obj . respond_to? ( meth )
534540 end
535541 refute_respond_to ( obj , meth , msg )
536542 end
537543
538- # pattern_list is an array which contains regexp and :*.
544+ # pattern_list is an array which contains regexp, string and :*.
539545 # :* means any sequence.
540546 #
541547 # pattern_list is anchored.
542- # Use [:*, regexp, :*] for non-anchored match.
548+ # Use [:*, regexp/string , :*] for non-anchored match.
543549 def assert_pattern_list ( pattern_list , actual , message = nil )
544550 rest = actual
545551 anchored = true
@@ -548,11 +554,13 @@ def assert_pattern_list(pattern_list, actual, message=nil)
548554 anchored = false
549555 else
550556 if anchored
551- match = / \A #{ pattern } / . match ( rest )
557+ match = rest . rindex ( pattern , 0 )
552558 else
553- match = pattern . match ( rest )
559+ match = rest . index ( pattern )
554560 end
555- unless match
561+ if match
562+ post_match = $~ ? $~. post_match : rest [ match +pattern . size ..-1 ]
563+ else
556564 msg = message ( msg ) {
557565 expect_msg = "Expected #{ mu_pp pattern } \n "
558566 if /\n [^\n ]/ =~ rest
@@ -569,7 +577,7 @@ def assert_pattern_list(pattern_list, actual, message=nil)
569577 }
570578 assert false , msg
571579 end
572- rest = match . post_match
580+ rest = post_match
573581 anchored = true
574582 end
575583 }
@@ -596,14 +604,14 @@ def assert_warn(*args)
596604
597605 def assert_deprecated_warning ( mesg = /deprecated/ )
598606 assert_warning ( mesg ) do
599- Warning [ :deprecated ] = true
607+ Warning [ :deprecated ] = true if Warning . respond_to? ( :[]= )
600608 yield
601609 end
602610 end
603611
604612 def assert_deprecated_warn ( mesg = /deprecated/ )
605613 assert_warn ( mesg ) do
606- Warning [ :deprecated ] = true
614+ Warning [ :deprecated ] = true if Warning . respond_to? ( :[]= )
607615 yield
608616 end
609617 end
@@ -641,7 +649,7 @@ def initialize
641649
642650 def for ( key )
643651 @count += 1
644- yield
652+ yield key
645653 rescue Exception => e
646654 @failures [ key ] = [ @count , e ]
647655 end
@@ -695,7 +703,7 @@ def assert_join_threads(threads, message = nil)
695703 msg = "exceptions on #{ errs . length } threads:\n " +
696704 errs . map { |t , err |
697705 "#{ t . inspect } :\n " +
698- RUBY_VERSION >= "2.5.0" ? err . full_message ( highlight : false , order : :top ) : err . message
706+ ( err . respond_to? ( :full_message ) ? err . full_message ( highlight : false , order : :top ) : err . message )
699707 } . join ( "\n ---\n " )
700708 if message
701709 msg = "#{ message } \n #{ msg } "
@@ -730,21 +738,36 @@ def assert_all_assertions_foreach(msg = nil, *keys, &block)
730738 end
731739 alias all_assertions_foreach assert_all_assertions_foreach
732740
733- def message ( msg = nil , *args , &default ) # :nodoc:
734- if Proc === msg
735- super ( nil , *args ) do
736- ary = [ msg . call , ( default . call if default ) ] . compact . reject ( &:empty? )
737- if 1 < ary . length
738- ary [ 0 ...-1 ] = ary [ 0 ...-1 ] . map { |str | str . sub ( /(?<!\. )\z / , '.' ) }
739- end
740- begin
741- ary . join ( "\n " )
742- rescue Encoding ::CompatibilityError
743- ary . map ( &:b ) . join ( "\n " )
744- end
741+ # Expect +seq+ to respond to +first+ and +each+ methods, e.g.,
742+ # Array, Range, Enumerator::ArithmeticSequence and other
743+ # Enumerable-s, and each elements should be size factors.
744+ #
745+ # :yield: each elements of +seq+.
746+ def assert_linear_performance ( seq , rehearsal : nil , pre : -> ( n ) { n } )
747+ first = seq . first
748+ *arg = pre . call ( first )
749+ times = ( 0 ..( rehearsal || ( 2 * first ) ) ) . map do
750+ st = Process . clock_gettime ( Process ::CLOCK_MONOTONIC )
751+ yield ( *arg )
752+ t = ( Process . clock_gettime ( Process ::CLOCK_MONOTONIC ) - st )
753+ assert_operator 0 , :<= , t
754+ t . nonzero?
755+ end
756+ times . compact!
757+ tmin , tmax = times . minmax
758+ tmax *= tmax / tmin
759+ tmax = 10 **Math . log10 ( tmax ) . ceil
760+
761+ seq . each do |i |
762+ next if i == first
763+ t = tmax * i . fdiv ( first )
764+ *arg = pre . call ( i )
765+ message = "[#{ i } ]: in #{ t } s"
766+ Timeout . timeout ( t , Timeout ::Error , message ) do
767+ st = Process . clock_gettime ( Process ::CLOCK_MONOTONIC )
768+ yield ( *arg )
769+ assert_operator ( Process . clock_gettime ( Process ::CLOCK_MONOTONIC ) - st ) , :<= , t , message
745770 end
746- else
747- super
748771 end
749772 end
750773
@@ -763,6 +786,11 @@ def diff(exp, act)
763786 end
764787 q . output
765788 end
789+
790+ def new_test_token
791+ token = "\e [7;1m#{ $$. to_s } :#{ Time . now . strftime ( '%s.%L' ) } :#{ rand ( 0x10000 ) . to_s ( 16 ) } :\e [m"
792+ return token . dump , Regexp . quote ( token )
793+ end
766794 end
767795 end
768796end
0 commit comments