This commit is contained in:
Benoit Daloze 2021-03-27 13:02:41 +01:00
parent 44736a6b7a
commit 95d9fe9538
26 changed files with 501 additions and 84 deletions

View File

@ -95,6 +95,7 @@ Lint/UnreachableCode:
- 'core/kernel/raise_spec.rb' - 'core/kernel/raise_spec.rb'
- 'core/kernel/throw_spec.rb' - 'core/kernel/throw_spec.rb'
- 'language/break_spec.rb' - 'language/break_spec.rb'
- 'language/optional_assignments_spec.rb'
- 'language/fixtures/break.rb' - 'language/fixtures/break.rb'
- 'language/fixtures/break_lambda_toplevel.rb' - 'language/fixtures/break_lambda_toplevel.rb'
- 'language/fixtures/break_lambda_toplevel_block.rb' - 'language/fixtures/break_lambda_toplevel_block.rb'

View File

@ -35,6 +35,11 @@ describe "Enumerable#chunk" do
result.should == [[:_alone, [1]], [false, [2, 3, 2]], [:_alone, [1]]] result.should == [[:_alone, [1]], [false, [2, 3, 2]], [:_alone, [1]]]
end end
it "yields Arrays as a single argument to a rest argument" do
e = EnumerableSpecs::Numerous.new([1, 2])
result = e.chunk { |*x| x.should == [[1,2]] }.to_a
end
it "does not return elements for which the block returns :_separator" do it "does not return elements for which the block returns :_separator" do
e = EnumerableSpecs::Numerous.new(1, 2, 3, 3, 2, 1) e = EnumerableSpecs::Numerous.new(1, 2, 3, 3, 2, 1)
result = e.chunk { |x| x == 2 ? :_separator : 1 }.to_a result = e.chunk { |x| x == 2 ? :_separator : 1 }.to_a

View File

@ -29,7 +29,23 @@ describe "rescuing Interrupt" do
sleep sleep
rescue Interrupt => e rescue Interrupt => e
e.signo.should == Signal.list["INT"] e.signo.should == Signal.list["INT"]
e.signm.should == "" ["", "Interrupt"].should.include?(e.message)
end end
end end
end end
describe "Interrupt" do
# This spec is basically the same as above,
# but it does not rely on Signal.trap(:INT, :SIG_DFL) which can be tricky
it "is raised on the main Thread by the default SIGINT handler" do
out = ruby_exe(<<-'RUBY', args: "2>&1")
begin
Process.kill :INT, Process.pid
sleep
rescue Interrupt => e
puts "Interrupt: #{e.signo}"
end
RUBY
out.should == "Interrupt: #{Signal.list["INT"]}\n"
end
end

View File

@ -14,10 +14,8 @@ describe "StopIteration#result" do
it "returns the method-returned-object from an Enumerator" do it "returns the method-returned-object from an Enumerator" do
@enum.next @enum.next
@enum.next @enum.next
-> { @enum.next }.should( -> { @enum.next }.should raise_error(StopIteration) { |error|
raise_error(StopIteration) do |error|
error.result.should equal(:method_returned) error.result.should equal(:method_returned)
end }
)
end end
end end

View File

@ -1,16 +1,40 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
describe "GC.stat" do describe "GC.stat" do
it "supports access by key" do
keys = [:heap_free_slots, :total_allocated_objects, :count]
keys.each do |key|
GC.stat(key).should be_kind_of(Integer)
end
end
it "returns hash of values" do it "returns hash of values" do
stat = GC.stat stat = GC.stat
stat.should be_kind_of(Hash) stat.should be_kind_of(Hash)
stat.keys.should include(:count) stat.keys.should include(:count)
end end
it "can return a single value" do
GC.stat(:count).should be_kind_of(Integer)
end
it "increases count after GC is run" do
count = GC.stat(:count)
GC.start
GC.stat(:count).should > count
end
it "increases major_gc_count after GC is run" do
count = GC.stat(:major_gc_count)
GC.start
GC.stat(:major_gc_count).should > count
end
it "provides some number for count" do
GC.stat(:count).should be_kind_of(Integer)
GC.stat[:count].should be_kind_of(Integer)
end
it "provides some number for heap_free_slots" do
GC.stat(:heap_free_slots).should be_kind_of(Integer)
GC.stat[:heap_free_slots].should be_kind_of(Integer)
end
it "provides some number for total_allocated_objects" do
GC.stat(:total_allocated_objects).should be_kind_of(Integer)
GC.stat[:total_allocated_objects].should be_kind_of(Integer)
end
end end

