mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
80b5a0ff2a
Cfuncs that use rb_scan_args with the : entry suffer similar keyword argument separation issues that Ruby methods suffer if the cfuncs accept optional or variable arguments. This makes the following changes to : handling. * Treats as **kw, prompting keyword argument separation warnings if called with a positional hash. * Do not look for an option hash if empty keywords are provided. For backwards compatibility, treat an empty keyword splat as a empty mandatory positional hash argument, but emit a a warning, as this behavior will be removed in Ruby 3. The argument number check needs to be moved lower so it can correctly handle an empty positional argument being added. * If the last argument is nil and it is necessary to treat it as an option hash in order to make sure all arguments are processed, continue to treat the last argument as the option hash. Emit a warning in this case, as this behavior will be removed in Ruby 3. * If splitting the keyword hash into two hashes, issue a warning, as we will not be splitting hashes in Ruby 3. * If the keyword argument is required to fill a mandatory positional argument, continue to do so, but emit a warning as this behavior will be going away in Ruby 3. * If keyword arguments are provided and the last argument is not a hash, that indicates something wrong. This can happen if a cfunc is calling rb_scan_args multiple times, and providing arguments that were not passed to it from Ruby. Callers need to switch to the new rb_scan_args_kw function, which allows passing of whether keywords were provided. This commit fixes all warnings caused by the changes above. It switches some function calls to *_kw versions with appropriate kw_splat flags. If delegating arguments, RB_PASS_CALLED_KEYWORDS is used. If creating new arguments, RB_PASS_KEYWORDS is used if the last argument is a hash to be treated as keywords. In open_key_args in io.c, use rb_scan_args_kw. In this case, the arguments provided come from another C function, not Ruby. The last argument may or may not be a hash, so we can't set keyword argument mode. However, if it is a hash, we don't want to warn when treating it as keywords. In Ruby files, make sure to appropriately use keyword splats or literal keywords when calling Cfuncs that now issue keyword argument separation warnings through rb_scan_args. Also, make sure not to pass nil in place of an option hash. Work around Kernel#warn warnings due to problems in the Rubygems override of the method. There is an open pull request to fix these issues in Rubygems, but part of the Rubygems tests for their override fail on ruby-head due to rb_scan_args not recognizing empty keyword splats, which this commit fixes. Implementation wise, adding rb_scan_args_kw is kind of a pain, because rb_scan_args takes a variable number of arguments. In order to not duplicate all the code, the function internals need to be split into two functions taking a va_list, and to avoid passing in a ton of arguments, a single struct argument is used to handle the variables previously local to the function.
1482 lines
35 KiB
Ruby
1482 lines
35 KiB
Ruby
# frozen_string_literal: false
|
|
require 'test/unit'
|
|
require 'tempfile'
|
|
|
|
class TestException < Test::Unit::TestCase
|
|
def test_exception_rescued
|
|
begin
|
|
raise "this must be handled"
|
|
assert(false)
|
|
rescue
|
|
assert(true)
|
|
end
|
|
end
|
|
|
|
def test_exception_retry
|
|
bad = true
|
|
begin
|
|
raise "this must be handled no.2"
|
|
rescue
|
|
if bad
|
|
bad = false
|
|
retry
|
|
end
|
|
assert(!bad)
|
|
end
|
|
assert(true)
|
|
end
|
|
|
|
def test_exception_in_rescue
|
|
string = "this must be handled no.3"
|
|
assert_raise_with_message(RuntimeError, string) do
|
|
begin
|
|
raise "exception in rescue clause"
|
|
rescue
|
|
raise string
|
|
end
|
|
assert(false)
|
|
end
|
|
end
|
|
|
|
def test_exception_in_ensure
|
|
string = "exception in ensure clause"
|
|
assert_raise_with_message(RuntimeError, string) do
|
|
begin
|
|
raise "this must be handled no.4"
|
|
ensure
|
|
assert_instance_of(RuntimeError, $!)
|
|
assert_equal("this must be handled no.4", $!.message)
|
|
raise "exception in ensure clause"
|
|
end
|
|
assert(false)
|
|
end
|
|
end
|
|
|
|
def test_exception_ensure
|
|
bad = true
|
|
begin
|
|
begin
|
|
raise "this must be handled no.5"
|
|
ensure
|
|
bad = false
|
|
end
|
|
rescue
|
|
end
|
|
assert(!bad)
|
|
end
|
|
|
|
def test_exception_ensure_2 # just duplication?
|
|
bad = true
|
|
begin
|
|
begin
|
|
raise "this must be handled no.6"
|
|
ensure
|
|
bad = false
|
|
end
|
|
rescue
|
|
end
|
|
assert(!bad)
|
|
end
|
|
|
|
def test_errinfo_in_debug
|
|
bug9568 = EnvUtil.labeled_class("[ruby-core:61091] [Bug #9568]", RuntimeError) do
|
|
def to_s
|
|
require '\0'
|
|
rescue LoadError
|
|
self.class.to_s
|
|
end
|
|
end
|
|
|
|
err = EnvUtil.verbose_warning do
|
|
assert_raise(bug9568) do
|
|
$DEBUG, debug = true, $DEBUG
|
|
begin
|
|
raise bug9568
|
|
ensure
|
|
$DEBUG = debug
|
|
end
|
|
end
|
|
end
|
|
assert_include(err, bug9568.to_s)
|
|
end
|
|
|
|
def test_errinfo_encoding_in_debug
|
|
exc = Module.new {break class_eval("class C\u{30a8 30e9 30fc} < RuntimeError; self; end".encode(Encoding::EUC_JP))}
|
|
exc.inspect
|
|
|
|
err = EnvUtil.verbose_warning do
|
|
assert_raise(exc) do
|
|
$DEBUG, debug = true, $DEBUG
|
|
begin
|
|
raise exc
|
|
ensure
|
|
$DEBUG = debug
|
|
end
|
|
end
|
|
end
|
|
assert_include(err, exc.to_s)
|
|
end
|
|
|
|
def test_break_ensure
|
|
bad = true
|
|
while true
|
|
begin
|
|
break
|
|
ensure
|
|
bad = false
|
|
end
|
|
end
|
|
assert(!bad)
|
|
end
|
|
|
|
def test_catch_no_throw
|
|
assert_equal(:foo, catch {:foo})
|
|
end
|
|
|
|
def test_catch_throw
|
|
result = catch(:foo) {
|
|
loop do
|
|
loop do
|
|
throw :foo, true
|
|
break
|
|
end
|
|
assert(false, "should not reach here")
|
|
end
|
|
false
|
|
}
|
|
assert(result)
|
|
end
|
|
|
|
def test_catch_throw_noarg
|
|
assert_nothing_raised(UncaughtThrowError) {
|
|
result = catch {|obj|
|
|
throw obj, :ok
|
|
assert(false, "should not reach here")
|
|
}
|
|
assert_equal(:ok, result)
|
|
}
|
|
end
|
|
|
|
def test_uncaught_throw
|
|
tag = nil
|
|
e = assert_raise_with_message(UncaughtThrowError, /uncaught throw/) {
|
|
catch("foo") {|obj|
|
|
tag = obj.dup
|
|
throw tag, :ok
|
|
assert(false, "should not reach here")
|
|
}
|
|
assert(false, "should not reach here")
|
|
}
|
|
assert_not_nil(tag)
|
|
assert_same(tag, e.tag)
|
|
assert_equal(:ok, e.value)
|
|
end
|
|
|
|
def test_catch_throw_in_require
|
|
bug7185 = '[ruby-dev:46234]'
|
|
Tempfile.create(["dep", ".rb"]) {|t|
|
|
t.puts("throw :extdep, 42")
|
|
t.close
|
|
assert_equal(42, assert_throw(:extdep, bug7185) {require t.path}, bug7185)
|
|
}
|
|
end
|
|
|
|
def test_throw_false
|
|
bug12743 = '[ruby-core:77229] [Bug #12743]'
|
|
Thread.start {
|
|
e = assert_raise_with_message(UncaughtThrowError, /false/, bug12743) {
|
|
throw false
|
|
}
|
|
assert_same(false, e.tag, bug12743)
|
|
}.join
|
|
end
|
|
|
|
def test_else_no_exception
|
|
begin
|
|
assert(true)
|
|
rescue
|
|
assert(false)
|
|
else
|
|
assert(true)
|
|
end
|
|
end
|
|
|
|
def test_else_raised
|
|
begin
|
|
assert(true)
|
|
raise
|
|
assert(false)
|
|
rescue
|
|
assert(true)
|
|
else
|
|
assert(false)
|
|
end
|
|
end
|
|
|
|
def test_else_nested_no_exception
|
|
begin
|
|
assert(true)
|
|
begin
|
|
assert(true)
|
|
rescue
|
|
assert(false)
|
|
else
|
|
assert(true)
|
|
end
|
|
assert(true)
|
|
rescue
|
|
assert(false)
|
|
else
|
|
assert(true)
|
|
end
|
|
end
|
|
|
|
def test_else_nested_rescued
|
|
begin
|
|
assert(true)
|
|
begin
|
|
assert(true)
|
|
raise
|
|
assert(false)
|
|
rescue
|
|
assert(true)
|
|
else
|
|
assert(false)
|
|
end
|
|
assert(true)
|
|
rescue
|
|
assert(false)
|
|
else
|
|
assert(true)
|
|
end
|
|
end
|
|
|
|
def test_else_nested_unrescued
|
|
begin
|
|
assert(true)
|
|
begin
|
|
assert(true)
|
|
rescue
|
|
assert(false)
|
|
else
|
|
assert(true)
|
|
end
|
|
assert(true)
|
|
raise
|
|
assert(false)
|
|
rescue
|
|
assert(true)
|
|
else
|
|
assert(false)
|
|
end
|
|
end
|
|
|
|
def test_else_nested_rescued_reraise
|
|
begin
|
|
assert(true)
|
|
begin
|
|
assert(true)
|
|
raise
|
|
assert(false)
|
|
rescue
|
|
assert(true)
|
|
else
|
|
assert(false)
|
|
end
|
|
assert(true)
|
|
raise
|
|
assert(false)
|
|
rescue
|
|
assert(true)
|
|
else
|
|
assert(false)
|
|
end
|
|
end
|
|
|
|
def test_raise_with_wrong_number_of_arguments
|
|
assert_raise(TypeError) { raise nil }
|
|
assert_raise(TypeError) { raise 1, 1 }
|
|
assert_raise(ArgumentError) { raise 1, 1, 1, 1 }
|
|
end
|
|
|
|
def test_type_error_message_encoding
|
|
c = eval("Module.new do break class C\u{4032}; self; end; end")
|
|
o = c.new
|
|
assert_raise_with_message(TypeError, /C\u{4032}/) do
|
|
""[o]
|
|
end
|
|
c.class_eval {def to_int; self; end}
|
|
assert_raise_with_message(TypeError, /C\u{4032}/) do
|
|
""[o]
|
|
end
|
|
c.class_eval {def to_a; self; end}
|
|
assert_raise_with_message(TypeError, /C\u{4032}/) do
|
|
[*o]
|
|
end
|
|
obj = eval("class C\u{1f5ff}; self; end").new
|
|
assert_raise_with_message(TypeError, /C\u{1f5ff}/) do
|
|
Class.new {include obj}
|
|
end
|
|
end
|
|
|
|
def test_errat
|
|
assert_in_out_err([], "p $@", %w(nil), [])
|
|
|
|
assert_in_out_err([], "$@ = 1", [], /\$! not set \(ArgumentError\)$/)
|
|
|
|
assert_in_out_err([], <<-INPUT, [], /backtrace must be Array of String \(TypeError\)$/)
|
|
begin
|
|
raise
|
|
rescue
|
|
$@ = 1
|
|
end
|
|
INPUT
|
|
|
|
assert_in_out_err([], <<-INPUT, [], /^foo: unhandled exception$/)
|
|
begin
|
|
raise
|
|
rescue
|
|
$@ = 'foo'
|
|
raise
|
|
end
|
|
INPUT
|
|
|
|
assert_in_out_err([], <<-INPUT, [], /^foo: unhandled exception\s+from bar\s+from baz$/)
|
|
begin
|
|
raise
|
|
rescue
|
|
$@ = %w(foo bar baz)
|
|
raise
|
|
end
|
|
INPUT
|
|
end
|
|
|
|
def test_thread_signal_location
|
|
skip
|
|
_, stderr, _ = EnvUtil.invoke_ruby(%w"--disable-gems -d", <<-RUBY, false, true)
|
|
Thread.start do
|
|
Thread.current.report_on_exception = false
|
|
begin
|
|
Process.kill(:INT, $$)
|
|
ensure
|
|
raise "in ensure"
|
|
end
|
|
end.join
|
|
RUBY
|
|
assert_not_match(/:0/, stderr, "[ruby-dev:39116]")
|
|
end
|
|
|
|
def test_errinfo
|
|
begin
|
|
raise "foo"
|
|
assert(false)
|
|
rescue => e
|
|
assert_equal(e, $!)
|
|
1.times { assert_equal(e, $!) }
|
|
end
|
|
|
|
assert_equal(nil, $!)
|
|
end
|
|
|
|
def test_inspect
|
|
assert_equal("#<Exception: Exception>", Exception.new.inspect)
|
|
|
|
e = Class.new(Exception)
|
|
e.class_eval do
|
|
def to_s; ""; end
|
|
end
|
|
assert_equal(e.inspect, e.new.inspect)
|
|
end
|
|
|
|
def test_to_s
|
|
e = StandardError.new("foo")
|
|
assert_equal("foo", e.to_s)
|
|
|
|
def (s = Object.new).to_s
|
|
"bar"
|
|
end
|
|
e = StandardError.new(s)
|
|
assert_equal("bar", e.to_s)
|
|
end
|
|
|
|
def test_set_backtrace
|
|
e = Exception.new
|
|
|
|
e.set_backtrace("foo")
|
|
assert_equal(["foo"], e.backtrace)
|
|
|
|
e.set_backtrace(%w(foo bar baz))
|
|
assert_equal(%w(foo bar baz), e.backtrace)
|
|
|
|
assert_raise(TypeError) { e.set_backtrace(1) }
|
|
assert_raise(TypeError) { e.set_backtrace([1]) }
|
|
end
|
|
|
|
def test_exit_success_p
|
|
begin
|
|
exit
|
|
rescue SystemExit => e
|
|
end
|
|
assert_send([e, :success?], "success by default")
|
|
|
|
begin
|
|
exit(true)
|
|
rescue SystemExit => e
|
|
end
|
|
assert_send([e, :success?], "true means success")
|
|
|
|
begin
|
|
exit(false)
|
|
rescue SystemExit => e
|
|
end
|
|
assert_not_send([e, :success?], "false means failure")
|
|
|
|
begin
|
|
abort
|
|
rescue SystemExit => e
|
|
end
|
|
assert_not_send([e, :success?], "abort means failure")
|
|
end
|
|
|
|
def test_nomethoderror
|
|
bug3237 = '[ruby-core:29948]'
|
|
str = "\u2600"
|
|
id = :"\u2604"
|
|
msg = "undefined method `#{id}' for \"#{str}\":String"
|
|
assert_raise_with_message(NoMethodError, msg, bug3237) do
|
|
str.__send__(id)
|
|
end
|
|
end
|
|
|
|
def test_errno
|
|
assert_equal(Encoding.find("locale"), Errno::EINVAL.new.message.encoding)
|
|
end
|
|
|
|
def test_too_many_args_in_eval
|
|
bug5720 = '[ruby-core:41520]'
|
|
arg_string = (0...140000).to_a.join(", ")
|
|
assert_raise(SystemStackError, bug5720) {eval "raise(#{arg_string})"}
|
|
end
|
|
|
|
def test_systemexit_new
|
|
e0 = SystemExit.new
|
|
assert_equal(0, e0.status)
|
|
assert_equal("SystemExit", e0.message)
|
|
ei = SystemExit.new(3)
|
|
assert_equal(3, ei.status)
|
|
assert_equal("SystemExit", ei.message)
|
|
es = SystemExit.new("msg")
|
|
assert_equal(0, es.status)
|
|
assert_equal("msg", es.message)
|
|
eis = SystemExit.new(7, "msg")
|
|
assert_equal(7, eis.status)
|
|
assert_equal("msg", eis.message)
|
|
|
|
bug5728 = '[ruby-dev:44951]'
|
|
et = SystemExit.new(true)
|
|
assert_equal(true, et.success?, bug5728)
|
|
assert_equal("SystemExit", et.message, bug5728)
|
|
ef = SystemExit.new(false)
|
|
assert_equal(false, ef.success?, bug5728)
|
|
assert_equal("SystemExit", ef.message, bug5728)
|
|
ets = SystemExit.new(true, "msg")
|
|
assert_equal(true, ets.success?, bug5728)
|
|
assert_equal("msg", ets.message, bug5728)
|
|
efs = SystemExit.new(false, "msg")
|
|
assert_equal(false, efs.success?, bug5728)
|
|
assert_equal("msg", efs.message, bug5728)
|
|
end
|
|
|
|
def test_exception_in_name_error_to_str
|
|
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
|
|
bug5575 = '[ruby-core:41612]'
|
|
begin;
|
|
begin
|
|
BasicObject.new.inspect
|
|
rescue
|
|
assert_nothing_raised(NameError, bug5575) {$!.inspect}
|
|
end
|
|
end;
|
|
end
|
|
|
|
def test_equal
|
|
bug5865 = '[ruby-core:41979]'
|
|
assert_equal(RuntimeError.new("a"), RuntimeError.new("a"), bug5865)
|
|
assert_not_equal(RuntimeError.new("a"), StandardError.new("a"), bug5865)
|
|
end
|
|
|
|
def test_exception_in_exception_equal
|
|
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
|
|
bug5865 = '[ruby-core:41979]'
|
|
begin;
|
|
o = Object.new
|
|
def o.exception(arg)
|
|
end
|
|
assert_nothing_raised(ArgumentError, bug5865) do
|
|
RuntimeError.new("a") == o
|
|
end
|
|
end;
|
|
end
|
|
|
|
def test_backtrace_by_exception
|
|
begin
|
|
line = __LINE__; raise "foo"
|
|
rescue => e
|
|
end
|
|
e2 = e.exception("bar")
|
|
assert_not_equal(e.message, e2.message)
|
|
assert_equal(e.backtrace, e2.backtrace)
|
|
loc = e2.backtrace_locations[0]
|
|
assert_equal([__FILE__, line], [loc.path, loc.lineno])
|
|
end
|
|
|
|
Bug4438 = '[ruby-core:35364]'
|
|
|
|
def test_rescue_single_argument
|
|
assert_raise(TypeError, Bug4438) do
|
|
begin
|
|
raise
|
|
rescue 1
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_rescue_splat_argument
|
|
assert_raise(TypeError, Bug4438) do
|
|
begin
|
|
raise
|
|
rescue *Array(1)
|
|
end
|
|
end
|
|
end
|
|
|
|
def test_to_s_taintness_propagation
|
|
for exc in [Exception, NameError]
|
|
m = "abcdefg"
|
|
e = exc.new(m)
|
|
e.taint
|
|
s = e.to_s
|
|
assert_equal(false, m.tainted?,
|
|
"#{exc}#to_s should not propagate taintness")
|
|
assert_equal(false, s.tainted?,
|
|
"#{exc}#to_s should not propagate taintness")
|
|
end
|
|
|
|
o = Object.new
|
|
def o.to_str
|
|
"foo"
|
|
end
|
|
o.taint
|
|
e = NameError.new(o)
|
|
s = e.to_s
|
|
assert_equal(false, s.tainted?)
|
|
end
|
|
|
|
def m
|
|
m(&->{return 0})
|
|
42
|
|
end
|
|
|
|
def test_stackoverflow
|
|
feature6216 = '[ruby-core:43794] [Feature #6216]'
|
|
e = assert_raise(SystemStackError, feature6216) {m}
|
|
level = e.backtrace.size
|
|
assert_operator(level, :>, 10, feature6216)
|
|
|
|
feature6216 = '[ruby-core:63377] [Feature #6216]'
|
|
e = assert_raise(SystemStackError, feature6216) {raise e}
|
|
assert_equal(level, e.backtrace.size, feature6216)
|
|
end
|
|
|
|
def test_machine_stackoverflow
|
|
bug9109 = '[ruby-dev:47804] [Bug #9109]'
|
|
assert_separately(%w[--disable-gem], <<-SRC)
|
|
assert_raise(SystemStackError, #{bug9109.dump}) {
|
|
h = {a: ->{h[:a].call}}
|
|
h[:a].call
|
|
}
|
|
SRC
|
|
rescue SystemStackError
|
|
end
|
|
|
|
def test_machine_stackoverflow_by_define_method
|
|
bug9454 = '[ruby-core:60113] [Bug #9454]'
|
|
assert_separately(%w[--disable-gem], <<-SRC)
|
|
assert_raise(SystemStackError, #{bug9454.dump}) {
|
|
define_method(:foo) {self.foo}
|
|
self.foo
|
|
}
|
|
SRC
|
|
rescue SystemStackError
|
|
end
|
|
|
|
def test_machine_stackoverflow_by_trace
|
|
assert_normal_exit("#{<<-"begin;"}\n#{<<~"end;"}", timeout: 60)
|
|
begin;
|
|
require 'timeout'
|
|
require 'tracer'
|
|
class HogeError < StandardError
|
|
def to_s
|
|
message.upcase # disable tailcall optimization
|
|
end
|
|
end
|
|
Tracer.stdout = open(IO::NULL, "w")
|
|
begin
|
|
Timeout.timeout(5) do
|
|
Tracer.on
|
|
HogeError.new.to_s
|
|
end
|
|
rescue Timeout::Error
|
|
# ok. there are no SEGV or critical error
|
|
rescue SystemStackError => e
|
|
# ok.
|
|
end
|
|
end;
|
|
end
|
|
|
|
def test_cause
|
|
msg = "[Feature #8257]"
|
|
cause = nil
|
|
e = assert_raise(StandardError) {
|
|
begin
|
|
raise msg
|
|
rescue => e
|
|
cause = e.cause
|
|
raise StandardError
|
|
end
|
|
}
|
|
assert_nil(cause, msg)
|
|
cause = e.cause
|
|
assert_instance_of(RuntimeError, cause, msg)
|
|
assert_equal(msg, cause.message, msg)
|
|
end
|
|
|
|
def test_cause_reraised
|
|
msg = "[Feature #8257]"
|
|
e = assert_raise(RuntimeError) {
|
|
begin
|
|
raise msg
|
|
rescue => e
|
|
raise e
|
|
end
|
|
}
|
|
assert_not_same(e, e.cause, "#{msg}: should not be recursive")
|
|
end
|
|
|
|
def test_cause_raised_in_rescue
|
|
a = nil
|
|
e = assert_raise_with_message(RuntimeError, 'b') {
|
|
begin
|
|
raise 'a'
|
|
rescue => a
|
|
begin
|
|
raise 'b'
|
|
rescue => b
|
|
assert_same(a, b.cause)
|
|
begin
|
|
raise 'c'
|
|
rescue
|
|
raise b
|
|
end
|
|
end
|
|
end
|
|
}
|
|
assert_same(a, e.cause, 'cause should not be overwritten by reraise')
|
|
end
|
|
|
|
def test_cause_at_raised
|
|
a = nil
|
|
e = assert_raise_with_message(RuntimeError, 'b') {
|
|
begin
|
|
raise 'a'
|
|
rescue => a
|
|
b = RuntimeError.new('b')
|
|
assert_nil(b.cause)
|
|
begin
|
|
raise 'c'
|
|
rescue
|
|
raise b
|
|
end
|
|
end
|
|
}
|
|
assert_equal('c', e.cause.message, 'cause should be the exception at raised')
|
|
assert_same(a, e.cause.cause)
|
|
end
|
|
|
|
def test_cause_at_end
|
|
errs = [
|
|
/-: unexpected return\n/,
|
|
/.*undefined local variable or method `n'.*\n/,
|
|
]
|
|
assert_in_out_err([], <<-'end;', [], errs)
|
|
END{n}; END{return}
|
|
end;
|
|
end
|
|
|
|
def test_raise_with_cause
|
|
msg = "[Feature #8257]"
|
|
cause = ArgumentError.new("foobar")
|
|
e = assert_raise(RuntimeError) {raise msg, cause: cause}
|
|
assert_same(cause, e.cause)
|
|
end
|
|
|
|
def test_cause_with_no_arguments
|
|
cause = ArgumentError.new("foobar")
|
|
assert_raise_with_message(ArgumentError, /with no arguments/) do
|
|
raise cause: cause
|
|
end
|
|
end
|
|
|
|
def test_raise_with_cause_in_rescue
|
|
e = assert_raise_with_message(RuntimeError, 'b') {
|
|
begin
|
|
raise 'a'
|
|
rescue => a
|
|
begin
|
|
raise 'b'
|
|
rescue => b
|
|
assert_same(a, b.cause)
|
|
begin
|
|
raise 'c'
|
|
rescue
|
|
raise b, cause: ArgumentError.new('d')
|
|
end
|
|
end
|
|
end
|
|
}
|
|
assert_equal('d', e.cause.message, 'cause option should be honored always')
|
|
assert_nil(e.cause.cause)
|
|
end
|
|
|
|
def test_cause_thread_no_cause
|
|
bug12741 = '[ruby-core:77222] [Bug #12741]'
|
|
|
|
x = Thread.current
|
|
a = false
|
|
y = Thread.start do
|
|
Thread.pass until a
|
|
x.raise "stop"
|
|
end
|
|
|
|
begin
|
|
raise bug12741
|
|
rescue
|
|
e = assert_raise_with_message(RuntimeError, "stop") do
|
|
a = true
|
|
sleep 1
|
|
end
|
|
end
|
|
assert_nil(e.cause)
|
|
ensure
|
|
y.join
|
|
end
|
|
|
|
def test_cause_thread_with_cause
|
|
bug12741 = '[ruby-core:77222] [Bug #12741]'
|
|
|
|
x = Thread.current
|
|
q = Queue.new
|
|
y = Thread.start do
|
|
q.pop
|
|
begin
|
|
raise "caller's cause"
|
|
rescue
|
|
x.raise "stop"
|
|
end
|
|
end
|
|
|
|
begin
|
|
raise bug12741
|
|
rescue
|
|
e = assert_raise_with_message(RuntimeError, "stop") do
|
|
q.push(true)
|
|
sleep 1
|
|
end
|
|
ensure
|
|
y.join
|
|
end
|
|
assert_equal("caller's cause", e.cause.message)
|
|
end
|
|
|
|
def test_unknown_option
|
|
bug = '[ruby-core:63203] [Feature #8257] should pass unknown options'
|
|
|
|
exc = Class.new(RuntimeError) do
|
|
attr_reader :arg
|
|
def initialize(msg = nil)
|
|
@arg = msg
|
|
super(msg)
|
|
end
|
|
end
|
|
|
|
e = assert_raise(exc, bug) {raise exc, "foo" => "bar", foo: "bar"}
|
|
assert_equal({"foo" => "bar", foo: "bar"}, e.arg, bug)
|
|
|
|
e = assert_raise(exc, bug) {raise exc, "foo" => "bar", foo: "bar", cause: RuntimeError.new("zzz")}
|
|
assert_equal({"foo" => "bar", foo: "bar"}, e.arg, bug)
|
|
|
|
e = assert_raise(exc, bug) {raise exc, {}}
|
|
assert_equal({}, e.arg, bug)
|
|
end
|
|
|
|
def test_circular_cause
|
|
bug13043 = '[ruby-core:78688] [Bug #13043]'
|
|
begin
|
|
begin
|
|
raise "error 1"
|
|
ensure
|
|
orig_error = $!
|
|
begin
|
|
raise "error 2"
|
|
rescue => err
|
|
raise orig_error
|
|
end
|
|
end
|
|
rescue => x
|
|
end
|
|
assert_equal(orig_error, x)
|
|
assert_equal(orig_error, err.cause)
|
|
assert_nil(orig_error.cause, bug13043)
|
|
end
|
|
|
|
def test_cause_with_frozen_exception
|
|
exc = ArgumentError.new("foo").freeze
|
|
assert_raise_with_message(ArgumentError, exc.message) {
|
|
raise exc, cause: RuntimeError.new("bar")
|
|
}
|
|
end
|
|
|
|
def test_anonymous_message
|
|
assert_in_out_err([], "raise Class.new(RuntimeError), 'foo'", [], /foo\n/)
|
|
end
|
|
|
|
PrettyObject =
|
|
Class.new(BasicObject) do
|
|
alias object_id __id__
|
|
def pretty_inspect; "`obj'"; end
|
|
alias inspect pretty_inspect
|
|
end
|
|
|
|
def test_frozen_error_receiver
|
|
obj = Object.new.freeze
|
|
(obj.foo = 1) rescue (e = $!)
|
|
assert_same(obj, e.receiver)
|
|
obj.singleton_class.const_set(:A, 2) rescue (e = $!)
|
|
assert_same(obj.singleton_class, e.receiver)
|
|
end
|
|
|
|
def test_frozen_error_initialize
|
|
obj = Object.new
|
|
exc = FrozenError.new("bar", obj)
|
|
assert_equal("bar", exc.message)
|
|
assert_same(obj, exc.receiver)
|
|
|
|
exc = FrozenError.new("bar")
|
|
assert_equal("bar", exc.message)
|
|
assert_raise_with_message(ArgumentError, "no receiver is available") {
|
|
exc.receiver
|
|
}
|
|
|
|
exc = FrozenError.new
|
|
assert_equal("FrozenError", exc.message)
|
|
assert_raise_with_message(ArgumentError, "no receiver is available") {
|
|
exc.receiver
|
|
}
|
|
end
|
|
|
|
def test_frozen_error_message
|
|
obj = Object.new.freeze
|
|
e = assert_raise_with_message(FrozenError, /can't modify frozen #{obj.class}/) {
|
|
obj.instance_variable_set(:@test, true)
|
|
}
|
|
assert_include(e.message, obj.inspect)
|
|
|
|
klass = Class.new do
|
|
def init
|
|
@x = true
|
|
end
|
|
def inspect
|
|
init
|
|
super
|
|
end
|
|
end
|
|
obj = klass.new.freeze
|
|
e = assert_raise_with_message(FrozenError, /can't modify frozen #{obj.class}/) {
|
|
obj.init
|
|
}
|
|
assert_include(e.message, klass.inspect)
|
|
end
|
|
|
|
def test_name_error_new_default
|
|
error = NameError.new
|
|
assert_equal("NameError", error.message)
|
|
end
|
|
|
|
def test_name_error_new_message
|
|
error = NameError.new("Message")
|
|
assert_equal("Message", error.message)
|
|
end
|
|
|
|
def test_name_error_new_name
|
|
error = NameError.new("Message")
|
|
assert_nil(error.name)
|
|
|
|
error = NameError.new("Message", :foo)
|
|
assert_equal(:foo, error.name)
|
|
end
|
|
|
|
def test_name_error_new_receiver
|
|
receiver = Object.new
|
|
|
|
error = NameError.new
|
|
assert_raise(ArgumentError) {error.receiver}
|
|
assert_equal("NameError", error.message)
|
|
|
|
error = NameError.new(receiver: receiver)
|
|
assert_equal(["NameError", receiver],
|
|
[error.message, error.receiver])
|
|
|
|
error = NameError.new("Message", :foo, receiver: receiver)
|
|
assert_equal(["Message", receiver, :foo],
|
|
[error.message, error.receiver, error.name])
|
|
end
|
|
|
|
def test_nomethod_error_new_default
|
|
error = NoMethodError.new
|
|
assert_equal("NoMethodError", error.message)
|
|
end
|
|
|
|
def test_nomethod_error_new_message
|
|
error = NoMethodError.new("Message")
|
|
assert_equal("Message", error.message)
|
|
end
|
|
|
|
def test_nomethod_error_new_name
|
|
error = NoMethodError.new("Message")
|
|
assert_nil(error.name)
|
|
|
|
error = NoMethodError.new("Message", :foo)
|
|
assert_equal(:foo, error.name)
|
|
end
|
|
|
|
def test_nomethod_error_new_name_args
|
|
error = NoMethodError.new("Message", :foo)
|
|
assert_nil(error.args)
|
|
|
|
error = NoMethodError.new("Message", :foo, [1, 2])
|
|
assert_equal([:foo, [1, 2]], [error.name, error.args])
|
|
end
|
|
|
|
def test_nomethod_error_new_name_args_priv
|
|
error = NoMethodError.new("Message", :foo, [1, 2])
|
|
assert_not_predicate(error, :private_call?)
|
|
|
|
error = NoMethodError.new("Message", :foo, [1, 2], true)
|
|
assert_equal([:foo, [1, 2], true],
|
|
[error.name, error.args, error.private_call?])
|
|
end
|
|
|
|
def test_nomethod_error_new_receiver
|
|
receiver = Object.new
|
|
|
|
error = NoMethodError.new
|
|
assert_raise(ArgumentError) {error.receiver}
|
|
|
|
error = NoMethodError.new(receiver: receiver)
|
|
assert_equal(receiver, error.receiver)
|
|
|
|
error = NoMethodError.new("Message")
|
|
assert_raise(ArgumentError) {error.receiver}
|
|
|
|
error = NoMethodError.new("Message", receiver: receiver)
|
|
assert_equal(["Message", receiver],
|
|
[error.message, error.receiver])
|
|
|
|
error = NoMethodError.new("Message", :foo)
|
|
assert_raise(ArgumentError) {error.receiver}
|
|
|
|
error = NoMethodError.new("Message", :foo, receiver: receiver)
|
|
assert_equal(["Message", :foo, receiver],
|
|
[error.message, error.name, error.receiver])
|
|
|
|
error = NoMethodError.new("Message", :foo, [1, 2])
|
|
assert_raise(ArgumentError) {error.receiver}
|
|
|
|
error = NoMethodError.new("Message", :foo, [1, 2], receiver: receiver)
|
|
assert_equal(["Message", :foo, [1, 2], receiver],
|
|
[error.message, error.name, error.args, error.receiver])
|
|
|
|
error = NoMethodError.new("Message", :foo, [1, 2], true)
|
|
assert_raise(ArgumentError) {error.receiver}
|
|
|
|
error = NoMethodError.new("Message", :foo, [1, 2], true, receiver: receiver)
|
|
assert_equal([:foo, [1, 2], true, receiver],
|
|
[error.name, error.args, error.private_call?, error.receiver])
|
|
end
|
|
|
|
def test_name_error_info_const
|
|
obj = PrettyObject.new
|
|
|
|
e = assert_raise(NameError) {
|
|
obj.instance_eval("Object")
|
|
}
|
|
assert_equal(:Object, e.name)
|
|
|
|
e = assert_raise(NameError) {
|
|
BasicObject::X
|
|
}
|
|
assert_same(BasicObject, e.receiver)
|
|
assert_equal(:X, e.name)
|
|
end
|
|
|
|
def test_name_error_info_method
|
|
obj = PrettyObject.new
|
|
|
|
e = assert_raise(NameError) {
|
|
obj.instance_eval {foo}
|
|
}
|
|
assert_equal(:foo, e.name)
|
|
assert_same(obj, e.receiver)
|
|
|
|
e = assert_raise(NoMethodError) {
|
|
obj.foo(1, 2)
|
|
}
|
|
assert_equal(:foo, e.name)
|
|
assert_equal([1, 2], e.args)
|
|
assert_same(obj, e.receiver)
|
|
assert_not_predicate(e, :private_call?)
|
|
|
|
e = assert_raise(NoMethodError) {
|
|
obj.instance_eval {foo(1, 2)}
|
|
}
|
|
assert_equal(:foo, e.name)
|
|
assert_equal([1, 2], e.args)
|
|
assert_same(obj, e.receiver)
|
|
assert_predicate(e, :private_call?)
|
|
end
|
|
|
|
def test_name_error_info_local_variables
|
|
obj = PrettyObject.new
|
|
def obj.test(a, b=nil, *c, &d)
|
|
e = a
|
|
1.times {|f| g = foo; g}
|
|
e
|
|
end
|
|
|
|
e = assert_raise(NameError) {
|
|
obj.test(3)
|
|
}
|
|
assert_equal(:foo, e.name)
|
|
assert_same(obj, e.receiver)
|
|
assert_equal(%i[a b c d e f g], e.local_variables.sort)
|
|
end
|
|
|
|
def test_name_error_info_method_missing
|
|
obj = PrettyObject.new
|
|
def obj.method_missing(*)
|
|
super
|
|
end
|
|
|
|
e = assert_raise(NoMethodError) {
|
|
obj.foo(1, 2)
|
|
}
|
|
assert_equal(:foo, e.name)
|
|
assert_equal([1, 2], e.args)
|
|
assert_same(obj, e.receiver)
|
|
assert_not_predicate(e, :private_call?)
|
|
|
|
e = assert_raise(NoMethodError) {
|
|
obj.instance_eval {foo(1, 2)}
|
|
}
|
|
assert_equal(:foo, e.name)
|
|
assert_equal([1, 2], e.args)
|
|
assert_same(obj, e.receiver)
|
|
assert_predicate(e, :private_call?)
|
|
end
|
|
|
|
def test_name_error_info_parent_iseq_mark
|
|
assert_separately(['-', File.join(__dir__, 'bug-11928.rb')], <<-'end;')
|
|
-> {require ARGV[0]}.call
|
|
end;
|
|
end
|
|
|
|
def test_output_string_encoding
|
|
# "\x82\xa0" in cp932 is "\u3042" (Japanese hiragana 'a')
|
|
# change $stderr to force calling rb_io_write() instead of fwrite()
|
|
assert_in_out_err(["-Eutf-8:cp932"], '# coding: cp932
|
|
$stderr = $stdout; raise "\x82\xa0"') do |outs, errs, status|
|
|
assert_equal 1, outs.size
|
|
assert_equal 0, errs.size
|
|
err = outs.first.force_encoding('utf-8')
|
|
assert err.valid_encoding?, 'must be valid encoding'
|
|
assert_match %r/\u3042/, err
|
|
end
|
|
end
|
|
|
|
def test_multibyte_and_newline
|
|
bug10727 = '[ruby-core:67473] [Bug #10727]'
|
|
assert_in_out_err([], <<-'end;', [], /\u{306b 307b 3093 3054} \(E\)\n\u{6539 884c}/, bug10727, encoding: "UTF-8")
|
|
class E < StandardError
|
|
def initialize
|
|
super("\u{306b 307b 3093 3054}\n\u{6539 884c}")
|
|
end
|
|
end
|
|
raise E
|
|
end;
|
|
end
|
|
|
|
def assert_null_char(src, *args, **opts)
|
|
begin
|
|
eval(src)
|
|
rescue => e
|
|
end
|
|
assert_not_nil(e)
|
|
assert_include(e.message, "\0")
|
|
assert_in_out_err([], src, [], [], *args, **opts) do |_, err,|
|
|
err.each do |e|
|
|
assert_not_include(e, "\0")
|
|
end
|
|
end
|
|
e
|
|
end
|
|
|
|
def test_control_in_message
|
|
bug7574 = '[ruby-dev:46749]'
|
|
assert_null_char("#{<<~"begin;"}\n#{<<~'end;'}", bug7574)
|
|
begin;
|
|
Object.const_defined?("String\0")
|
|
end;
|
|
assert_null_char("#{<<~"begin;"}\n#{<<~'end;'}", bug7574)
|
|
begin;
|
|
Object.const_get("String\0")
|
|
end;
|
|
end
|
|
|
|
def test_encoding_in_message
|
|
name = "\u{e9}t\u{e9}"
|
|
e = EnvUtil.with_default_external("US-ASCII") do
|
|
assert_raise(NameError) do
|
|
Object.const_get(name)
|
|
end
|
|
end
|
|
assert_include(e.message, name)
|
|
end
|
|
|
|
def test_method_missing_reason_clear
|
|
bug10969 = '[ruby-core:68515] [Bug #10969]'
|
|
a = Class.new {def method_missing(*) super end}.new
|
|
assert_raise(NameError) {a.instance_eval("foo")}
|
|
assert_raise(NoMethodError, bug10969) {a.public_send("bar", true)}
|
|
end
|
|
|
|
def test_message_of_name_error
|
|
assert_raise_with_message(NameError, /\Aundefined method `foo' for module `#<Module:.*>'$/) do
|
|
Module.new do
|
|
module_function :foo
|
|
end
|
|
end
|
|
end
|
|
|
|
def capture_warning_warn
|
|
verbose = $VERBOSE
|
|
warning = []
|
|
|
|
::Warning.class_eval do
|
|
alias_method :warn2, :warn
|
|
remove_method :warn
|
|
|
|
define_method(:warn) do |str|
|
|
warning << str
|
|
end
|
|
end
|
|
|
|
$VERBOSE = true
|
|
yield
|
|
|
|
return warning
|
|
ensure
|
|
$VERBOSE = verbose
|
|
|
|
::Warning.class_eval do
|
|
remove_method :warn
|
|
alias_method :warn, :warn2
|
|
remove_method :warn2
|
|
end
|
|
end
|
|
|
|
def test_warning_warn
|
|
warning = capture_warning_warn {@a}
|
|
assert_match(/instance variable @a not initialized/, warning[0])
|
|
|
|
assert_equal(["a\nz\n"], capture_warning_warn {warn "a\n", "z"})
|
|
assert_equal([], capture_warning_warn {warn})
|
|
assert_equal(["\n"], capture_warning_warn {warn ""})
|
|
end
|
|
|
|
def test_kernel_warn_uplevel
|
|
warning = capture_warning_warn {warn("test warning", uplevel: 0)}
|
|
assert_equal("#{__FILE__}:#{__LINE__-1}: warning: test warning\n", warning[0])
|
|
def (obj = Object.new).w(n) warn("test warning", uplevel: n) end
|
|
warning = capture_warning_warn {obj.w(0)}
|
|
assert_equal("#{__FILE__}:#{__LINE__-2}: warning: test warning\n", warning[0])
|
|
warning = capture_warning_warn {obj.w(1)}
|
|
assert_equal("#{__FILE__}:#{__LINE__-1}: warning: test warning\n", warning[0])
|
|
assert_raise(ArgumentError) {warn("test warning", uplevel: -1)}
|
|
assert_in_out_err(["-e", "warn 'ok', uplevel: 1"], '', [], /warning:/)
|
|
warning = capture_warning_warn {warn("test warning", {uplevel: 0})}
|
|
assert_equal("#{__FILE__}:#{__LINE__-1}: warning: The last argument is used as the keyword parameter\n", warning[0])
|
|
assert_match(/warning: for method defined here|warning: test warning/, warning[1])
|
|
warning = capture_warning_warn {warn("test warning", **{uplevel: 0})}
|
|
assert_equal("#{__FILE__}:#{__LINE__-1}: warning: test warning\n", warning[0])
|
|
warning = capture_warning_warn {warn("test warning", {uplevel: 0}, **{})}
|
|
assert_equal("test warning\n{:uplevel=>0}\n", warning[0])
|
|
assert_raise(ArgumentError) {warn("test warning", foo: 1)}
|
|
end
|
|
|
|
def test_warning_warn_invalid_argument
|
|
assert_raise(TypeError) do
|
|
::Warning.warn nil
|
|
end
|
|
assert_raise(TypeError) do
|
|
::Warning.warn 1
|
|
end
|
|
assert_raise(Encoding::CompatibilityError) do
|
|
::Warning.warn "\x00a\x00b\x00c".force_encoding("utf-16be")
|
|
end
|
|
end
|
|
|
|
def test_warning_warn_circular_require_backtrace
|
|
warning = nil
|
|
path = nil
|
|
Tempfile.create(%w[circular .rb]) do |t|
|
|
path = File.realpath(t.path)
|
|
basename = File.basename(path)
|
|
t.puts "require '#{basename}'"
|
|
t.close
|
|
$LOAD_PATH.push(File.dirname(t))
|
|
warning = capture_warning_warn {require basename}
|
|
ensure
|
|
$LOAD_PATH.pop
|
|
$LOADED_FEATURES.delete(t)
|
|
end
|
|
assert_equal(1, warning.size)
|
|
assert_match(/circular require/, warning.first)
|
|
assert_match(/^\tfrom #{Regexp.escape(path)}:1:/, warning.first)
|
|
end
|
|
|
|
def test_warning_warn_super
|
|
assert_in_out_err(%[-W0], "#{<<~"{#"}\n#{<<~'};'}", [], /instance variable @a not initialized/)
|
|
{#
|
|
module Warning
|
|
def warn(message)
|
|
super
|
|
end
|
|
end
|
|
|
|
$VERBOSE = true
|
|
@a
|
|
};
|
|
end
|
|
|
|
def test_undefined_backtrace
|
|
assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
|
|
begin;
|
|
class Exception
|
|
undef backtrace
|
|
end
|
|
|
|
assert_raise(RuntimeError) {
|
|
raise RuntimeError, "hello"
|
|
}
|
|
end;
|
|
end
|
|
|
|
def test_redefined_backtrace
|
|
assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
|
|
begin;
|
|
$exc = nil
|
|
|
|
class Exception
|
|
undef backtrace
|
|
def backtrace
|
|
$exc = self
|
|
end
|
|
end
|
|
|
|
e = assert_raise(RuntimeError) {
|
|
raise RuntimeError, "hello"
|
|
}
|
|
assert_same(e, $exc)
|
|
end;
|
|
end
|
|
|
|
def test_blocking_backtrace
|
|
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
|
|
begin;
|
|
class Bug < RuntimeError
|
|
def backtrace
|
|
IO.readlines(IO::NULL)
|
|
end
|
|
end
|
|
bug = Bug.new '[ruby-core:85939] [Bug #14577]'
|
|
n = 10000
|
|
i = 0
|
|
n.times do
|
|
begin
|
|
raise bug
|
|
rescue Bug
|
|
i += 1
|
|
end
|
|
end
|
|
assert_equal(n, i)
|
|
end;
|
|
end
|
|
|
|
def test_wrong_backtrace
|
|
assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
|
|
begin;
|
|
class Exception
|
|
undef backtrace
|
|
def backtrace(a)
|
|
end
|
|
end
|
|
|
|
assert_raise(RuntimeError) {
|
|
raise RuntimeError, "hello"
|
|
}
|
|
end;
|
|
|
|
error_class = Class.new(StandardError) do
|
|
def backtrace; :backtrace; end
|
|
end
|
|
begin
|
|
raise error_class
|
|
rescue error_class => e
|
|
assert_raise(TypeError) {$@}
|
|
assert_raise(TypeError) {e.full_message}
|
|
end
|
|
end
|
|
|
|
def test_backtrace_in_eval
|
|
bug = '[ruby-core:84434] [Bug #14229]'
|
|
assert_in_out_err(['-e', 'eval("raise")'], "", [], /^\(eval\):1:/, bug)
|
|
end
|
|
|
|
def test_full_message
|
|
message = RuntimeError.new("testerror").full_message
|
|
assert_operator(message, :end_with?, "\n")
|
|
|
|
test_method = "def foo; raise 'testerror'; end"
|
|
|
|
out1, err1, status1 = EnvUtil.invoke_ruby(['-e', "#{test_method}; begin; foo; rescue => e; puts e.full_message; end"], '', true, true)
|
|
assert_predicate(status1, :success?)
|
|
assert_empty(err1, "expected nothing wrote to $stdout by #full_message")
|
|
|
|
_, err2, status1 = EnvUtil.invoke_ruby(['-e', "#{test_method}; begin; foo; end"], '', true, true)
|
|
assert_equal(err2, out1)
|
|
|
|
e = RuntimeError.new("a\n")
|
|
message = assert_nothing_raised(ArgumentError, proc {e.pretty_inspect}) do
|
|
e.full_message
|
|
end
|
|
assert_operator(message, :end_with?, "\n")
|
|
message = message.gsub(/\e\[[\d;]*m/, '')
|
|
assert_not_operator(message, :end_with?, "\n\n")
|
|
e = RuntimeError.new("a\n\nb\n\nc")
|
|
message = assert_nothing_raised(ArgumentError, proc {e.pretty_inspect}) do
|
|
e.full_message
|
|
end
|
|
assert_all?(message.lines) do |m|
|
|
/\e\[\d[;\d]*m[^\e]*\n/ !~ m
|
|
end
|
|
|
|
e = RuntimeError.new("testerror")
|
|
message = e.full_message(highlight: false)
|
|
assert_not_match(/\e/, message)
|
|
|
|
bt = ["test:100", "test:99", "test:98", "test:1"]
|
|
e = assert_raise(RuntimeError) {raise RuntimeError, "testerror", bt}
|
|
|
|
bottom = "test:100: testerror (RuntimeError)\n"
|
|
top = "test:1\n"
|
|
remark = "Traceback (most recent call last):"
|
|
|
|
message = e.full_message(highlight: false, order: :top)
|
|
assert_not_match(/\e/, message)
|
|
assert_operator(message.count("\n"), :>, 2)
|
|
assert_operator(message, :start_with?, bottom)
|
|
assert_operator(message, :end_with?, top)
|
|
|
|
message = e.full_message(highlight: false, order: :bottom)
|
|
assert_not_match(/\e/, message)
|
|
assert_operator(message.count("\n"), :>, 2)
|
|
assert_operator(message, :start_with?, remark)
|
|
assert_operator(message, :end_with?, bottom)
|
|
|
|
assert_raise_with_message(ArgumentError, /:top or :bottom/) {
|
|
e.full_message(highlight: false, order: :middle)
|
|
}
|
|
|
|
message = e.full_message(highlight: true)
|
|
assert_match(/\e/, message)
|
|
assert_not_match(/(\e\[1)m\1/, message)
|
|
e2 = assert_raise(RuntimeError) {raise RuntimeError, "", bt}
|
|
assert_not_match(/(\e\[1)m\1/, e2.full_message(highlight: true))
|
|
|
|
message = e.full_message
|
|
if Exception.to_tty?
|
|
assert_match(/\e/, message)
|
|
message = message.gsub(/\e\[[\d;]*m/, '')
|
|
assert_operator(message, :start_with?, remark)
|
|
assert_operator(message, :end_with?, bottom)
|
|
else
|
|
assert_not_match(/\e/, message)
|
|
assert_operator(message, :start_with?, bottom)
|
|
assert_operator(message, :end_with?, top)
|
|
end
|
|
end
|
|
|
|
def test_exception_in_message
|
|
code = "#{<<~"begin;"}\n#{<<~'end;'}"
|
|
begin;
|
|
class Bug14566 < StandardError
|
|
def message; raise self.class; end
|
|
end
|
|
raise Bug14566
|
|
end;
|
|
assert_in_out_err([], code, [], /Bug14566/, success: false, timeout: 2)
|
|
end
|
|
|
|
def test_non_exception_cause
|
|
assert_raise_with_message(TypeError, /exception/) do
|
|
raise "foo", cause: 1
|
|
end;
|
|
end
|
|
|
|
def test_circular_cause_handle
|
|
assert_raise_with_message(ArgumentError, /circular cause/) do
|
|
begin
|
|
raise "error 1"
|
|
rescue => e1
|
|
raise "error 2" rescue raise e1, cause: $!
|
|
end
|
|
end;
|
|
end
|
|
|
|
def test_super_in_method_missing
|
|
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
|
|
begin;
|
|
$VERBOSE = nil
|
|
class Object
|
|
def method_missing(name, *args, &block)
|
|
super
|
|
end
|
|
end
|
|
|
|
bug14670 = '[ruby-dev:50522] [Bug #14670]'
|
|
assert_raise_with_message(NoMethodError, /`foo'/, bug14670) do
|
|
Object.new.foo
|
|
end
|
|
end;
|
|
end
|
|
end
|