@@ -28,9 +28,13 @@ module CoreAssertions
2828 require_relative '../../envutil'
2929 # for ruby core testing
3030 include MiniTest ::Assertions
31+
32+ # Compatibility hack for assert_raise
33+ Test ::Unit ::AssertionFailedError = MiniTest ::Assertion
3134 else
3235 module MiniTest
33- class Skip ; end
36+ class Assertion < Exception ; end
37+ class Skip < Assertion ; end
3438 end
3539
3640 require 'pp'
@@ -90,6 +94,112 @@ def assert_in_out_err(args, test_stdin = "", test_stdout = [], test_stderr = [],
9094 end
9195 end
9296
97+ if defined? ( RubyVM ::InstructionSequence )
98+ def syntax_check ( code , fname , line )
99+ code = code . dup . force_encoding ( Encoding ::UTF_8 )
100+ RubyVM ::InstructionSequence . compile ( code , fname , fname , line )
101+ :ok
102+ ensure
103+ raise if SyntaxError === $!
104+ end
105+ else
106+ def syntax_check ( code , fname , line )
107+ code = code . b
108+ code . sub! ( /\A (?:\xef \xbb \xbf )?(\s *\# .*$)*(\n )?/n ) {
109+ "#$&#{ "\n " if $1 && !$2} BEGIN{throw tag, :ok}\n "
110+ }
111+ code = code . force_encoding ( Encoding ::UTF_8 )
112+ catch { |tag | eval ( code , binding , fname , line - 1 ) }
113+ end
114+ end
115+
116+ # :call-seq:
117+ # assert_nothing_raised( *args, &block )
118+ #
119+ #If any exceptions are given as arguments, the assertion will
120+ #fail if one of those exceptions are raised. Otherwise, the test fails
121+ #if any exceptions are raised.
122+ #
123+ #The final argument may be a failure message.
124+ #
125+ # assert_nothing_raised RuntimeError do
126+ # raise Exception #Assertion passes, Exception is not a RuntimeError
127+ # end
128+ #
129+ # assert_nothing_raised do
130+ # raise Exception #Assertion fails
131+ # end
132+ def assert_nothing_raised ( *args )
133+ self . _assertions += 1
134+ if Module === args . last
135+ msg = nil
136+ else
137+ msg = args . pop
138+ end
139+ begin
140+ line = __LINE__ ; yield
141+ rescue MiniTest ::Skip
142+ raise
143+ rescue Exception => e
144+ bt = e . backtrace
145+ as = e . instance_of? ( MiniTest ::Assertion )
146+ if as
147+ ans = /\A #{ Regexp . quote ( __FILE__ ) } :#{ line } :in /o
148+ bt . reject! { |ln | ans =~ ln }
149+ end
150+ if ( ( args . empty? && !as ) ||
151+ args . any? { |a | a . instance_of? ( Module ) ? e . is_a? ( a ) : e . class == a } )
152+ msg = message ( msg ) { "Exception raised:\n <#{ mu_pp ( e ) } >" }
153+ raise MiniTest ::Assertion , msg . call , bt
154+ else
155+ raise
156+ end
157+ end
158+ end
159+
160+ def prepare_syntax_check ( code , fname = nil , mesg = nil , verbose : nil )
161+ fname ||= caller_locations ( 2 , 1 ) [ 0 ]
162+ mesg ||= fname . to_s
163+ verbose , $VERBOSE = $VERBOSE, verbose
164+ case
165+ when Array === fname
166+ fname , line = *fname
167+ when defined? ( fname . path ) && defined? ( fname . lineno )
168+ fname , line = fname . path , fname . lineno
169+ else
170+ line = 1
171+ end
172+ yield ( code , fname , line , message ( mesg ) {
173+ if code . end_with? ( "\n " )
174+ "```\n #{ code } ```\n "
175+ else
176+ "```\n #{ code } \n ```\n " "no-newline"
177+ end
178+ } )
179+ ensure
180+ $VERBOSE = verbose
181+ end
182+
183+ def assert_valid_syntax ( code , *args , **opt )
184+ prepare_syntax_check ( code , *args , **opt ) do |src , fname , line , mesg |
185+ yield if defined? ( yield )
186+ assert_nothing_raised ( SyntaxError , mesg ) do
187+ assert_equal ( :ok , syntax_check ( src , fname , line ) , mesg )
188+ end
189+ end
190+ end
191+
192+ def assert_normal_exit ( testsrc , message = '' , child_env : nil , **opt )
193+ assert_valid_syntax ( testsrc , caller_locations ( 1 , 1 ) [ 0 ] )
194+ if child_env
195+ child_env = [ child_env ]
196+ else
197+ child_env = [ ]
198+ end
199+ out , _ , status = EnvUtil . invoke_ruby ( child_env + %W' -W0 ' , testsrc , true , :merge_to_stdout , **opt )
200+ assert !status . signaled? , FailDesc [ status , message , out ]
201+ end
202+
93203 def assert_ruby_status ( args , test_stdin = "" , message = nil , **opt )
94204 out , _ , status = EnvUtil . invoke_ruby ( args , test_stdin , true , :merge_to_stdout , **opt )
95205 desc = FailDesc [ status , message , out ]
@@ -100,35 +210,55 @@ def assert_ruby_status(args, test_stdin="", message=nil, **opt)
100210
101211 ABORT_SIGNALS = Signal . list . values_at ( *%w" ILL ABRT BUS SEGV TERM " )
102212
213+ def separated_runner ( out = nil )
214+ out = out ? IO . new ( out , 'w' ) : STDOUT
215+ at_exit {
216+ out . puts [ Marshal . dump ( $!) ] . pack ( 'm' ) , "assertions=\# {self._assertions}"
217+ }
218+ Test ::Unit ::Runner . class_variable_set ( :@@stop_auto_run , true )
219+ end
220+
103221 def assert_separately ( args , file = nil , line = nil , src , ignore_stderr : nil , **opt )
104222 unless file and line
105223 loc , = caller_locations ( 1 , 1 )
106224 file ||= loc . path
107225 line ||= loc . lineno
108226 end
227+ capture_stdout = true
228+ unless /mswin|mingw/ =~ RUBY_PLATFORM
229+ capture_stdout = false
230+ opt [ :out ] = MiniTest ::Unit . output
231+ res_p , res_c = IO . pipe
232+ opt [ res_c . fileno ] = res_c . fileno
233+ end
109234 src = <<eom
110235# -*- coding: #{ line += __LINE__ ; src . encoding } ; -*-
236+ BEGIN {
111237 require "test/unit";include Test::Unit::Assertions;require #{ ( __dir__ + "/core_assertions" ) . dump } ;include Test::Unit::CoreAssertions
112- END {
113- puts [Marshal.dump($!)].pack('m'), "assertions=\# {self._assertions}"
114- }
238+ separated_runner #{ res_c &.fileno }
239+ }
115240#{ line -= __LINE__ ; src }
116- class Test::Unit::Runner
117- @@stop_auto_run = true
118- end
119241eom
120242 args = args . dup
121243 args . insert ( ( Hash === args . first ? 1 : 0 ) , "-w" , "--disable=gems" , *$:. map { |l | "-I#{ l } " } )
122- stdout , stderr , status = EnvUtil . invoke_ruby ( args , src , true , true , **opt )
244+ stdout , stderr , status = EnvUtil . invoke_ruby ( args , src , capture_stdout , true , **opt )
245+ if res_c
246+ res_c . close
247+ res = res_p . read
248+ res_p . close
249+ else
250+ res = stdout
251+ end
123252 abort = status . coredump? || ( status . signaled? && ABORT_SIGNALS . include? ( status . termsig ) )
124253 assert ( !abort , FailDesc [ status , nil , stderr ] )
125- self . _assertions += stdout [ /^assertions=(\d +)/ , 1 ] . to_i
254+ self . _assertions += res [ /^assertions=(\d +)/ , 1 ] . to_i
126255 begin
127- res = Marshal . load ( stdout . unpack ( "m" ) [ 0 ] )
256+ res = Marshal . load ( res . unpack1 ( "m" ) )
128257 rescue => marshal_error
129258 ignore_stderr = nil
259+ res = nil
130260 end
131- if res
261+ if res and ! ( SystemExit === res )
132262 if bt = res . backtrace
133263 bt . each do |l |
134264 l . sub! ( /\A -:(\d +)/ ) { "#{ file } :#{ line + $1. to_i } " }
@@ -137,7 +267,7 @@ class Test::Unit::Runner
137267 else
138268 res . set_backtrace ( caller )
139269 end
140- raise res unless SystemExit === res
270+ raise res
141271 end
142272
143273 # really is it succeed?
@@ -212,7 +342,7 @@ def assert_raise(*exp, &b)
212342 }
213343
214344 assert expected , proc {
215- exception_details ( e , message ( msg ) { "#{ mu_pp ( exp ) } exception expected, not" } . call )
345+ flunk ( message ( msg ) { "#{ mu_pp ( exp ) } exception expected, not #{ mu_pp ( e ) } " } )
216346 }
217347
218348 return e
@@ -377,7 +507,7 @@ def assert_join_threads(threads, message = nil)
377507 msg = "exceptions on #{ errs . length } threads:\n " +
378508 errs . map { |t , err |
379509 "#{ t . inspect } :\n " +
380- err . full_message ( highlight : false , order : :top )
510+ RUBY_VERSION >= "2.5.0" ? err . full_message ( highlight : false , order : :top ) : err . message
381511 } . join ( "\n ---\n " )
382512 if message
383513 msg = "#{ message } \n #{ msg } "
0 commit comments