View File

@ -27,6 +27,27 @@ describe "Kernel#raise" do
ScratchPad.recorded.should be_nil ScratchPad.recorded.should be_nil
end end
ruby_version_is "2.6" do
it "accepts a cause keyword argument that sets the cause" do
cause = StandardError.new
-> { raise("error", cause: cause) }.should raise_error(RuntimeError) { |e| e.cause.should == cause }
end
it "accepts a cause keyword argument that overrides the last exception" do
begin
raise "first raise"
rescue => ignored
cause = StandardError.new
-> { raise("error", cause: cause) }.should raise_error(RuntimeError) { |e| e.cause.should == cause }
end
end
it "raises an ArgumentError when only cause is given" do
cause = StandardError.new
-> { raise(cause: cause) }.should raise_error(ArgumentError)
end
end
end end
describe "Kernel#raise" do describe "Kernel#raise" do

View File

@ -22,6 +22,12 @@ describe "Kernel#sleep" do
sleep(Rational(1, 999)).should >= 0 sleep(Rational(1, 999)).should >= 0
end end
it "accepts any Object that reponds to divmod" do
o = Object.new
def o.divmod(*); [0, 0.001]; end
sleep(o).should >= 0
end
it "raises an ArgumentError when passed a negative duration" do it "raises an ArgumentError when passed a negative duration" do
-> { sleep(-0.1) }.should raise_error(ArgumentError) -> { sleep(-0.1) }.should raise_error(ArgumentError)
-> { sleep(-1) }.should raise_error(ArgumentError) -> { sleep(-1) }.should raise_error(ArgumentError)

View File

@ -1,12 +1,9 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
require_relative 'fixtures/classes'
describe "Kernel#trap" do describe "Kernel#trap" do
it "is a private method" do it "is a private method" do
Kernel.should have_private_instance_method(:trap) Kernel.should have_private_instance_method(:trap)
end end
end
describe "Kernel.trap" do # Behaviour is specified for Signal.trap
it "needs to be reviewed for spec completeness"
end end

View File

