diff options
Diffstat (limited to 'spec/ruby/optional/capi/kernel_spec.rb')
| -rw-r--r-- | spec/ruby/optional/capi/kernel_spec.rb | 395 |
1 files changed, 311 insertions, 84 deletions
diff --git a/spec/ruby/optional/capi/kernel_spec.rb b/spec/ruby/optional/capi/kernel_spec.rb index 54d8d8a8d3..6633ee50c1 100644 --- a/spec/ruby/optional/capi/kernel_spec.rb +++ b/spec/ruby/optional/capi/kernel_spec.rb @@ -1,12 +1,21 @@ require_relative 'spec_helper' +require_relative 'fixtures/kernel' kernel_path = load_extension("kernel") +class CApiKernelSpecs::Exc < StandardError +end +exception_class = CApiKernelSpecs::Exc + describe "C-API Kernel function" do before :each do @s = CApiKernelSpecs.new end + after :each do + @s.rb_errinfo.should == nil + end + describe "rb_block_given_p" do it "returns false if no block is passed" do @s.should_not.rb_block_given_p @@ -77,6 +86,22 @@ describe "C-API Kernel function" do -> { @s.rb_raise(h) }.should raise_error(TypeError) h[:stage].should == :before end + + it "re-raises a rescued exception" do + -> do + begin + raise StandardError, "aaa" + rescue Exception + begin + @s.rb_raise({}) + rescue TypeError + end + + # should raise StandardError "aaa" + raise + end + end.should raise_error(StandardError, "aaa") + end end describe "rb_throw" do @@ -131,26 +156,16 @@ describe "C-API Kernel function" do end describe "rb_warn" do - before :each do - @stderr, $stderr = $stderr, IOStub.new - @verbose = $VERBOSE - end - - after :each do - $stderr = @stderr - $VERBOSE = @verbose - end - it "prints a message to $stderr if $VERBOSE evaluates to true" do - $VERBOSE = true - @s.rb_warn("This is a warning") - $stderr.should =~ /This is a warning/ + -> { + @s.rb_warn("This is a warning") + }.should complain(/warning: This is a warning/, verbose: true) end it "prints a message to $stderr if $VERBOSE evaluates to false" do - $VERBOSE = false - @s.rb_warn("This is a warning") - $stderr.should =~ /This is a warning/ + -> { + @s.rb_warn("This is a warning") + }.should complain(/warning: This is a warning/, verbose: false) end end @@ -172,13 +187,35 @@ describe "C-API Kernel function" do it "raises an exception from the given error" do -> do @s.rb_syserr_fail(Errno::EINVAL::Errno, "additional info") - end.should raise_error(Errno::EINVAL, /additional info/) + end.should raise_error(Errno::EINVAL, "Invalid argument - additional info") end it "can take a NULL message" do -> do @s.rb_syserr_fail(Errno::EINVAL::Errno, nil) - end.should raise_error(Errno::EINVAL) + end.should raise_error(Errno::EINVAL, "Invalid argument") + end + + it "uses some kind of string as message when errno is unknown" do + -> { @s.rb_syserr_fail(-10, nil) }.should raise_error(SystemCallError, /[[:graph:]]+/) + end + end + + describe "rb_syserr_fail_str" do + it "raises an exception from the given error" do + -> do + @s.rb_syserr_fail_str(Errno::EINVAL::Errno, "additional info") + end.should raise_error(Errno::EINVAL, "Invalid argument - additional info") + end + + it "can take nil as a message" do + -> do + @s.rb_syserr_fail_str(Errno::EINVAL::Errno, nil) + end.should raise_error(Errno::EINVAL, "Invalid argument") + end + + it "uses some kind of string as message when errno is unknown" do + -> { @s.rb_syserr_fail_str(-10, nil) }.should raise_error(SystemCallError, /[[:graph:]]+/) end end @@ -205,10 +242,8 @@ describe "C-API Kernel function" do @s.rb_yield(1) { break 73 }.should == 73 end - platform_is_not :"solaris2.10" do # NOTE: i386-pc-solaris2.10 - it "rb_yield through a callback to a block that breaks with a value returns the value" do - @s.rb_yield_indirected(1) { break 73 }.should == 73 - end + it "rb_yield through a callback to a block that breaks with a value returns the value" do + @s.rb_yield_indirected(1) { break 73 }.should == 73 end it "rb_yield to block passed to enumerator" do @@ -294,7 +329,7 @@ describe "C-API Kernel function" do it "will allow cleanup code to run after a raise" do proof = [] # Hold proof of work performed after the yield. -> do - @s.rb_protect_yield(77, proof) { |x| raise NameError} + @s.rb_protect_yield(77, proof) { |x| raise NameError } end.should raise_error(NameError) proof[0].should == 23 end @@ -302,7 +337,7 @@ describe "C-API Kernel function" do it "will return nil if an error was raised" do proof = [] # Hold proof of work performed after the yield. -> do - @s.rb_protect_yield(77, proof) { |x| raise NameError} + @s.rb_protect_yield(77, proof) { |x| raise NameError } end.should raise_error(NameError) proof[0].should == 23 proof[1].should == nil @@ -310,14 +345,21 @@ describe "C-API Kernel function" do it "accepts NULL as status and returns nil if it failed" do @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 NameError }.should == nil + @s.rb_errinfo().should.is_a? NameError + ensure + @s.rb_set_errinfo(nil) end - it "populates errinfo with the captured exception" do + it "populates rb_errinfo() with the captured exception" do proof = [] - @s.rb_protect_errinfo(77, proof) { |x| raise NameError }.class.should == NameError + @s.rb_protect_ignore_status(77, proof) { |x| raise NameError } + @s.rb_errinfo().should.is_a? NameError + # Note: on CRuby $! is the NameError here, but not clear if that is desirable or bug proof[0].should == 23 proof[1].should == nil + ensure + @s.rb_set_errinfo(nil) end end @@ -381,9 +423,21 @@ describe "C-API Kernel function" do -> { @s.rb_rescue(@std_error_proc, nil, @std_error_proc, nil) }.should raise_error(StandardError) end - it "makes $! available only during the 'rescue function' execution" do - @s.rb_rescue(@std_error_proc, nil, -> *_ { $! }, nil).class.should == StandardError + it "sets $! and rb_errinfo() during the 'rescue function' execution" do + @s.rb_rescue(-> *_ { raise exception_class, '' }, nil, -> _, exc { + exc.should.is_a?(exception_class) + $!.should.equal?(exc) + @s.rb_errinfo.should.equal?(exc) + }, nil) + + @s.rb_rescue(-> _ { @s.rb_raise({}) }, nil, -> _, exc { + exc.should.is_a?(TypeError) + $!.should.equal?(exc) + @s.rb_errinfo.should.equal?(exc) + }, nil) + $!.should == nil + @s.rb_errinfo.should == nil end it "returns the break value if the passed function yields to a block with a break" do @@ -401,7 +455,7 @@ describe "C-API Kernel function" do describe "rb_rescue2" do it "only rescues if one of the passed exceptions is raised" do - proc = -> x { x } + proc = -> x, _exc { x } arg_error_proc = -> *_ { raise ArgumentError, '' } run_error_proc = -> *_ { raise RuntimeError, '' } type_error_proc = -> *_ { raise Exception, 'custom error' } @@ -417,6 +471,23 @@ describe "C-API Kernel function" do @s.rb_rescue2(-> *_ { raise RuntimeError, "foo" }, :no_exc, -> x { x }, :exc, Object.new, 42) }.should raise_error(TypeError, /class or module required/) end + + it "sets $! and rb_errinfo() during the 'rescue function' execution" do + @s.rb_rescue2(-> *_ { raise exception_class, '' }, :no_exc, -> _, exc { + exc.should.is_a?(exception_class) + $!.should.equal?(exc) + @s.rb_errinfo.should.equal?(exc) + }, :exc, exception_class, ScriptError) + + @s.rb_rescue2(-> *_ { @s.rb_raise({}) }, :no_exc, -> _, exc { + exc.should.is_a?(TypeError) + $!.should.equal?(exc) + @s.rb_errinfo.should.equal?(exc) + }, :exc, TypeError, ArgumentError) + + $!.should == nil + @s.rb_errinfo.should == nil + end end describe "rb_catch" do @@ -469,6 +540,40 @@ describe "C-API Kernel function" do end end + describe "rb_category_warn" do + it "emits a warning into stderr" do + Warning[:deprecated] = true + + -> { + @s.rb_category_warn_deprecated + }.should complain(/warning: foo/, verbose: true) + end + + it "supports printf format modifiers" do + Warning[:deprecated] = true + + -> { + @s.rb_category_warn_deprecated_with_integer_extra_value(42) + }.should complain(/warning: foo 42/, verbose: true) + end + + it "does not emits a warning when a category is disabled" do + Warning[:deprecated] = false + + -> { + @s.rb_category_warn_deprecated + }.should_not complain(verbose: true) + end + + it "does not emits a warning when $VERBOSE is nil" do + Warning[:deprecated] = true + + -> { + @s.rb_category_warn_deprecated + }.should_not complain(verbose: nil) + end + end + describe "rb_ensure" do it "executes passed function and returns its value" do proc = -> x { x } @@ -485,12 +590,33 @@ describe "C-API Kernel function" do it "executes passed 'ensure function' when an exception is raised" do foo = nil - raise_proc = -> { raise '' } + raise_proc = -> _ { raise exception_class } ensure_proc = -> x { foo = x } - @s.rb_ensure(raise_proc, nil, ensure_proc, :foo) rescue nil + -> { + @s.rb_ensure(raise_proc, nil, ensure_proc, :foo) + }.should raise_error(exception_class) foo.should == :foo end + it "sets $! and rb_errinfo() during the 'ensure function' execution" do + -> { + @s.rb_ensure(-> _ { raise exception_class }, nil, -> _ { + $!.should.is_a?(exception_class) + @s.rb_errinfo.should.is_a?(exception_class) + }, nil) + }.should raise_error(exception_class) + + -> { + @s.rb_ensure(-> _ { @s.rb_raise({}) }, nil, -> _ { + $!.should.is_a?(TypeError) + @s.rb_errinfo.should.is_a?(TypeError) + }, nil) + }.should raise_error(TypeError) + + $!.should == nil + @s.rb_errinfo.should == nil + end + it "raises the same exception raised inside passed function" do raise_proc = -> *_ { raise RuntimeError, 'foo' } proc = -> *_ { } @@ -502,24 +628,31 @@ describe "C-API Kernel function" do it "evaluates a string of ruby code" do @s.rb_eval_string("1+1").should == 2 end - end - describe "rb_eval_cmd_kw" do - it "evaluates a string of ruby code" do - @s.rb_eval_cmd_kw("1+1", [], 0).should == 2 + it "captures local variables when called within a method" do + a = 2 + @s.rb_eval_string("a+1").should == 3 end + end - it "calls a proc with the supplied arguments" do - @s.rb_eval_cmd_kw(-> *x { x.map { |i| i + 1 } }, [1, 3, 7], 0).should == [2, 4, 8] - end + ruby_version_is ""..."4.0" do + describe "rb_eval_cmd_kw" do + it "evaluates a string of ruby code" do + @s.rb_eval_cmd_kw("1+1", [], 0).should == 2 + end + + it "calls a proc with the supplied arguments" do + @s.rb_eval_cmd_kw(-> *x { x.map { |i| i + 1 } }, [1, 3, 7], 0).should == [2, 4, 8] + end - it "calls a proc with keyword arguments if kw_splat is non zero" do - a_proc = -> *x, **y { - res = x.map { |i| i + 1 } - y.each { |k, v| res << k; res << v } - res - } - @s.rb_eval_cmd_kw(a_proc, [1, 3, 7, {a: 1, b: 2, c: 3}], 1).should == [2, 4, 8, :a, 1, :b, 2, :c, 3] + it "calls a proc with keyword arguments if kw_splat is non zero" do + a_proc = -> *x, **y { + res = x.map { |i| i + 1 } + y.each { |k, v| res << k; res << v } + res + } + @s.rb_eval_cmd_kw(a_proc, [1, 3, 7, {a: 1, b: 2, c: 3}], 1).should == [2, 4, 8, :a, 1, :b, 2, :c, 3] + end end end @@ -597,47 +730,65 @@ describe "C-API Kernel function" do @s.rb_funcallv(self, :empty, []).should == 42 @s.rb_funcallv(self, :sum, [1, 2]).should == 3 end - end - ruby_version_is "3.0" do - describe "rb_funcallv_kw" do - it "passes keyword arguments to the callee" do - def m(*args, **kwargs) - [args, kwargs] - end + it "calls a private method" do + object = CApiKernelSpecs::ClassWithPrivateMethod.new + @s.rb_funcallv(object, :private_method, []).should == :private + end + + it "calls a protected method" do + object = CApiKernelSpecs::ClassWithProtectedMethod.new + @s.rb_funcallv(object, :protected_method, []).should == :protected + end + end - @s.rb_funcallv_kw(self, :m, [{}]).should == [[], {}] - @s.rb_funcallv_kw(self, :m, [{a: 1}]).should == [[], {a: 1}] - @s.rb_funcallv_kw(self, :m, [{b: 2}, {a: 1}]).should == [[{b: 2}], {a: 1}] - @s.rb_funcallv_kw(self, :m, [{b: 2}, {}]).should == [[{b: 2}], {}] + describe "rb_funcallv_kw" do + it "passes keyword arguments to the callee" do + def m(*args, **kwargs) + [args, kwargs] end - it "raises TypeError if the last argument is not a Hash" do - def m(*args, **kwargs) - [args, kwargs] - end + @s.rb_funcallv_kw(self, :m, [{}]).should == [[], {}] + @s.rb_funcallv_kw(self, :m, [{a: 1}]).should == [[], {a: 1}] + @s.rb_funcallv_kw(self, :m, [{b: 2}, {a: 1}]).should == [[{b: 2}], {a: 1}] + @s.rb_funcallv_kw(self, :m, [{b: 2}, {}]).should == [[{b: 2}], {}] + end + + it "calls a private method" do + object = CApiKernelSpecs::ClassWithPrivateMethod.new + @s.rb_funcallv_kw(object, :private_method, [{}]).should == :private + end + + it "calls a protected method" do + object = CApiKernelSpecs::ClassWithProtectedMethod.new + @s.rb_funcallv_kw(object, :protected_method, [{}]).should == :protected + end - -> { - @s.rb_funcallv_kw(self, :m, [42]) - }.should raise_error(TypeError, 'no implicit conversion of Integer into Hash') + it "raises TypeError if the last argument is not a Hash" do + def m(*args, **kwargs) + [args, kwargs] end + + -> { + @s.rb_funcallv_kw(self, :m, [42]) + }.should raise_error(TypeError, 'no implicit conversion of Integer into Hash') end + end - describe "rb_keyword_given_p" do - it "returns whether keywords were given to the C extension method" do - h = {a: 1} - empty = {} - @s.rb_keyword_given_p(a: 1).should == true - @s.rb_keyword_given_p("foo" => "bar").should == true - @s.rb_keyword_given_p(**h).should == true + describe "rb_keyword_given_p" do + it "returns whether keywords were given to the C extension method" do + h = {a: 1} + empty = {} + @s.rb_keyword_given_p(a: 1).should == true + @s.rb_keyword_given_p("foo" => "bar").should == true + @s.rb_keyword_given_p(**h).should == true - @s.rb_keyword_given_p(h).should == false - @s.rb_keyword_given_p().should == false - @s.rb_keyword_given_p(**empty).should == false + @s.rb_keyword_given_p(h).should == false + @s.rb_keyword_given_p().should == false + @s.rb_keyword_given_p(**empty).should == false - @s.rb_funcallv_kw(@s, :rb_keyword_given_p, [{a: 1}]).should == true - @s.rb_funcallv_kw(@s, :rb_keyword_given_p, [{}]).should == false - end + @s.rb_funcallv_kw(@s, :rb_keyword_given_p, [{a: 1}]).should == true + @s.rb_funcallv_kw(@s, :rb_keyword_given_p, [{}]).should == false end end @@ -676,21 +827,97 @@ describe "C-API Kernel function" do end describe 'rb_funcall_with_block' do - before :each do + it "calls a method with block" do @obj = Object.new class << @obj - def method_public; yield end - def method_private; yield end - private :method_private + def method_public(*args); [args, yield] end end + + @s.rb_funcall_with_block(@obj, :method_public, [1, 2], proc { :result }).should == [[1, 2], :result] end - it "calls a method with block" do - @s.rb_funcall_with_block(@obj, :method_public, proc { :result }).should == :result + it "does not call a private method" do + object = CApiKernelSpecs::ClassWithPrivateMethod.new + + -> { + @s.rb_funcall_with_block(object, :private_method, [], proc { }) + }.should raise_error(NoMethodError, /private/) + end + + it "does not call a protected method" do + object = CApiKernelSpecs::ClassWithProtectedMethod.new + + -> { + @s.rb_funcall_with_block(object, :protected_method, [], proc { }) + }.should raise_error(NoMethodError, /protected/) + end + end + + describe 'rb_funcall_with_block_kw' do + it "calls a method with keyword arguments and a block" do + @obj = Object.new + class << @obj + def method_public(*args, **kw, &block); [args, kw, block.call] end + end + + @s.rb_funcall_with_block_kw(@obj, :method_public, [1, 2, {a: 2}], proc { :result }).should == [[1, 2], {a: 2}, :result] end it "does not call a private method" do - -> { @s.rb_funcall_with_block(@obj, :method_private, proc { :result }) }.should raise_error(NoMethodError, /private/) + object = CApiKernelSpecs::ClassWithPrivateMethod.new + + -> { + @s.rb_funcall_with_block_kw(object, :private_method, [{}], proc { }) + }.should raise_error(NoMethodError, /private/) + end + + it "does not call a protected method" do + object = CApiKernelSpecs::ClassWithProtectedMethod.new + + -> { + @s.rb_funcall_with_block_kw(object, :protected_method, [{}], proc { }) + }.should raise_error(NoMethodError, /protected/) + end + end + + describe "rb_check_funcall" do + it "calls a method" do + @s.rb_check_funcall(1, :+, [2]).should == 3 + end + + it "returns Qundef if the method is not defined" do + obj = Object.new + @s.rb_check_funcall(obj, :foo, []).should == :Qundef + end + + it "uses #respond_to? to check if the method is defined" do + ScratchPad.record [] + obj = Object.new + def obj.respond_to?(name, priv) + ScratchPad << name + name == :foo || super + end + def obj.method_missing(name, *args) + name == :foo ? [name, 42] : super + end + @s.rb_check_funcall(obj, :foo, []).should == [:foo, 42] + ScratchPad.recorded.should == [:foo] + end + + it "calls a private method" do + object = CApiKernelSpecs::ClassWithPrivateMethod.new + @s.rb_check_funcall(object, :private_method, []).should == :private + end + + it "calls a protected method" do + object = CApiKernelSpecs::ClassWithProtectedMethod.new + @s.rb_check_funcall(object, :protected_method, []).should == :protected + end + end + + describe "rb_str_format" do + it "returns a string according to format and arguments" do + @s.rb_str_format(3, [10, 2.5, "test"], "%d %f %s").should == "10 2.500000 test" end end end |
