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- err . full_message ( highlight : false , order : :top )
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,58 @@ 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+ %w[
742+ CLOCK_THREAD_CPUTIME_ID CLOCK_PROCESS_CPUTIME_ID
743+ CLOCK_MONOTONIC
744+ ] . find do |clk |
745+ if Process . const_defined? ( clk )
746+ clk = clk . to_sym
747+ begin
748+ Process . clock_gettime ( clk )
749+ rescue
750+ # Constants may be defined but not implemented, e.g., mingw.
751+ else
752+ PERFORMANCE_CLOCK = clk
753+ end
754+ end
755+ end
756+
757+ # Expect +seq+ to respond to +first+ and +each+ methods, e.g.,
758+ # Array, Range, Enumerator::ArithmeticSequence and other
759+ # Enumerable-s, and each elements should be size factors.
760+ #
761+ # :yield: each elements of +seq+.
762+ def assert_linear_performance ( seq , rehearsal : nil , pre : -> ( n ) { n } )
763+ pend "No PERFORMANCE_CLOCK found" unless defined? ( PERFORMANCE_CLOCK )
764+
765+ # Timeout testing generally doesn't work when RJIT compilation happens.
766+ rjit_enabled = defined? ( RubyVM ::RJIT ) && RubyVM ::RJIT . enabled?
767+ measure = proc do |arg , message |
768+ st = Process . clock_gettime ( PERFORMANCE_CLOCK )
769+ yield ( *arg )
770+ t = ( Process . clock_gettime ( PERFORMANCE_CLOCK ) - st )
771+ assert_operator 0 , :<= , t , message unless rjit_enabled
772+ t
773+ end
774+
775+ first = seq . first
776+ *arg = pre . call ( first )
777+ times = ( 0 ..( rehearsal || ( 2 * first ) ) ) . map do
778+ measure [ arg , "rehearsal" ] . nonzero?
779+ end
780+ times . compact!
781+ tmin , tmax = times . minmax
782+ tbase = 10 ** Math . log10 ( tmax * ( [ ( tmax / tmin ) , 2 ] . max ** 2 ) ) . ceil
783+ info = "(tmin: #{ tmin } , tmax: #{ tmax } , tbase: #{ tbase } )"
784+
785+ seq . each do |i |
786+ next if i == first
787+ t = tbase * i . fdiv ( first )
788+ *arg = pre . call ( i )
789+ message = "[#{ i } ]: in #{ t } s #{ info } "
790+ Timeout . timeout ( t , Timeout ::Error , message ) do
791+ measure [ arg , message ]
745792 end
746- else
747- super
748793 end
749794 end
750795
@@ -763,6 +808,11 @@ def diff(exp, act)
763808 end
764809 q . output
765810 end
811+
812+ def new_test_token
813+ token = "\e [7;1m#{ $$. to_s } :#{ Time . now . strftime ( '%s.%L' ) } :#{ rand ( 0x10000 ) . to_s ( 16 ) } :\e [m"
814+ return token . dump , Regexp . quote ( token )
815+ end
766816 end
767817 end
768818end
0 commit comments