@ -1,11 +1,12 @@
require_relative '../../spec_helper' require_relative '../../spec_helper'
platform_is_not :windows do describe "Signal.trap" do
describe "Signal.trap" do platform_is_not :windows do
before :each do before :each do
ScratchPad.clear ScratchPad.clear
@proc = -> {} @proc = -> {}
@saved_trap = Signal.trap(:HUP, @proc) @saved_trap = Signal.trap(:HUP, @proc)
@hup_number = Signal.list["HUP"]
end end
after :each do after :each do
@ -16,10 +17,11 @@ platform_is_not :windows do
Signal.trap(:HUP, @saved_trap).should equal(@proc) Signal.trap(:HUP, @saved_trap).should equal(@proc)
end end
it "accepts a block in place of a proc/command argument" do it "accepts a block" do
done = false done = false
Signal.trap(:HUP) do Signal.trap(:HUP) do |signo|
signo.should == @hup_number
ScratchPad.record :block_trap ScratchPad.record :block_trap
done = true done = true
end end
@ -30,6 +32,94 @@ platform_is_not :windows do
ScratchPad.recorded.should == :block_trap ScratchPad.recorded.should == :block_trap
end end
it "accepts a proc" do
done = false
handler = -> signo {
signo.should == @hup_number
ScratchPad.record :proc_trap
done = true
}
Signal.trap(:HUP, handler)
Process.kill :HUP, Process.pid
Thread.pass until done
ScratchPad.recorded.should == :proc_trap
end
it "accepts a method" do
done = false
handler_class = Class.new
hup_number = @hup_number
handler_class.define_method :handler_method do |signo|
signo.should == hup_number
ScratchPad.record :method_trap
done = true
end
handler_method = handler_class.new.method(:handler_method)
Signal.trap(:HUP, handler_method)
Process.kill :HUP, Process.pid
Thread.pass until done
ScratchPad.recorded.should == :method_trap
end
it "accepts anything you can call" do
done = false
callable = Object.new
hup_number = @hup_number
callable.singleton_class.define_method :call do |signo|
signo.should == hup_number
ScratchPad.record :callable_trap
done = true
end
Signal.trap(:HUP, callable)
Process.kill :HUP, Process.pid
Thread.pass until done
ScratchPad.recorded.should == :callable_trap
end
it "raises an exception for a non-callable at the point of use" do
not_callable = Object.new
Signal.trap(:HUP, not_callable)
-> {
Process.kill :HUP, Process.pid
loop { Thread.pass }
}.should raise_error(NoMethodError)
end
it "accepts a non-callable that becomes callable when used" do
done = false
late_callable = Object.new
hup_number = @hup_number
Signal.trap(:HUP, late_callable)
late_callable.singleton_class.define_method :call do |signo|
signo.should == hup_number
ScratchPad.record :late_callable_trap
done = true
end
Process.kill :HUP, Process.pid
Thread.pass until done
ScratchPad.recorded.should == :late_callable_trap
end
it "is possible to create a new Thread when the handler runs" do it "is possible to create a new Thread when the handler runs" do
done = false done = false
@ -130,14 +220,12 @@ platform_is_not :windows do
Signal.trap :HUP, @proc Signal.trap :HUP, @proc
Signal.trap(:HUP, @saved_trap).should equal(@proc) Signal.trap(:HUP, @saved_trap).should equal(@proc)
end end
end
describe "Signal.trap" do
# See man 2 signal # See man 2 signal
%w[KILL STOP].each do |signal| %w[KILL STOP].each do |signal|
it "raises ArgumentError or Errno::EINVAL for SIG#{signal}" do it "raises ArgumentError or Errno::EINVAL for SIG#{signal}" do
-> { -> {
trap(signal, -> {}) Signal.trap(signal, -> {})
}.should raise_error(StandardError) { |e| }.should raise_error(StandardError) { |e|
[ArgumentError, Errno::EINVAL].should include(e.class) [ArgumentError, Errno::EINVAL].should include(e.class)
e.message.should =~ /Invalid argument|Signal already used by VM or OS/ e.message.should =~ /Invalid argument|Signal already used by VM or OS/
@ -152,7 +240,7 @@ platform_is_not :windows do
end end
it "returns 'DEFAULT' for the initial SIGINT handler" do it "returns 'DEFAULT' for the initial SIGINT handler" do
ruby_exe('print trap(:INT) { abort }').should == 'DEFAULT' ruby_exe("print Signal.trap(:INT) { abort }").should == 'DEFAULT'
end end
it "returns SYSTEM_DEFAULT if passed DEFAULT and no handler was ever set" do it "returns SYSTEM_DEFAULT if passed DEFAULT and no handler was ever set" do
@ -174,23 +262,22 @@ platform_is_not :windows do
Signal.signame(status.termsig).should == "PIPE" Signal.signame(status.termsig).should == "PIPE"
end end
end end
end
describe "Signal.trap" do
describe "the special EXIT signal code" do describe "the special EXIT signal code" do
it "accepts the EXIT code" do it "accepts the EXIT code" do
code = "trap(:EXIT, proc { print 1 })" code = "Signal.trap(:EXIT, proc { print 1 })"
ruby_exe(code).should == "1" ruby_exe(code).should == "1"
end end
it "runs the proc before at_exit handlers" do it "runs the proc before at_exit handlers" do
code = "at_exit {print 1}; trap(:EXIT, proc {print 2}); at_exit {print 3}" code = "at_exit {print 1}; Signal.trap(:EXIT, proc {print 2}); at_exit {print 3}"
ruby_exe(code).should == "231" ruby_exe(code).should == "231"
end end
it "can unset the handler" do it "can unset the handler" do
code = "trap(:EXIT, proc { print 1 }); trap(:EXIT, 'DEFAULT')" code = "Signal.trap(:EXIT, proc { print 1 }); Signal.trap(:EXIT, 'DEFAULT')"
ruby_exe(code).should == "" ruby_exe(code).should == ""
end end
end end
end end

View File

@ -38,6 +38,20 @@ describe "Time.at" do
Time.at(BigDecimal('1.1')).to_f.should == 1.1 Time.at(BigDecimal('1.1')).to_f.should == 1.1
end end
end end
describe "passed Rational" do
it "returns Time with correct microseconds" do
t = Time.at(Rational(1_486_570_508_539_759, 1_000_000))
t.usec.should == 539_759
t.nsec.should == 539_759_000
end
it "returns Time with correct nanoseconds" do
t = Time.at(Rational(1_486_570_508_539_759_123, 1_000_000_000))
t.usec.should == 539_759
t.nsec.should == 539_759_123
end
end
end end
describe "passed Time" do describe "passed Time" do

View File

@ -381,52 +381,6 @@ describe "Constant resolution within methods" do
ConstantSpecs::ClassA.constx.should == :CS_CONSTX ConstantSpecs::ClassA.constx.should == :CS_CONSTX
ConstantSpecs::ClassA.new.constx.should == :CS_CONSTX ConstantSpecs::ClassA.new.constx.should == :CS_CONSTX
end end
describe "with ||=" do
it "assigns a scoped constant if previously undefined" do
ConstantSpecs.should_not have_constant(:OpAssignUndefined)
module ConstantSpecs
OpAssignUndefined ||= 42
end
ConstantSpecs::OpAssignUndefined.should == 42
ConstantSpecs::OpAssignUndefinedOutside ||= 42
ConstantSpecs::OpAssignUndefinedOutside.should == 42
ConstantSpecs.send(:remove_const, :OpAssignUndefined)
ConstantSpecs.send(:remove_const, :OpAssignUndefinedOutside)
end
it "assigns a global constant if previously undefined" do
OpAssignGlobalUndefined ||= 42
::OpAssignGlobalUndefinedExplicitScope ||= 42
OpAssignGlobalUndefined.should == 42
::OpAssignGlobalUndefinedExplicitScope.should == 42
Object.send :remove_const, :OpAssignGlobalUndefined
Object.send :remove_const, :OpAssignGlobalUndefinedExplicitScope
end
end
describe "with &&=" do
it "re-assigns a scoped constant if already true" do
module ConstantSpecs
OpAssignTrue = true
end
suppress_warning do
ConstantSpecs::OpAssignTrue &&= 1
end
ConstantSpecs::OpAssignTrue.should == 1
ConstantSpecs.send :remove_const, :OpAssignTrue
end
it "leaves scoped constant if not true" do
module ConstantSpecs
OpAssignFalse = false
end
ConstantSpecs::OpAssignFalse &&= 1
ConstantSpecs::OpAssignFalse.should == false
ConstantSpecs.send :remove_const, :OpAssignFalse
end
end
end end
describe "Constant resolution within a singleton class (class << obj)" do describe "Constant resolution within a singleton class (class << obj)" do

View File

@ -74,9 +74,9 @@ describe "An ensure block inside a begin block" do
ensure ensure
raise "from ensure" raise "from ensure"
end end
}.should raise_error(RuntimeError, "from ensure") do |e| }.should raise_error(RuntimeError, "from ensure") { |e|
e.cause.message.should == "from block" e.cause.message.should == "from block"
end }
end end
end end

View File

@ -167,3 +167,42 @@ describe "Hash literal" do
usascii_hash.keys.first.encoding.should == Encoding::US_ASCII usascii_hash.keys.first.encoding.should == Encoding::US_ASCII
end end
end end
describe "The ** operator" do
it "makes a copy when calling a method taking a keyword rest argument" do
def m(**h)
h.delete(:one); h
end
h = { one: 1, two: 2 }
m(**h).should == { two: 2 }
m(**h).should_not.equal?(h)
h.should == { one: 1, two: 2 }
end
ruby_version_is ""..."3.0" do
it "makes a caller-side copy when calling a method taking a positional Hash" do
def m(h)
h.delete(:one); h
end
h = { one: 1, two: 2 }
m(**h).should == { two: 2 }
m(**h).should_not.equal?(h)
h.should == { one: 1, two: 2 }
end
end
ruby_version_is "3.0" do
it "does not copy when calling a method taking a positional Hash" do
def m(h)
h.delete(:one); h
end
h = { one: 1, two: 2 }
m(**h).should == { two: 2 }
m(**h).should.equal?(h)
h.should == { two: 2 }
end
end
end

View File

@ -1758,6 +1758,17 @@ describe "A method" do
end end
end end
end end
it "assigns the last Hash to the last optional argument if the Hash contains non-Symbol keys and is not passed as keywords" do
def m(a = nil, b = {}, v: false)
[a, b, v]
end
h = { "key" => "value" }
m(:a, h).should == [:a, h, false]
m(:a, h, v: true).should == [:a, h, true]
m(v: true).should == [nil, {}, true]
end
end end
describe "A method call with a space between method name and parentheses" do describe "A method call with a space between method name and parentheses" do

View File

@ -1,4 +1,5 @@
require_relative '../spec_helper' require_relative '../spec_helper'
require_relative '../fixtures/constants'
describe 'Optional variable assignments' do describe 'Optional variable assignments' do
describe 'using ||=' do describe 'using ||=' do
@ -351,3 +352,107 @@ describe 'Optional variable assignments' do
end end
end end
end end
describe 'Optional constant assignment' do
describe 'with ||=' do
it "assigns a scoped constant if previously undefined" do
ConstantSpecs.should_not have_constant(:OpAssignUndefined)
module ConstantSpecs
OpAssignUndefined ||= 42
end
ConstantSpecs::OpAssignUndefined.should == 42
ConstantSpecs::OpAssignUndefinedOutside ||= 42
ConstantSpecs::OpAssignUndefinedOutside.should == 42
ConstantSpecs.send(:remove_const, :OpAssignUndefined)
ConstantSpecs.send(:remove_const, :OpAssignUndefinedOutside)
end
it "assigns a global constant if previously undefined" do
OpAssignGlobalUndefined ||= 42
::OpAssignGlobalUndefinedExplicitScope ||= 42
OpAssignGlobalUndefined.should == 42
::OpAssignGlobalUndefinedExplicitScope.should == 42
Object.send :remove_const, :OpAssignGlobalUndefined
Object.send :remove_const, :OpAssignGlobalUndefinedExplicitScope
end
it 'correctly defines non-existing constants' do
ConstantSpecs::ClassA::OR_ASSIGNED_CONSTANT1 ||= :assigned
ConstantSpecs::ClassA::OR_ASSIGNED_CONSTANT1.should == :assigned
end
it 'correctly overwrites nil constants' do
suppress_warning do # already initialized constant
ConstantSpecs::ClassA::NIL_OR_ASSIGNED_CONSTANT1 = nil
ConstantSpecs::ClassA::NIL_OR_ASSIGNED_CONSTANT1 ||= :assigned
ConstantSpecs::ClassA::NIL_OR_ASSIGNED_CONSTANT1.should == :assigned
end
end
it 'causes side-effects of the module part to be applied only once (for undefined constant)' do
x = 0
(x += 1; ConstantSpecs::ClassA)::OR_ASSIGNED_CONSTANT2 ||= :assigned
x.should == 1
ConstantSpecs::ClassA::OR_ASSIGNED_CONSTANT2.should == :assigned
end
it 'causes side-effects of the module part to be applied (for nil constant)' do
suppress_warning do # already initialized constant
ConstantSpecs::ClassA::NIL_OR_ASSIGNED_CONSTANT2 = nil
x = 0
(x += 1; ConstantSpecs::ClassA)::NIL_OR_ASSIGNED_CONSTANT2 ||= :assigned
x.should == 1
ConstantSpecs::ClassA::NIL_OR_ASSIGNED_CONSTANT2.should == :assigned
end
end
it 'does not evaluate the right-hand side if the module part raises an exception (for undefined constant)' do
x = 0
y = 0
-> {
(x += 1; raise Exception; ConstantSpecs::ClassA)::OR_ASSIGNED_CONSTANT3 ||= (y += 1; :assigned)
}.should raise_error(Exception)
x.should == 1
y.should == 0
defined?(ConstantSpecs::ClassA::OR_ASSIGNED_CONSTANT3).should == nil
end
it 'does not evaluate the right-hand side if the module part raises an exception (for nil constant)' do
ConstantSpecs::ClassA::NIL_OR_ASSIGNED_CONSTANT3 = nil
x = 0
y = 0
-> {
(x += 1; raise Exception; ConstantSpecs::ClassA)::NIL_OR_ASSIGNED_CONSTANT3 ||= (y += 1; :assigned)
}.should raise_error(Exception)
x.should == 1
y.should == 0
ConstantSpecs::ClassA::NIL_OR_ASSIGNED_CONSTANT3.should == nil
end
end
describe "with &&=" do
it "re-assigns a scoped constant if already true" do
module ConstantSpecs
OpAssignTrue = true
end
suppress_warning do
ConstantSpecs::OpAssignTrue &&= 1
end
ConstantSpecs::OpAssignTrue.should == 1
ConstantSpecs.send :remove_const, :OpAssignTrue
end
it "leaves scoped constant if not true" do
module ConstantSpecs
OpAssignFalse = false
end
ConstantSpecs::OpAssignFalse &&= 1
ConstantSpecs::OpAssignFalse.should == false
ConstantSpecs.send :remove_const, :OpAssignFalse
end
end
end

View File

@ -25,4 +25,11 @@ describe "Regexps with grouping" do
-> { Regexp.new("(?<1a>a)") }.should raise_error(RegexpError) -> { Regexp.new("(?<1a>a)") }.should raise_error(RegexpError)
-> { Regexp.new("(?<-a>a)") }.should raise_error(RegexpError) -> { Regexp.new("(?<-a>a)") }.should raise_error(RegexpError)
end end
it "ignore capture groups in line comments" do
/^
(a) # there is a capture group on this line
b # there is no capture group on this line (not even here)
$/x.match("ab").to_a.should == [ "ab", "a" ]
end
end end

View File

@ -428,9 +428,9 @@ describe "The rescue keyword" do
raise "from block" raise "from block"
rescue (raise "from rescue expression") rescue (raise "from rescue expression")
end end
}.should raise_error(RuntimeError, "from rescue expression") do |e| }.should raise_error(RuntimeError, "from rescue expression") { |e|
e.cause.message.should == "from block" e.cause.message.should == "from block"
end }
end end
it "should splat the handling Error classes" do it "should splat the handling Error classes" do

View File

@ -308,4 +308,14 @@ describe "Ruby String interpolation" do
eval(code).should_not.frozen? eval(code).should_not.frozen?
end end
end end
ruby_version_is ""..."3.0" do
it "creates a frozen String when # frozen-string-literal: true is used" do
code = <<~'RUBY'
# frozen-string-literal: true
"a#{6*7}c"
RUBY
eval(code).should.frozen?
end
end
end end

View File

@ -0,0 +1,30 @@
require_relative '../../spec_helper'
require 'monitor'
describe "Monitor#synchronize" do
it "unlocks after return, even if it was interrupted by Thread#raise" do
exc_class = Class.new(RuntimeError)
monitor = Monitor.new
10.times do
locked = false
thread = Thread.new do
begin
monitor.synchronize do
locked = true
# Do not wait here, we are trying to interrupt the ensure part of #synchronize
end
sleep # wait for exception if it did not happen yet
rescue exc_class
monitor.should_not.mon_locked?
:ok
end
end
Thread.pass until locked
thread.raise exc_class, "interrupt"
thread.value.should == :ok
end
end
end

View File

@ -21,7 +21,7 @@ describe 'Socket.udp_server_loop' do
it 'yields the message and a Socket::UDPSource' do it 'yields the message and a Socket::UDPSource' do
msg, src = nil msg, src = nil
Thread.new do thread = Thread.new do
SocketSpecs::ServerLoopPortFinder.udp_server_loop('127.0.0.1', 0) do |message, source| SocketSpecs::ServerLoopPortFinder.udp_server_loop('127.0.0.1', 0) do |message, source|
msg = message msg = message
src = source src = source
@ -52,6 +52,8 @@ describe 'Socket.udp_server_loop' do
msg.should == 'hello' msg.should == 'hello'
src.should be_an_instance_of(Socket::UDPSource) src.should be_an_instance_of(Socket::UDPSource)
thread.join
end end
end end
end end

View File

@ -13,9 +13,11 @@ class WeakRefSpec
def self.make_dead_weakref def self.make_dead_weakref
weaks = [] weaks = []
weak = nil weak = nil
10_000.times do 1000.times do
weaks << make_weakref weaks << make_weakref
GC.start end
1000.times do
GC.start GC.start
break if weak = weaks.find { |w| !w.weakref_alive? } break if weak = weaks.find { |w| !w.weakref_alive? }
end end

View File

@ -191,6 +191,14 @@ static VALUE kernel_spec_rb_protect_yield(VALUE self, VALUE obj, VALUE ary) {
return res; return res;
} }
static VALUE kernel_spec_rb_protect_errinfo(VALUE self, VALUE obj, VALUE ary) {
int status = 0;
VALUE res = rb_protect(rb_yield, obj, &status);
rb_ary_store(ary, 0, INT2NUM(23));
rb_ary_store(ary, 1, res);
return rb_errinfo();
}
static VALUE kernel_spec_rb_protect_null_status(VALUE self, VALUE obj) { static VALUE kernel_spec_rb_protect_null_status(VALUE self, VALUE obj) {
return rb_protect(rb_yield, obj, NULL); return rb_protect(rb_yield, obj, NULL);
} }
@ -345,6 +353,7 @@ void Init_kernel_spec(void) {
rb_define_method(cls, "rb_rescue", kernel_spec_rb_rescue, 4); rb_define_method(cls, "rb_rescue", kernel_spec_rb_rescue, 4);
rb_define_method(cls, "rb_rescue2", kernel_spec_rb_rescue2, -1); rb_define_method(cls, "rb_rescue2", kernel_spec_rb_rescue2, -1);
rb_define_method(cls, "rb_protect_yield", kernel_spec_rb_protect_yield, 2); rb_define_method(cls, "rb_protect_yield", kernel_spec_rb_protect_yield, 2);
rb_define_method(cls, "rb_protect_errinfo", kernel_spec_rb_protect_errinfo, 2);
rb_define_method(cls, "rb_protect_null_status", kernel_spec_rb_protect_null_status, 1); rb_define_method(cls, "rb_protect_null_status", kernel_spec_rb_protect_null_status, 1);
rb_define_method(cls, "rb_eval_string_protect", kernel_spec_rb_eval_string_protect, 2); rb_define_method(cls, "rb_eval_string_protect", kernel_spec_rb_eval_string_protect, 2);
rb_define_method(cls, "rb_catch", kernel_spec_rb_catch, 2); rb_define_method(cls, "rb_catch", kernel_spec_rb_catch, 2);

View File

@ -9,6 +9,18 @@ static VALUE module_specs_test_method(VALUE self) {
return ID2SYM(rb_intern("test_method")); return ID2SYM(rb_intern("test_method"));
} }
static VALUE module_specs_test_method_2required(VALUE self, VALUE arg1, VALUE arg2) {
return ID2SYM(rb_intern("test_method_2required"));
}
static VALUE module_specs_test_method_c_array(int argc, VALUE *argv, VALUE self) {
return ID2SYM(rb_intern("test_method_c_array"));
}
static VALUE module_specs_test_method_ruby_array(VALUE self, VALUE args) {
return ID2SYM(rb_intern("test_method_ruby_array"));
}
static VALUE module_specs_const_defined(VALUE self, VALUE klass, VALUE id) { static VALUE module_specs_const_defined(VALUE self, VALUE klass, VALUE id) {
return rb_const_defined(klass, SYM2ID(id)) ? Qtrue : Qfalse; return rb_const_defined(klass, SYM2ID(id)) ? Qtrue : Qfalse;
} }
@ -76,6 +88,21 @@ static VALUE module_specs_rb_define_method(VALUE self, VALUE cls, VALUE str_name
return Qnil; return Qnil;
} }
static VALUE module_specs_rb_define_method_2required(VALUE self, VALUE cls, VALUE str_name) {
rb_define_method(cls, RSTRING_PTR(str_name), module_specs_test_method_2required, 2);
return Qnil;
}
static VALUE module_specs_rb_define_method_c_array(VALUE self, VALUE cls, VALUE str_name) {
rb_define_method(cls, RSTRING_PTR(str_name), module_specs_test_method_c_array, -1);
return Qnil;
}
static VALUE module_specs_rb_define_method_ruby_array(VALUE self, VALUE cls, VALUE str_name) {
rb_define_method(cls, RSTRING_PTR(str_name), module_specs_test_method_ruby_array, -2);
return Qnil;
}
static VALUE module_specs_rb_define_module_function(VALUE self, VALUE cls, VALUE str_name) { static VALUE module_specs_rb_define_module_function(VALUE self, VALUE cls, VALUE str_name) {
rb_define_module_function(cls, RSTRING_PTR(str_name), module_specs_test_method, 0); rb_define_module_function(cls, RSTRING_PTR(str_name), module_specs_test_method, 0);
return Qnil; return Qnil;
@ -132,6 +159,10 @@ void Init_module_spec(void) {
module_specs_rb_define_global_function, 1); module_specs_rb_define_global_function, 1);
rb_define_method(cls, "rb_define_method", module_specs_rb_define_method, 2); rb_define_method(cls, "rb_define_method", module_specs_rb_define_method, 2);
rb_define_method(cls, "rb_define_method_2required", module_specs_rb_define_method_2required, 2);
rb_define_method(cls, "rb_define_method_c_array", module_specs_rb_define_method_c_array, 2);
rb_define_method(cls, "rb_define_method_ruby_array", module_specs_rb_define_method_ruby_array, 2);
rb_define_method(cls, "rb_define_module_function", rb_define_method(cls, "rb_define_module_function",
module_specs_rb_define_module_function, 2); module_specs_rb_define_module_function, 2);

View File

@ -312,6 +312,14 @@ describe "C-API Kernel function" do
@s.rb_protect_null_status(42) { |x| x + 1 }.should == 43 @s.rb_protect_null_status(42) { |x| x + 1 }.should == 43
@s.rb_protect_null_status(42) { |x| raise }.should == nil @s.rb_protect_null_status(42) { |x| raise }.should == nil
end end
it "populates errinfo with the captured exception" do
proof = []
@s.rb_protect_errinfo(77, proof) { |x| raise NameError }.class.should == NameError
proof[0].should == 23
proof[1].should == nil
end
end end
describe "rb_eval_string_protect" do describe "rb_eval_string_protect" do

View File

@ -246,12 +246,30 @@ describe "CApiModule" do
cls.new.test_method.should == :test_method cls.new.test_method.should == :test_method
end end
it "returns the correct arity of the method in class" do it "returns the correct arity when argc of the method in class is 0" do
cls = Class.new cls = Class.new
@m.rb_define_method(cls, "test_method") @m.rb_define_method(cls, "test_method")
cls.new.method(:test_method).arity.should == 0 cls.new.method(:test_method).arity.should == 0
end end
it "returns the correct arity when argc of the method in class is -1" do
cls = Class.new
@m.rb_define_method_c_array(cls, "test_method_c_array")
cls.new.method(:test_method_c_array).arity.should == -1
end
it "returns the correct arity when argc of the method in class is -2" do
cls = Class.new
@m.rb_define_method_ruby_array(cls, "test_method_ruby_array")
cls.new.method(:test_method_ruby_array).arity.should == -1
end
it "returns the correct arity when argc of the method in class is 2" do
cls = Class.new
@m.rb_define_method_2required(cls, "test_method_2required")
cls.new.method(:test_method_2required).arity.should == 2
end
it "defines a method on a module" do it "defines a method on a module" do
mod = Module.new mod = Module.new
@m.rb_define_method(mod, "test_method") @m.rb_define_method(mod, "test_method")

View File

@ -12,6 +12,28 @@ describe :kernel_raise, shared: true do
ScratchPad.recorded.should be_nil ScratchPad.recorded.should be_nil
end end
it "accepts an exception that implements to_hash" do
custom_error = Class.new(StandardError) do
def to_hash
{}
end
end
error = custom_error.new
-> { @object.raise(error) }.should raise_error(custom_error)
end
it "allows the message parameter to be a hash" do
data_error = Class.new(StandardError) do
attr_reader :data
def initialize(data)
@data = data
end
end
-> { @object.raise(data_error, {:data => 42}) }.should raise_error(data_error) do |ex|
ex.data.should == {:data => 42}
end
end
it "raises RuntimeError if no exception class is given" do it "raises RuntimeError if no exception class is given" do
-> { @object.raise }.should raise_error(RuntimeError, "") -> { @object.raise }.should raise_error(RuntimeError, "")
end end